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 */