postgres.git / summary / log / commit / refs
commit d75b1dc96f0a15b71dd832285fafaac7a755f4cb
Author: Tom Lane <tgl@sss.pgh.pa.us>
Commit: Noah Misch <noah@leadboat.com>
Date: Mon May 11 12:13:50 2026 +0000
First released in: 14.23, 15.18, 16.14, 17.10, 18.4
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.
Back-patch of commit 8934f2136, done now because pg_add_size_overflow()
and friends are needed more widely for security fixes.
Author: Jacob Champion <jacob.champion@enterprisedb.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/CAOYmi%2B%3D%2BpqUd2MUitvgW1pAJuXgG_TKCVc3_Ek7pe8z9nkf%2BAg%40mail.gmail.com
Backpatch-through: 14-18
Security: CVE-2026-6473
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 e2617fbc5d2..e22cf9115c9 100644
--- a/src/include/common/int.h
+++ b/src/include/common/int.h
@@ -438,4 +438,71 @@ pg_mul_u64_overflow(uint64 a, uint64 b, uint64 *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.
+ */
+/*
+ * static inline bool
+ * pg_neg_size_overflow(size_t a, ssize_t *result)
+ * {
+ * ...
+ * }
+ */
+
#endif /* COMMON_INT_H */
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index b2331699bed..04a69b73a6b 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"
@@ -4113,27 +4114,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
@@ -4218,14 +4198,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;
}
@@ -4388,9 +4368,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
@@ -4400,22 +4380,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 75ef502235c..ffa04013402 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"
@@ -468,27 +469,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,
@@ -509,20 +489,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 0b710be5a97..32e4e30ea0d 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -27,6 +27,7 @@
#endif
#endif
+#include "common/int.h"
#include "libpq-fe.h"
#include "libpq-int.h"
#include "mb/pg_wchar.h"
@@ -2212,26 +2213,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.
*
@@ -2264,11 +2245,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)
@@ -2304,7 +2285,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;
[parent: 7fe365693959]