Refine memory allocation in ICU conversions
authorPeter Eisentraut <peter_e@gmx.net>
Sat, 24 Jun 2017 13:39:24 +0000 (09:39 -0400)
committerPeter Eisentraut <peter_e@gmx.net>
Sun, 2 Jul 2017 03:08:37 +0000 (23:08 -0400)
The simple calculations done to estimate the size of the output buffers
for ucnv_fromUChars() and ucnv_toUChars() could overflow int32_t for
large strings.  To avoid that, go the long way and run the function
first without an output buffer to get the correct output buffer size
requirement.

src/backend/utils/adt/pg_locale.c

index eae9fcb0def66a28ec19d4419c88068d2293e6e5..12419fc8df9089bd86e0278103eb65a51b94305f 100644 (file)
@@ -1511,14 +1511,22 @@ icu_to_uchar(UChar **buff_uchar, const char *buff, size_t nbytes)
 
    init_icu_converter();
 
-   len_uchar = 2 * nbytes + 1; /* max length per docs */
-   *buff_uchar = palloc(len_uchar * sizeof(**buff_uchar));
    status = U_ZERO_ERROR;
-   len_uchar = ucnv_toUChars(icu_converter, *buff_uchar, len_uchar,
+   len_uchar = ucnv_toUChars(icu_converter, NULL, 0,
+                             buff, nbytes, &status);
+   if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
+       ereport(ERROR,
+               (errmsg("ucnv_toUChars failed: %s", u_errorName(status))));
+
+   *buff_uchar = palloc((len_uchar + 1) * sizeof(**buff_uchar));
+
+   status = U_ZERO_ERROR;
+   len_uchar = ucnv_toUChars(icu_converter, *buff_uchar, len_uchar + 1,
                              buff, nbytes, &status);
    if (U_FAILURE(status))
        ereport(ERROR,
                (errmsg("ucnv_toUChars failed: %s", u_errorName(status))));
+
    return len_uchar;
 }
 
@@ -1541,14 +1549,22 @@ icu_from_uchar(char **result, const UChar *buff_uchar, int32_t len_uchar)
 
    init_icu_converter();
 
-   len_result = UCNV_GET_MAX_BYTES_FOR_STRING(len_uchar, ucnv_getMaxCharSize(icu_converter));
+   status = U_ZERO_ERROR;
+   len_result = ucnv_fromUChars(icu_converter, NULL, 0,
+                                buff_uchar, len_uchar, &status);
+   if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
+       ereport(ERROR,
+               (errmsg("ucnv_fromUChars failed: %s", u_errorName(status))));
+
    *result = palloc(len_result + 1);
+
    status = U_ZERO_ERROR;
    len_result = ucnv_fromUChars(icu_converter, *result, len_result + 1,
                                 buff_uchar, len_uchar, &status);
    if (U_FAILURE(status))
        ereport(ERROR,
                (errmsg("ucnv_fromUChars failed: %s", u_errorName(status))));
+
    return len_result;
 }
 #endif                         /* USE_ICU */