From c6806ca8269e0066947a3b01669b918e1f8e2480 Mon Sep 17 00:00:00 2001
From: Jacob Champion <jacob.champion@enterprisedb.com>
Date: Mon, 17 Nov 2025 13:47:45 -0800
Subject: [PATCH 1/2] Add pg_add_size_overflow() and friends

Commit 600086f47 added (several bespoke copies of) size_t addition with
overflow checks to libpq. Move this to common/int.h, along with
its subtraction and multiplication counterparts.

pg_neg_size_overflow() is intentionally omitted; I'm not sure we should
add SSIZE_MAX to win32_port.h for the sake of a function with no
callers.
---
 src/include/common/int.h            | 67 +++++++++++++++++++++++++++++
 src/interfaces/libpq/fe-exec.c      | 44 ++++++-------------
 src/interfaces/libpq/fe-print.c     | 36 ++++------------
 src/interfaces/libpq/fe-protocol3.c | 27 ++----------
 4 files changed, 91 insertions(+), 83 deletions(-)

diff --git a/src/include/common/int.h b/src/include/common/int.h
index 3973f13379d..a99f2b95c67 100644
--- a/src/include/common/int.h
+++ b/src/include/common/int.h
@@ -601,6 +601,73 @@ pg_neg_u64_overflow(uint64 a, int64 *result)
 #endif
 }
 
+/*
+ * size_t
+ */
+static inline bool
+pg_add_size_overflow(size_t a, size_t b, size_t *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+	return __builtin_add_overflow(a, b, result);
+#else
+	size_t		res = a + b;
+
+	if (res < a)
+	{
+		*result = 0x5EED;		/* to avoid spurious warnings */
+		return true;
+	}
+	*result = res;
+	return false;
+#endif
+}
+
+static inline bool
+pg_sub_size_overflow(size_t a, size_t b, size_t *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+	return __builtin_sub_overflow(a, b, result);
+#else
+	if (b > a)
+	{
+		*result = 0x5EED;		/* to avoid spurious warnings */
+		return true;
+	}
+	*result = a - b;
+	return false;
+#endif
+}
+
+static inline bool
+pg_mul_size_overflow(size_t a, size_t b, size_t *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+	return __builtin_mul_overflow(a, b, result);
+#else
+	size_t		res = a * b;
+
+	if (a != 0 && b != res / a)
+	{
+		*result = 0x5EED;		/* to avoid spurious warnings */
+		return true;
+	}
+	*result = res;
+	return false;
+#endif
+}
+
+/*
+ * pg_neg_size_overflow is currently omitted, to avoid having to reason about
+ * the portability of SSIZE_MIN/_MAX before a use case exists.
+ */
+#if 0
+static inline bool
+pg_neg_size_overflow(size_t a, ssize_t *result)
+{
+	...
+}
+#endif
+
 /*------------------------------------------------------------------------
  *
  * Comparison routines for integer types.
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index dcc8a447d66..7ab33930a39 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -24,6 +24,7 @@
 #include <unistd.h>
 #endif
 
+#include "common/int.h"
 #include "libpq-fe.h"
 #include "libpq-int.h"
 #include "mb/pg_wchar.h"
@@ -4220,27 +4221,6 @@ PQescapeString(char *to, const char *from, size_t length)
 }
 
 
-/*
- * Frontend version of the backend's add_size(), intended to be API-compatible
- * with the pg_add_*_overflow() helpers. Stores the result into *dst on success.
- * Returns true instead if the addition overflows.
- *
- * TODO: move to common/int.h
- */
-static bool
-add_size_overflow(size_t s1, size_t s2, size_t *dst)
-{
-	size_t		result;
-
-	result = s1 + s2;
-	if (result < s1 || result < s2)
-		return true;
-
-	*dst = result;
-	return false;
-}
-
-
 /*
  * Escape arbitrary strings.  If as_ident is true, we escape the result
  * as an identifier; if false, as a literal.  The result is returned in
@@ -4324,14 +4304,14 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
 	 * Allocate output buffer. Protect against overflow, in case the caller
 	 * has allocated a large fraction of the available size_t.
 	 */
-	if (add_size_overflow(input_len, num_quotes, &result_size) ||
-		add_size_overflow(result_size, 3, &result_size))	/* two quotes plus a NUL */
+	if (pg_add_size_overflow(input_len, num_quotes, &result_size) ||
+		pg_add_size_overflow(result_size, 3, &result_size)) /* two quotes plus a NUL */
 		goto overflow;
 
 	if (!as_ident && num_backslashes > 0)
 	{
-		if (add_size_overflow(result_size, num_backslashes, &result_size) ||
-			add_size_overflow(result_size, 2, &result_size))	/* for " E" prefix */
+		if (pg_add_size_overflow(result_size, num_backslashes, &result_size) ||
+			pg_add_size_overflow(result_size, 2, &result_size)) /* for " E" prefix */
 			goto overflow;
 	}
 
