ICU: check for U_STRING_NOT_TERMINATED_WARNING.
authorJeff Davis <[email protected]>
Wed, 17 May 2023 20:43:41 +0000 (13:43 -0700)
committerJeff Davis <[email protected]>
Wed, 17 May 2023 21:18:45 +0000 (14:18 -0700)
Fixes memory error in cases where the length of the language name
returned by uloc_getLanguage() is exactly ULOC_LANG_CAPACITY, in which
case the status is set to U_STRING_NOT_TERMINATED_WARNING.

Also check in call sites for other ICU functions that are expected to
return a C string to be safe (no bug is known at these other call
sites).

Reported-by: Alexander Lakhin
Discussion: https://postgr.es/m/2098874d-c111-41e4-9063-30bcf135226b@gmail.com

src/backend/utils/adt/pg_locale.c
src/bin/initdb/initdb.c

index de678da6033693cd6dea37998feb83bb84a2304a..eea1d1ae0ff8ad94db67c1087e7f9004cc63af15 100644 (file)
@@ -2468,7 +2468,7 @@ pg_ucol_open(const char *loc_str)
 
                status = U_ZERO_ERROR;
                uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status);
-               if (U_FAILURE(status))
+               if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
                {
                        ereport(ERROR,
                                        (errmsg("could not get language from locale \"%s\": %s",
@@ -2504,7 +2504,7 @@ pg_ucol_open(const char *loc_str)
                 * Pretend the error came from ucol_open(), for consistent error
                 * message across ICU versions.
                 */
-               if (U_FAILURE(status))
+               if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
                {
                        ucol_close(collator);
                        ereport(ERROR,
@@ -2639,7 +2639,8 @@ icu_from_uchar(char **result, const UChar *buff_uchar, int32_t len_uchar)
        status = U_ZERO_ERROR;
        len_result = ucnv_fromUChars(icu_converter, *result, len_result + 1,
                                                                 buff_uchar, len_uchar, &status);
-       if (U_FAILURE(status))
+       if (U_FAILURE(status) ||
+               status == U_STRING_NOT_TERMINATED_WARNING)
                ereport(ERROR,
                                (errmsg("%s failed: %s", "ucnv_fromUChars",
                                                u_errorName(status))));
@@ -2681,7 +2682,7 @@ icu_set_collation_attributes(UCollator *collator, const char *loc,
        icu_locale_id = palloc(len + 1);
        *status = U_ZERO_ERROR;
        len = uloc_canonicalize(loc, icu_locale_id, len + 1, status);
-       if (U_FAILURE(*status))
+       if (U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING)
                return;
 
        lower_str = asc_tolower(icu_locale_id, strlen(icu_locale_id));
@@ -2765,7 +2766,6 @@ icu_set_collation_attributes(UCollator *collator, const char *loc,
 
        pfree(lower_str);
 }
-
 #endif
 
 /*
@@ -2789,7 +2789,7 @@ icu_language_tag(const char *loc_str, int elevel)
 
        status = U_ZERO_ERROR;
        uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status);
-       if (U_FAILURE(status))
+       if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
        {
                if (elevel > 0)
                        ereport(elevel,
@@ -2811,19 +2811,12 @@ icu_language_tag(const char *loc_str, int elevel)
        langtag = palloc(buflen);
        while (true)
        {
-               int32_t         len;
-
                status = U_ZERO_ERROR;
-               len = uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status);
+               uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status);
 
-               /*
-                * If the result fits in the buffer exactly (len == buflen),
-                * uloc_toLanguageTag() will return success without nul-terminating
-                * the result. Check for either U_BUFFER_OVERFLOW_ERROR or len >=
-                * buflen and try again.
-                */
+               /* try again if the buffer is not large enough */
                if ((status == U_BUFFER_OVERFLOW_ERROR ||
-                        (U_SUCCESS(status) && len >= buflen)) &&
+                        status == U_STRING_NOT_TERMINATED_WARNING) &&
                        buflen < MaxAllocSize)
                {
                        buflen = Min(buflen * 2, MaxAllocSize);
@@ -2878,7 +2871,7 @@ icu_validate_locale(const char *loc_str)
        /* validate that we can extract the language */
        status = U_ZERO_ERROR;
        uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status);
-       if (U_FAILURE(status))
+       if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
        {
                ereport(elevel,
                                (errmsg("could not get language from ICU locale \"%s\": %s",
@@ -2901,7 +2894,7 @@ icu_validate_locale(const char *loc_str)
 
                status = U_ZERO_ERROR;
                uloc_getLanguage(otherloc, otherlang, ULOC_LANG_CAPACITY, &status);
-               if (U_FAILURE(status))
+               if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
                        continue;
 
                if (strcmp(lang, otherlang) == 0)
index e03d498b1e073e4447b7108b4ee28c5b526ab8df..30b576932fd049e8f05aee667cef8eda8b51cd72 100644 (file)
@@ -2252,7 +2252,7 @@ icu_language_tag(const char *loc_str)
 
        status = U_ZERO_ERROR;
        uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status);
-       if (U_FAILURE(status))
+       if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
        {
                pg_fatal("could not get language from locale \"%s\": %s",
                                 loc_str, u_errorName(status));
@@ -2272,19 +2272,12 @@ icu_language_tag(const char *loc_str)
        langtag = pg_malloc(buflen);
        while (true)
        {
-               int32_t         len;
-
                status = U_ZERO_ERROR;
-               len = uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status);
+               uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status);
 
-               /*
-                * If the result fits in the buffer exactly (len == buflen),
-                * uloc_toLanguageTag() will return success without nul-terminating
-                * the result. Check for either U_BUFFER_OVERFLOW_ERROR or len >=
-                * buflen and try again.
-                */
+               /* try again if the buffer is not large enough */
                if (status == U_BUFFER_OVERFLOW_ERROR ||
-                       (U_SUCCESS(status) && len >= buflen))
+                       status == U_STRING_NOT_TERMINATED_WARNING)
                {
                        buflen = buflen * 2;
                        langtag = pg_realloc(langtag, buflen);