mirror of
https://github.com/postgres/postgres.git
synced 2026-05-21 09:48:06 -04:00
Add pg_add_size_overflow() and friends
Commit600086f47added (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 commit8934f2136, 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
This commit is contained in:
parent
7fe3656939
commit
d75b1dc96f
4 changed files with 91 additions and 83 deletions
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue