public inbox for [email protected]  
help / color / mirror / Atom feed
oauth integer overflow
10+ messages / 3 participants
[nested] [flat]

* oauth integer overflow
@ 2026-04-23 16:12  Andres Freund <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Andres Freund @ 2026-04-23 16:12 UTC (permalink / raw)
  To: pgsql-hackers; Jacob Champion <[email protected]>; Daniel Gustafsson <[email protected]>

Hi,

I was once more looking at what it'd take to work with -ftrapv, when cassert
is enabled.  Partially motivated with the worry that we support compilers that
don't understand -fwrapv so code relying on signed overflow isn't actually
safe. And because it sometimes leads to unexpected results that can cause
trouble and -ftrapv helps find those.

One thing that quickly triggers when doing so is:

#0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
#1  0x00007fe1a05a9dbf in __pthread_kill_internal (threadid=<optimized out>, signo=6) at ./nptl/pthread_kill.c:89
#2  0x00007fe1a0552d02 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#3  0x00007fe1a053a4b2 in __GI_abort () at ./stdlib/abort.c:77
#4  0x00007fe19f218ac1 in __addvsi3 ()
   from /srv/dev/build/postgres/m-dev-assert/tmp_install//srv/dev/install/postgres/m-dev-assert/lib/x86_64-linux-gnu/libpq-oauth.so
#5  0x00007fe19f20da05 in handle_token_response (actx=0x561825a06350, token=0x7ffc2f677e48)
    at ../../../../../home/andres/src/postgresql/src/interfaces/libpq-oauth/oauth-curl.c:2625
#6  0x00007fe19f20dec5 in pg_fe_run_oauth_flow_impl (conn=0x561825998490, request=0x561825a06e30, altsock=0x561825998860)
    at ../../../../../home/andres/src/postgresql/src/interfaces/libpq-oauth/oauth-curl.c:2924
#7  0x00007fe19f20e0a8 in pg_fe_run_oauth_flow (conn=0x561825998490, request=0x561825a06e30, altsock=0x561825998860)
    at ../../../../../home/andres/src/postgresql/src/interfaces/libpq-oauth/oauth-curl.c:3027
#8  0x00007fe1a0a35627 in do_async (state=0x5618259a1880, request=0x561825a06e30)
    at ../../../../../home/andres/src/postgresql/src/interfaces/libpq/fe-auth-oauth.c:1528
#9  0x00007fe1a0a346e3 in run_oauth_flow (conn=0x561825998490) at ../../../../../home/andres/src/postgresql/src/interfaces/libpq/fe-auth-oauth.c:751
#10 0x00007fe1a0a3ffc1 in PQconnectPoll (conn=0x561825998490) at ../../../../../home/andres/src/postgresql/src/interfaces/libpq/fe-connect.c:4302
#11 0x00007fe1a0a3db6c in pqConnectDBComplete (conn=0x561825998490) at ../../../../../home/andres/src/postgresql/src/interfaces/libpq/fe-connect.c:2893
#12 0x00007fe1a0a3a1f7 in PQconnectdbParams (keywords=0x5618259983f0, values=0x561825998440, expand_dbname=1)

	/*
	 * A slow_down error requires us to permanently increase our retry
	 * interval by five seconds.
	 */
	if (strcmp(err->error, "slow_down") == 0)
	{
		int			prev_interval = actx->authz.interval;

		actx->authz.interval += 5;
		if (actx->authz.interval < prev_interval)
		{
			actx_error(actx, "slow_down interval overflow");
			goto token_cleanup;
		}
	}

I don't think it's safe to rely on that type of check working without -fwrapv
support, which in turn we can't rely on having.

I think this should use pg_add_s32_overflow().

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: oauth integer overflow
@ 2026-04-23 17:28  Daniel Gustafsson <[email protected]>
  parent: Andres Freund <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Daniel Gustafsson @ 2026-04-23 17:28 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: pgsql-hackers; Jacob Champion <[email protected]>

> On 23 Apr 2026, at 18:12, Andres Freund <[email protected]> wrote:

> I don't think it's safe to rely on that type of check working without -fwrapv
> support, which in turn we can't rely on having.
> 
> I think this should use pg_add_s32_overflow().

Agreed, I'll write up a patch to fix it.

--
Daniel Gustafsson






^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: oauth integer overflow
@ 2026-04-23 17:49  Jacob Champion <[email protected]>
  parent: Daniel Gustafsson <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Jacob Champion @ 2026-04-23 17:49 UTC (permalink / raw)
  To: Daniel Gustafsson <[email protected]>; +Cc: Andres Freund <[email protected]>; pgsql-hackers; Jacob Champion <[email protected]>