@@ -4493,9 +4473,9 @@ PQescapeByteaInternal(PGconn *conn,
 	if (use_hex)
 	{
 		/* We prepend "\x" and double each input character. */
-		if (add_size_overflow(len, bslash_len + 1, &len) ||
-			add_size_overflow(len, from_length, &len) ||
-			add_size_overflow(len, from_length, &len))
+		if (pg_add_size_overflow(len, bslash_len + 1, &len) ||
+			pg_add_size_overflow(len, from_length, &len) ||
+			pg_add_size_overflow(len, from_length, &len))
 			goto overflow;
 	}
 	else
@@ -4505,22 +4485,22 @@ PQescapeByteaInternal(PGconn *conn,
 		{
 			if (*vp < 0x20 || *vp > 0x7e)
 			{
-				if (add_size_overflow(len, bslash_len + 3, &len))	/* octal "\ooo" */
+				if (pg_add_size_overflow(len, bslash_len + 3, &len))	/* octal "\ooo" */
 					goto overflow;
 			}
 			else if (*vp == '\'')
 			{
-				if (add_size_overflow(len, 2, &len))	/* double each quote */
+				if (pg_add_size_overflow(len, 2, &len)) /* double each quote */
 					goto overflow;
 			}
 			else if (*vp == '\\')
 			{
-				if (add_size_overflow(len, bslash_len * 2, &len))	/* double each backslash */
+				if (pg_add_size_overflow(len, bslash_len * 2, &len))	/* double each backslash */
 					goto overflow;
 			}
 			else
 			{
-				if (add_size_overflow(len, 1, &len))
+				if (pg_add_size_overflow(len, 1, &len))
 					goto overflow;
 			}
 		}
diff --git a/src/interfaces/libpq/fe-print.c b/src/interfaces/libpq/fe-print.c
index cc667c9bac9..fc153bee630 100644
--- a/src/interfaces/libpq/fe-print.c
+++ b/src/interfaces/libpq/fe-print.c
@@ -33,6 +33,7 @@
 #endif
 #endif
 
+#include "common/int.h"
 #include "libpq-fe.h"
 #include "libpq-int.h"
 
@@ -451,27 +452,6 @@ do_field(const PQprintOpt *po, const PGresult *res,
 }
 
 
-/*
- * Frontend version of the backend's add_size(), intended to be API-compatible
- * with the pg_add_*_overflow() helpers. Stores the result into *dst on success.
- * Returns true instead if the addition overflows.
- *
- * TODO: move to common/int.h
- */
-static bool
-add_size_overflow(size_t s1, size_t s2, size_t *dst)
-{
-	size_t		result;
-
-	result = s1 + s2;
-	if (result < s1 || result < s2)
-		return true;
-
-	*dst = result;
-	return false;
-}
-
-
 static char *
 do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
 		  const char **fieldNames, unsigned char *fieldNotNum,
@@ -492,20 +472,20 @@ do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
 		for (; n < nFields; n++)
 		{
 			/* Field plus separator, plus 2 extra '-' in standard format. */
-			if (add_size_overflow(tot, fieldMax[n], &tot) ||
-				add_size_overflow(tot, fs_len, &tot) ||
-				(po->standard && add_size_overflow(tot, 2, &tot)))
+			if (pg_add_size_overflow(tot, fieldMax[n], &tot) ||
+				pg_add_size_overflow(tot, fs_len, &tot) ||
+				(po->standard && pg_add_size_overflow(tot, 2, &tot)))
 				goto overflow;
 		}
 		if (po->standard)
 		{
 			/* An extra separator at the front and back. */
-			if (add_size_overflow(tot, fs_len, &tot) ||
-				add_size_overflow(tot, fs_len, &tot) ||
-				add_size_overflow(tot, 2, &tot))
+			if (pg_add_size_overflow(tot, fs_len, &tot) ||
+				pg_add_size_overflow(tot, fs_len, &tot) ||
+				pg_add_size_overflow(tot, 2, &tot))
 				goto overflow;
 		}
-		if (add_size_overflow(tot, 1, &tot))	/* terminator */
+		if (pg_add_size_overflow(tot, 1, &tot)) /* terminator */
 			goto overflow;
 
 		border = malloc(tot);
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 838e42e661a..16f504a867d 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -25,6 +25,7 @@
 #include <netinet/tcp.h>
 #endif
 
+#include "common/int.h"
 #include "libpq-fe.h"
 #include "libpq-int.h"
 #include "mb/pg_wchar.h"
@@ -2404,26 +2405,6 @@ pqBuildStartupPacket3(PGconn *conn, int *packetlen,
 	return startpacket;
 }
 
-/*
- * Frontend version of the backend's add_size(), intended to be API-compatible
- * with the pg_add_*_overflow() helpers. Stores the result into *dst on success.
- * Returns true instead if the addition overflows.
- *
- * TODO: move to common/int.h
- */
-static bool
-add_size_overflow(size_t s1, size_t s2, size_t *dst)
-{
-	size_t		result;
-
-	result = s1 + s2;
-	if (result < s1 || result < s2)
-		return true;
-
-	*dst = result;
-	return false;
-}
-
 /*
  * Build a startup packet given a filled-in PGconn structure.
  *
@@ -2456,11 +2437,11 @@ build_startup_packet(const PGconn *conn, char *packet,
 	do { \
 		if (packet) \
 			strcpy(packet + packet_len, optname); \
-		if (add_size_overflow(packet_len, strlen(optname) + 1, &packet_len)) \
+		if (pg_add_size_overflow(packet_len, strlen(optname) + 1, &packet_len)) \
 			return 0; \
 		if (packet) \
 			strcpy(packet + packet_len, optval); \
-		if (add_size_overflow(packet_len, strlen(optval) + 1, &packet_len)) \
+		if (pg_add_size_overflow(packet_len, strlen(optval) + 1, &packet_len)) \
 			return 0; \
 	} while(0)
 
@@ -2496,7 +2477,7 @@ build_startup_packet(const PGconn *conn, char *packet,
 	/* Add trailing terminator */
 	if (packet)
 		packet[packet_len] = '\0';
-	if (add_size_overflow(packet_len, 1, &packet_len))
+	if (pg_add_size_overflow(packet_len, 1, &packet_len))
 		return 0;
 
 	return packet_len;
-- 
2.34.1

