This is an automated email from the ASF dual-hosted git repository.

tuhaihe pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudberry.git

commit dd6cb001c755407d5add4caea4547b2985b5eb86
Author: Tom Lane <[email protected]>
AuthorDate: Mon May 11 05:13:50 2026 -0700

    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 <[email protected]>
    Author: Tom Lane <[email protected]>
    Backpatch-through: 14
    Security: CVE-2026-6473
---
 src/backend/utils/adt/pg_locale.c | 87 ++++++++++++++++++++-------------------
 1 file changed, 44 insertions(+), 43 deletions(-)

diff --git a/src/backend/utils/adt/pg_locale.c 
b/src/backend/utils/adt/pg_locale.c
index d115a141bb8..e3127159bea 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -153,8 +153,8 @@ static UConverter *icu_converter = NULL;
 
 static UCollator *pg_ucol_open(const char *loc_str);
 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);
@@ -1822,8 +1822,9 @@ pg_strncoll_libc_win32_utf8(const char *arg1, size_t 
len1, const char *arg2,
        char       *buf = sbuf;
        char       *a1p,
                           *a2p;
-       int                     a1len = len1 * 2 + 2;
-       int                     a2len = len2 * 2 + 2;
+       size_t          a1len,
+                               a2len,
+                               buflen;
        int                     r;
        int                     result;
 
@@ -1833,8 +1834,16 @@ pg_strncoll_libc_win32_utf8(const char *arg1, size_t 
len1, const char *arg2,
        Assert(false);
 #endif
 
-       if (a1len + a2len > TEXTBUFLEN)
-               buf = palloc(a1len + a2len);
+       /*
+        * 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 (buflen > TEXTBUFLEN)
+               buf = palloc(buflen);
 
        a1p = buf;
        a2p = buf + a1len;
@@ -1985,12 +1994,11 @@ static int
 pg_strncoll_icu_no_utf8(const char *arg1, int32_t len1,
                                                const char *arg2, int32_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;
@@ -2005,14 +2013,13 @@ pg_strncoll_icu_no_utf8(const char *arg1, int32_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);
-
-       if (bufsize1 + bufsize2 > TEXTBUFLEN)
-               buf = palloc(bufsize1 + bufsize2);
+       /* 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);
 
-       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);
@@ -2193,11 +2200,9 @@ static size_t
 pg_strnxfrm_icu(char *dest, const char *src, int32_t srclen, int32_t destsize,
                                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;
 
        Assert(locale->provider == COLLPROVIDER_ICU);
@@ -2206,12 +2211,8 @@ pg_strnxfrm_icu(char *dest, const char *src, int32_t 
srclen, int32_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);
 
@@ -2226,8 +2227,8 @@ pg_strnxfrm_icu(char *dest, const char *src, int32_t 
srclen, int32_t destsize,
        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');
@@ -2240,14 +2241,12 @@ static size_t
 pg_strnxfrm_prefix_icu_no_utf8(char *dest, const char *src, int32_t srclen,
                                                           int32_t destsize, 
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;
 
        Assert(locale->provider == COLLPROVIDER_ICU);
@@ -2257,12 +2256,8 @@ pg_strnxfrm_prefix_icu_no_utf8(char *dest, const char 
*src, int32_t srclen,
 
        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);
 
@@ -2280,6 +2275,9 @@ pg_strnxfrm_prefix_icu_no_utf8(char *dest, const char 
*src, int32_t srclen,
                                (errmsg("sort key generation failed: %s",
                                                u_errorName(status))));
 
+       if (uchar != sbuf)
+               pfree(uchar);
+
        return result_bsize;
 }
 
@@ -2623,8 +2621,12 @@ init_icu_converter(void)
 
 /*
  * Find length, in UChars, of given string if converted to UChar string.
+ *
+ * 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;
@@ -2648,7 +2650,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,
@@ -2677,7 +2678,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);
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to