On Thu, Apr 23, 2026 at 10:29 AM Daniel Gustafsson <[email protected]> wrote:
> > On 23 Apr 2026, at 18:12, Andres Freund <[email protected]> wrote:
> > I think this should use pg_add_s32_overflow().

Agreed, thanks for the report!

> Agreed, I'll write up a patch to fix it.

Cool. I have one written up and can share it for comparison, if you'd
like, but it's fairly verbose and I wonder if there's a better way to
do it.

--Jacob





^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: oauth integer overflow
@ 2026-04-23 18:17  Daniel Gustafsson <[email protected]>
  parent: Jacob Champion <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Daniel Gustafsson @ 2026-04-23 18:17 UTC (permalink / raw)
  To: Jacob Champion <[email protected]>; +Cc: Andres Freund <[email protected]>; pgsql-hackers; Jacob Champion <[email protected]>

> On 23 Apr 2026, at 19:49, Jacob Champion <[email protected]> wrote:

> Cool. I have one written up and can share it for comparison, if you'd
> like, but it's fairly verbose and I wonder if there's a better way to
> do it.

Well, if you're already done then please do share it, and we'll use that as a
starting point.

--
Daniel Gustafsson






^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: oauth integer overflow
@ 2026-04-23 18:31  Jacob Champion <[email protected]>
  parent: Daniel Gustafsson <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Jacob Champion @ 2026-04-23 18:31 UTC (permalink / raw)
  To: Daniel Gustafsson <[email protected]>; +Cc: Andres Freund <[email protected]>; pgsql-hackers

On Thu, Apr 23, 2026 at 11:17 AM Daniel Gustafsson <[email protected]> wrote:
> > Cool. I have one written up and can share it for comparison, if you'd
> > like, but it's fairly verbose and I wonder if there's a better way to
> > do it.
>
> Well, if you're already done then please do share it, and we'll use that as a
> starting point.

Attached. The static_assert for the millisecond calculation is the
only part I don't really like, but doing an overflow check on a
calculation that can't overflow int64 is even more verbose/wasteful.

--Jacob


Attachments:

  [application/octet-stream] 0001-libpq-oauth-Avoid-overflow-for-very-large-intervals.patch (2.9K, 2-0001-libpq-oauth-Avoid-overflow-for-very-large-intervals.patch)
  download | inline diff:
From 27de39f667ab12b78778142fe60e6376f4d49ec3 Mon Sep 17 00:00:00 2001
From: Jacob Champion <[email protected]>
Date: Thu, 23 Apr 2026 09:46:42 -0700
Subject: [PATCH] libpq-oauth: Avoid overflow for very large intervals

---
 src/interfaces/libpq-oauth/oauth-curl.c | 38 ++++++++++++++++++-------
 1 file changed, 28 insertions(+), 10 deletions(-)

diff --git a/src/interfaces/libpq-oauth/oauth-curl.c b/src/interfaces/libpq-oauth/oauth-curl.c
index abbef93f95f..70cc4710b59 100644
--- a/src/interfaces/libpq-oauth/oauth-curl.c
+++ b/src/interfaces/libpq-oauth/oauth-curl.c
@@ -28,6 +28,7 @@
 #error libpq-oauth is not supported on this platform
 #endif
 
+#include "common/int.h"
 #include "common/jsonapi.h"
 #include "mb/pg_wchar.h"
 #include "oauth-curl.h"
@@ -136,7 +137,7 @@ struct device_authz
 
 	/* Fields below are parsed from the corresponding string above. */
 	int			expires_in;
-	int			interval;
+	int32		interval;
 };
 
 static void
@@ -1020,7 +1021,7 @@ parse_json_number(const char *s)
  * expensive network polling loop.) Tests may remove the lower bound with
  * PGOAUTHDEBUG, for improved performance.
  */
-static int
+static int32
 parse_interval(struct async_ctx *actx, const char *interval_str)
 {
 	double		parsed;
@@ -1031,8 +1032,8 @@ parse_interval(struct async_ctx *actx, const char *interval_str)
 	if (parsed < 1)
 		return (actx->debug_flags & OAUTHDEBUG_UNSAFE_DOS_ENDPOINT) ? 0 : 1;
 
-	else if (parsed >= INT_MAX)
-		return INT_MAX;
+	else if (parsed >= INT32_MAX)
+		return INT32_MAX;
 
 	return parsed;
 }
