mirror of
https://github.com/postgres/postgres.git
synced 2026-05-26 19:28:27 -04:00
Fix integer-overflow and alignment hazards in locale-related code.
pg_locale_icu.c was full of places where a very long input string could cause integer overflow while calculating a buffer size, leading to buffer overruns. It also was cavalier about using char-type local arrays as buffers holding arrays of UChar. The alignment of a char[] variable isn't guaranteed, so that this risked failure on alignment-picky platforms. The lack of complaints suggests that such platforms are very rare nowadays; but it's likely that we are paying a performance price on rather more platforms. Declare those arrays as UChar[] instead, keeping their physical size the same. pg_locale_libc.c's strncoll_libc_win32_utf8() also had the disease of assuming it could double or quadruple the input string length without concern for overflow. Reported-by: Xint Code Reported-by: Pavel Kohout <pavel.kohout@aisle.com> Author: Tom Lane <tgl@sss.pgh.pa.us> Backpatch-through: 14 Security: CVE-2026-6473
This commit is contained in:
parent
a1063eeced
commit
b2869ebc43
2 changed files with 45 additions and 50 deletions
|
|
@ -111,8 +111,8 @@ static size_t strnxfrm_prefix_icu_utf8(char *dest, size_t destsize,
|
|||
const char *src, ssize_t srclen,
|
||||
pg_locale_t locale);
|
||||
static void init_icu_converter(void);
|
||||
static size_t uchar_length(UConverter *converter,
|
||||
const char *str, int32_t len);
|
||||
static int32_t uchar_length(UConverter *converter,
|
||||
const char *str, int32_t len);
|
||||
static int32_t uchar_convert(UConverter *converter,
|
||||
UChar *dest, int32_t destlen,
|
||||
const char *src, int32_t srclen);
|
||||
|
|
@ -571,7 +571,7 @@ make_icu_collator(const char *iculocstr, const char *icurules)
|
|||
total = u_strlen(std_rules) + u_strlen(my_rules) + 1;
|
||||
|
||||
/* avoid leaking collator on OOM */
|
||||
all_rules = palloc_extended(sizeof(UChar) * total, MCXT_ALLOC_NO_OOM);
|
||||
all_rules = palloc_array_extended(UChar, total, MCXT_ALLOC_NO_OOM);
|
||||
if (!all_rules)
|
||||
{
|
||||
ucol_close(collator_std_rules);
|
||||
|
|
@ -755,23 +755,17 @@ size_t
|
|||
strnxfrm_icu(char *dest, size_t destsize, const char *src, ssize_t srclen,
|
||||
pg_locale_t locale)
|
||||
{
|
||||
char sbuf[TEXTBUFLEN];
|
||||
char *buf = sbuf;
|
||||
UChar *uchar;
|
||||
UChar sbuf[TEXTBUFLEN / sizeof(UChar)];
|
||||
UChar *uchar = sbuf;
|
||||
int32_t ulen;
|
||||
size_t uchar_bsize;
|
||||
Size result_bsize;
|
||||
|
||||
init_icu_converter();
|
||||
|
||||
ulen = uchar_length(icu_converter, src, srclen);
|
||||
|
||||
uchar_bsize = (ulen + 1) * sizeof(UChar);
|
||||
|
||||
if (uchar_bsize > TEXTBUFLEN)
|
||||
buf = palloc(uchar_bsize);
|
||||
|
||||
uchar = (UChar *) buf;
|
||||
if (ulen >= lengthof(sbuf))
|
||||
uchar = palloc_array(UChar, ulen + 1);
|
||||
|
||||
ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
|
||||
|
||||
|
|
@ -786,8 +780,8 @@ strnxfrm_icu(char *dest, size_t destsize, const char *src, ssize_t srclen,
|
|||
Assert(result_bsize > 0);
|
||||
result_bsize--;
|
||||
|
||||
if (buf != sbuf)
|
||||
pfree(buf);
|
||||
if (uchar != sbuf)
|
||||
pfree(uchar);
|
||||
|
||||
/* if dest is defined, it should be nul-terminated */
|
||||
Assert(result_bsize >= destsize || dest[result_bsize] == '\0');
|
||||
|
|
@ -862,7 +856,7 @@ icu_to_uchar(UChar **buff_uchar, const char *buff, size_t nbytes)
|
|||
|
||||
len_uchar = uchar_length(icu_converter, buff, nbytes);
|
||||
|
||||
*buff_uchar = palloc((len_uchar + 1) * sizeof(**buff_uchar));
|
||||
*buff_uchar = palloc_array(UChar, len_uchar + 1);
|
||||
len_uchar = uchar_convert(icu_converter,
|
||||
*buff_uchar, len_uchar + 1, buff, nbytes);
|
||||
|
||||
|
|
@ -919,7 +913,7 @@ convert_case_uchar(ICU_Convert_Func func, pg_locale_t mylocale,
|
|||
int32_t len_dest;
|
||||
|
||||
len_dest = len_source; /* try first with same length */
|
||||
*buff_dest = palloc(len_dest * sizeof(**buff_dest));
|
||||
*buff_dest = palloc_array(UChar, len_dest);
|
||||
status = U_ZERO_ERROR;
|
||||
len_dest = func(*buff_dest, len_dest, buff_source, len_source,
|
||||
mylocale->icu.locale, &status);
|
||||
|
|
@ -927,7 +921,7 @@ convert_case_uchar(ICU_Convert_Func func, pg_locale_t mylocale,
|
|||
{
|
||||
/* try again with adjusted length */
|
||||
pfree(*buff_dest);
|
||||
*buff_dest = palloc(len_dest * sizeof(**buff_dest));
|
||||
*buff_dest = palloc_array(UChar, len_dest);
|
||||
status = U_ZERO_ERROR;
|
||||
len_dest = func(*buff_dest, len_dest, buff_source, len_source,
|
||||
mylocale->icu.locale, &status);
|
||||
|
|
@ -1020,12 +1014,11 @@ static int
|
|||
strncoll_icu(const char *arg1, ssize_t len1,
|
||||
const char *arg2, ssize_t len2, pg_locale_t locale)
|
||||
{
|
||||
char sbuf[TEXTBUFLEN];
|
||||
char *buf = sbuf;
|
||||
UChar sbuf[TEXTBUFLEN / sizeof(UChar)];
|
||||
UChar *buf = sbuf;
|
||||
int32_t ulen1;
|
||||
int32_t ulen2;
|
||||
size_t bufsize1;
|
||||
size_t bufsize2;
|
||||
size_t bufsize;
|
||||
UChar *uchar1,
|
||||
*uchar2;
|
||||
int result;
|
||||
|
|
@ -1040,14 +1033,13 @@ strncoll_icu(const char *arg1, ssize_t len1,
|
|||
ulen1 = uchar_length(icu_converter, arg1, len1);
|
||||
ulen2 = uchar_length(icu_converter, arg2, len2);
|
||||
|
||||
bufsize1 = (ulen1 + 1) * sizeof(UChar);
|
||||
bufsize2 = (ulen2 + 1) * sizeof(UChar);
|
||||
/* ulen1+1 or ulen2+1 doesn't risk overflow, but summing them might */
|
||||
bufsize = add_size(ulen1 + 1, ulen2 + 1);
|
||||
if (bufsize > lengthof(sbuf))
|
||||
buf = palloc_array(UChar, bufsize);
|
||||
|
||||
if (bufsize1 + bufsize2 > TEXTBUFLEN)
|
||||
buf = palloc(bufsize1 + bufsize2);
|
||||
|
||||
uchar1 = (UChar *) buf;
|
||||
uchar2 = (UChar *) (buf + bufsize1);
|
||||
uchar1 = buf;
|
||||
uchar2 = buf + ulen1 + 1;
|
||||
|
||||
ulen1 = uchar_convert(icu_converter, uchar1, ulen1 + 1, arg1, len1);
|
||||
ulen2 = uchar_convert(icu_converter, uchar2, ulen2 + 1, arg2, len2);
|
||||
|
|
@ -1068,14 +1060,12 @@ strnxfrm_prefix_icu(char *dest, size_t destsize,
|
|||
const char *src, ssize_t srclen,
|
||||
pg_locale_t locale)
|
||||
{
|
||||
char sbuf[TEXTBUFLEN];
|
||||
char *buf = sbuf;
|
||||
UChar sbuf[TEXTBUFLEN / sizeof(UChar)];
|
||||
UChar *uchar = sbuf;
|
||||
UCharIterator iter;
|
||||
uint32_t state[2];
|
||||
UErrorCode status;
|
||||
int32_t ulen = -1;
|
||||
UChar *uchar = NULL;
|
||||
size_t uchar_bsize;
|
||||
int32_t ulen;
|
||||
Size result_bsize;
|
||||
|
||||
/* if encoding is UTF8, use more efficient strnxfrm_prefix_icu_utf8 */
|
||||
|
|
@ -1085,12 +1075,8 @@ strnxfrm_prefix_icu(char *dest, size_t destsize,
|
|||
|
||||
ulen = uchar_length(icu_converter, src, srclen);
|
||||
|
||||
uchar_bsize = (ulen + 1) * sizeof(UChar);
|
||||
|
||||
if (uchar_bsize > TEXTBUFLEN)
|
||||
buf = palloc(uchar_bsize);
|
||||
|
||||
uchar = (UChar *) buf;
|
||||
if (ulen >= lengthof(sbuf))
|
||||
uchar = palloc_array(UChar, ulen + 1);
|
||||
|
||||
ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
|
||||
|
||||
|
|
@ -1108,8 +1094,8 @@ strnxfrm_prefix_icu(char *dest, size_t destsize,
|
|||
(errmsg("sort key generation failed: %s",
|
||||
u_errorName(status))));
|
||||
|
||||
if (buf != sbuf)
|
||||
pfree(buf);
|
||||
if (uchar != sbuf)
|
||||
pfree(uchar);
|
||||
|
||||
return result_bsize;
|
||||
}
|
||||
|
|
@ -1145,8 +1131,12 @@ init_icu_converter(void)
|
|||
* Find length, in UChars, of given string if converted to UChar string.
|
||||
*
|
||||
* A length of -1 indicates that the input string is NUL-terminated.
|
||||
*
|
||||
* Note: given the assumption that the input string fits in MaxAllocSize,
|
||||
* the result cannot overflow int32_t. But callers must be careful about
|
||||
* multiplying the result by sizeof(UChar).
|
||||
*/
|
||||
static size_t
|
||||
static int32_t
|
||||
uchar_length(UConverter *converter, const char *str, int32_t len)
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
|
@ -1172,7 +1162,6 @@ uchar_convert(UConverter *converter, UChar *dest, int32_t destlen,
|
|||
UErrorCode status = U_ZERO_ERROR;
|
||||
int32_t ulen;
|
||||
|
||||
status = U_ZERO_ERROR;
|
||||
ulen = ucnv_toUChars(converter, dest, destlen, src, srclen, &status);
|
||||
if (U_FAILURE(status))
|
||||
ereport(ERROR,
|
||||
|
|
|
|||
|
|
@ -1061,8 +1061,9 @@ strncoll_libc_win32_utf8(const char *arg1, ssize_t len1, const char *arg2,
|
|||
char *buf = sbuf;
|
||||
char *a1p,
|
||||
*a2p;
|
||||
int a1len;
|
||||
int a2len;
|
||||
size_t a1len,
|
||||
a2len,
|
||||
buflen;
|
||||
int r;
|
||||
int result;
|
||||
|
||||
|
|
@ -1073,11 +1074,16 @@ strncoll_libc_win32_utf8(const char *arg1, ssize_t len1, const char *arg2,
|
|||
if (len2 == -1)
|
||||
len2 = strlen(arg2);
|
||||
|
||||
a1len = len1 * 2 + 2;
|
||||
a2len = len2 * 2 + 2;
|
||||
/*
|
||||
* In a 32-bit build, twice the input length can overflow size_t, so we
|
||||
* must be careful.
|
||||
*/
|
||||
a1len = add_size(add_size(len1, len1), 2);
|
||||
a2len = add_size(add_size(len2, len2), 2);
|
||||
buflen = add_size(a1len, a2len);
|
||||
|
||||
if (a1len + a2len > TEXTBUFLEN)
|
||||
buf = palloc(a1len + a2len);
|
||||
if (buflen > TEXTBUFLEN)
|
||||
buf = palloc(buflen);
|
||||
|
||||
a1p = buf;
|
||||
a2p = buf + a1len;
|
||||
|
|
|
|||
Loading…
Reference in a new issue