]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Add pg_add_size_overflow() and friends
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 11 May 2026 12:13:49 +0000 (05:13 -0700)
committerNoah Misch <noah@leadboat.com>
Mon, 11 May 2026 12:13:49 +0000 (05:13 -0700)
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
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/fe-print.c
src/interfaces/libpq/fe-protocol3.c

index 487124473d25b206a985a9742f39b348f50a5324..1f4218bfbc57d14459d7c2ba92e8c404c075efb5 100644 (file)
@@ -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 */
index 33f1af103d7467111afee38656a213812a77f6d8..31d91d1adbbed93fd15059fbbd69c2979b055253 100644 (file)
@@ -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"
@@ -4075,27 +4076,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
@@ -4179,14 +4159,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;
        }
 
@@ -4348,9 +4328,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
@@ -4360,22 +4340,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;
                        }
                }
index 4b9dd7da087eaf91765ab85d17bd359264d4d9e0..b1d653ab20f12b4aa09a50f1345a00ec5d5802cb 100644 (file)
@@ -33,6 +33,7 @@
 #endif
 #endif
 
+#include "common/int.h"
 #include "libpq-fe.h"
 #include "libpq-int.h"
 
@@ -462,27 +463,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,
@@ -503,20 +483,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);
index c42a52e162eacf919e021d793295f3b9d1deff41..bfaca3a3ad9ad05cf0f661952db20cdc9306d6a6 100644 (file)
@@ -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"
@@ -2247,26 +2248,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.
  *
@@ -2299,11 +2280,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)
 
@@ -2339,7 +2320,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;