@@ -2620,10 +2621,7 @@ handle_token_response(struct async_ctx *actx, char **token)
 	 */
 	if (strcmp(err->error, "slow_down") == 0)
 	{
-		int			prev_interval = actx->authz.interval;
-
-		actx->authz.interval += 5;
-		if (actx->authz.interval < prev_interval)
+		if (pg_add_s32_overflow(actx->authz.interval, 5, &actx->authz.interval))
 		{
 			actx_error(actx, "slow_down interval overflow");
 			goto token_cleanup;
@@ -2949,8 +2947,28 @@ pg_fe_run_oauth_flow_impl(PGconn *conn, PGoauthBearerRequestV2 *request,
 				 * Wait for the required interval before issuing the next
 				 * request.
 				 */
-				if (!set_timer(actx, actx->authz.interval * 1000))
-					goto error_return;
+				{
+					/*
+					 * LONG_MAX milliseconds is 24 days on 32-bit platforms,
+					 * which for most people is going to be equivalent to a
+					 * disabled timer... but avoid overflow in case the
+					 * compiler does something unintuitive.
+					 */
+					int64		interval_ms;
+
+					/*
+					 * If you trip over this, the multiplication here needs to
+					 * be adjusted for overflow too.
+					 */
+					static_assert(sizeof(actx->authz.interval) <= 4);
+
+					interval_ms = actx->authz.interval * ((int64) 1000);
+					if (interval_ms > LONG_MAX)
+						interval_ms = LONG_MAX;
+
+					if (!set_timer(actx, (long) interval_ms))
+						goto error_return;
+				}
 
 				/*
 				 * No Curl requests are running, so we can simplify by having
-- 
2.34.1



^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: oauth integer overflow
@ 2026-04-23 18:37  Andres Freund <[email protected]>
  parent: Jacob Champion <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Andres Freund @ 2026-04-23 18:37 UTC (permalink / raw)
  To: Jacob Champion <[email protected]>; +Cc: Daniel Gustafsson <[email protected]>; pgsql-hackers

Hi,

On 2026-04-23 11:31:34 -0700, Jacob Champion wrote:
> On Thu, Apr 23, 2026 at 11:17 AM Daniel Gustafsson <[email protected]> wrote:
> > > Cool. I have one written up and can share it for comparison, if you'd
> > > like, but it's fairly verbose and I wonder if there's a better way to
> > > do it.
> >
> > Well, if you're already done then please do share it, and we'll use that as a
> > starting point.
> 
> Attached. The static_assert for the millisecond calculation is the
> only part I don't really like, but doing an overflow check on a
> calculation that can't overflow int64 is even more verbose/wasteful.

How about instead making sure that actx->authz.interval never gets big enough
to have any chance of overflowing during either the += 5 or the * 1000?  It's
clearly ok to error out well before that...

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: oauth integer overflow
@ 2026-04-23 19:05  Jacob Champion <[email protected]>
  parent: Andres Freund <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Jacob Champion @ 2026-04-23 19:05 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Daniel Gustafsson <[email protected]>; pgsql-hackers

On Thu, Apr 23, 2026 at 11:37 AM Andres Freund <[email protected]> wrote:
> How about instead making sure that actx->authz.interval never gets big enough
> to have any chance of overflowing during either the += 5 or the * 1000?  It's
> clearly ok to error out well before that...

It probably is, but I guess the approach depends on whether you prefer
checking at the time of operation, or attempting to reason about it
ahead of time in far-away code. With the latter, if additional math is
added in the future, then either the new overflow hazard gets missed,
or the ceiling gets lowered again, or the new math gets an overflow
check when the others don't. I prefer the time-of-use pattern,
personally.

--Jacob





^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: oauth integer overflow
@ 2026-04-28 11:17  Daniel Gustafsson <[email protected]>
  parent: Jacob Champion <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Daniel Gustafsson @ 2026-04-28 11:17 UTC (permalink / raw)
  To: Jacob Champion <[email protected]>; +Cc: Andres Freund <[email protected]>; pgsql-hackers

> On 23 Apr 2026, at 21:05, Jacob Champion <[email protected]> wrote:
> 
> On Thu, Apr 23, 2026 at 11:37 AM Andres Freund <[email protected]> wrote:
>> How about instead making sure that actx->authz.interval never gets big enough
>> to have any chance of overflowing during either the += 5 or the * 1000?  It's
>> clearly ok to error out well before that...
> 
> It probably is, but I guess the approach depends on whether you prefer
> checking at the time of operation, or attempting to reason about it
> ahead of time in far-away code. With the latter, if additional math is
> added in the future, then either the new overflow hazard gets missed,
> or the ceiling gets lowered again, or the new math gets an overflow
> check when the others don't. I prefer the time-of-use pattern,
> personally.

I am fine with your approach in the attached patch.  If you don't like the
static assert you could move it to be out of the way, and expand the comment
for it to what it means if it hits.  Just one small nitpick on the patch:

+	 * LONG_MAX milliseconds is 24 days on 32-bit platforms,
+	 * which for most people is going to be equivalent to a
+	 * disabled timer... but avoid overflow in case the

When teading "disabled timer" I interpret that as a timer which is 0 and has no
interval (which might be due to not being a native speaker), but what it
actually describes is an interval which (in practice) never ends.  Perhaps it
could be phrased more like "for most people is going to be equivalent to a
never ending interval".

--
Daniel Gustafsson






^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: oauth integer overflow
@ 2026-06-05 19:06  Jacob Champion <[email protected]>
  parent: Daniel Gustafsson <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Jacob Champion @ 2026-06-05 19:06 UTC (permalink / raw)
  To: Daniel Gustafsson <[email protected]>; +Cc: Andres Freund <[email protected]>; pgsql-hackers

On Thu, Apr 23, 2026 at 11:31 AM Jacob Champion
<[email protected]> wrote:
> Attached. The static_assert for the millisecond calculation is the
> only part I don't really like, but doing an overflow check on a
> calculation that can't overflow int64 is even more verbose/wasteful.

I was preparing to commit this for beta1 last week, and I realized
that I've changed my tune. With all the recent backports of overflow
checks, I think I need to be reaching for them by default, especially
in a non-performance-critical path.

v2 rewrites that part with a checked multiplication, which removes any
need for a static_assert complication.

On Tue, Apr 28, 2026 at 4:18 AM Daniel Gustafsson <[email protected]> wrote:
> When teading "disabled timer" I interpret that as a timer which is 0 and has no
> interval (which might be due to not being a native speaker), but what it
> actually describes is an interval which (in practice) never ends.  Perhaps it
> could be phrased more like "for most people is going to be equivalent to a
> never ending interval".

This has been completely rewritten now; see what you think.

Thanks!
--Jacob


Attachments:

  [application/octet-stream] v2-0001-libpq-oauth-Avoid-overflow-for-very-large-interva.patch (3.4K, 2-v2-0001-libpq-oauth-Avoid-overflow-for-very-large-interva.patch)
  download | inline diff:
From d6d3a851e73fede3b3d601f735e099d3206c5cff Mon Sep 17 00:00:00 2001
From: Jacob Champion <[email protected]>
Date: Fri, 29 May 2026 16:21:46 -0700
Subject: [PATCH v2] libpq-oauth: Avoid overflow for very large intervals

The slow_down interval parsing code checks explicitly for overflow, but
since it does that after the signed overflow has already occurred, we
end up inviting undefined behavior from the compiler anyway.

Use checked arithmetic instead. set_timer() takes a long int in order to
interface nicely with libcurl, so use an int32 as the interval counter
and clamp to LONG_MAX during conversion to milliseconds.

Backpatch to 18, where libpq-oauth was introduced.

Reported-by: Andres Freund <[email protected]>
Reviewed-by: Daniel Gustafsson <[email protected]>
Discussion: https://postgr.es/m/qtclihmrkq67ach3xjxyi4qcksstin5qxwsnkqefkmotxwh4g6%40ae2bj6jvcmry
Backpatch-through: 18
---
 src/interfaces/libpq-oauth/oauth-curl.c | 35 ++++++++++++++++++-------
 1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/src/interfaces/libpq-oauth/oauth-curl.c b/src/interfaces/libpq-oauth/oauth-curl.c
index 7ba75fc6d04..260137291cb 100644
--- a/src/interfaces/libpq-oauth/oauth-curl.c
+++ b/src/interfaces/libpq-oauth/oauth-curl.c
@@ -28,6 +28,7 @@
 #error libpq-oauth is not supported on this platform
 #endif
 
+#include "common/int.h"
 #include "common/jsonapi.h"
 #include "mb/pg_wchar.h"
 #include "oauth-curl.h"
@@ -136,7 +137,7 @@ struct device_authz
 
 	/* Fields below are parsed from the corresponding string above. */
 	int			expires_in;
-	int			interval;
+	int32		interval;
 };
 
 static void
@@ -1020,7 +1021,7 @@ parse_json_number(const char *s)
  * expensive network polling loop.) Tests may remove the lower bound with
  * PGOAUTHDEBUG, for improved performance.
  */
-static int
+static int32
 parse_interval(struct async_ctx *actx, const char *interval_str)
 {
 	double		parsed;
@@ -1031,8 +1032,8 @@ parse_interval(struct async_ctx *actx, const char *interval_str)
 	if (parsed < 1)
 		return (actx->debug_flags & OAUTHDEBUG_UNSAFE_DOS_ENDPOINT) ? 0 : 1;
 
-	else if (parsed >= INT_MAX)
-		return INT_MAX;
+	else if (parsed >= INT32_MAX)
+		return INT32_MAX;
 
 	return parsed;
 }
@@ -2620,10 +2621,7 @@ handle_token_response(struct async_ctx *actx, char **token)
 	 */
 	if (strcmp(err->error, "slow_down") == 0)
 	{
-		int			prev_interval = actx->authz.interval;
-
-		actx->authz.interval += 5;
-		if (actx->authz.interval < prev_interval)
+		if (pg_add_s32_overflow(actx->authz.interval, 5, &actx->authz.interval))
 		{
 			actx_error(actx, "slow_down interval overflow");
 			goto token_cleanup;
@@ -2949,8 +2947,25 @@ pg_fe_run_oauth_flow_impl(PGconn *conn, PGoauthBearerRequestV2 *request,
 				 * Wait for the required interval before issuing the next
 				 * request.
 				 */
-				if (!set_timer(actx, actx->authz.interval * 1000))
-					goto error_return;
+				{
+					/*
+					 * Avoid overflow of long int. (By the time we reach
+					 * LONG_MAX milliseconds -- 24 days on 32-bit platforms --
+					 * continuing to honor slow_down requests seems pretty
+					 * pointless anyway.)
+					 */
+					int64		interval_ms;
+
+					if (pg_mul_s64_overflow(actx->authz.interval, 1000,
+											&interval_ms)
+						|| (interval_ms > LONG_MAX))
+					{
+						interval_ms = LONG_MAX;
+					}
+
+					if (!set_timer(actx, (long) interval_ms))
+						goto error_return;
+				}
 
 				/*
 				 * No Curl requests are running, so we can simplify by having
-- 
2.34.1



^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: oauth integer overflow
@ 2026-06-05 19:25  Daniel Gustafsson <[email protected]>
  parent: Jacob Champion <[email protected]>
  0 siblings, 0 replies; 10+ messages in thread

From: Daniel Gustafsson @ 2026-06-05 19:25 UTC (permalink / raw)
  To: Jacob Champion <[email protected]>; +Cc: Andres Freund <[email protected]>; pgsql-hackers

> On 5 Jun 2026, at 21:06, Jacob Champion <[email protected]> wrote:
> 
> On Thu, Apr 23, 2026 at 11:31 AM Jacob Champion
> <[email protected]> wrote:
>> Attached. The static_assert for the millisecond calculation is the
>> only part I don't really like, but doing an overflow check on a
>> calculation that can't overflow int64 is even more verbose/wasteful.
> 
> I was preparing to commit this for beta1 last week, and I realized
> that I've changed my tune. With all the recent backports of overflow
> checks, I think I need to be reaching for them by default, especially
> in a non-performance-critical path.
> 
> v2 rewrites that part with a checked multiplication, which removes any
> need for a static_assert complication.

I agree with this approach, +1 on this version.

> On Tue, Apr 28, 2026 at 4:18 AM Daniel Gustafsson <[email protected]> wrote:
>> When teading "disabled timer" I interpret that as a timer which is 0 and has no
>> interval (which might be due to not being a native speaker), but what it
>> actually describes is an interval which (in practice) never ends.  Perhaps it
>> could be phrased more like "for most people is going to be equivalent to a
>> never ending interval".
> 
> This has been completely rewritten now; see what you think.

LGTM.

--
Daniel Gustafsson







^ permalink  raw  reply  [nested|flat] 10+ messages in thread


end of thread, other threads:[~2026-06-05 19:25 UTC | newest]

Thread overview: 10+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-04-23 16:12 oauth integer overflow Andres Freund <[email protected]>
2026-04-23 17:28 ` Daniel Gustafsson <[email protected]>
2026-04-23 17:49   ` Jacob Champion <[email protected]>
2026-04-23 18:17     ` Daniel Gustafsson <[email protected]>
2026-04-23 18:31       ` Jacob Champion <[email protected]>
2026-04-23 18:37         ` Andres Freund <[email protected]>
2026-04-23 19:05           ` Jacob Champion <[email protected]>
2026-04-28 11:17             ` Daniel Gustafsson <[email protected]>
2026-06-05 19:06               ` Jacob Champion <[email protected]>
2026-06-05 19:25                 ` Daniel Gustafsson <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox