1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-06 07:49:08 +03:00

ICU: check for U_STRING_NOT_TERMINATED_WARNING.

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
This commit is contained in:
Jeff Davis
2023-05-17 13:43:41 -07:00
parent 6de31ce446
commit 1c634f6647
2 changed files with 15 additions and 29 deletions

View File

@@ -2468,7 +2468,7 @@ pg_ucol_open(const char *loc_str)
status = U_ZERO_ERROR; status = U_ZERO_ERROR;
uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status); 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, ereport(ERROR,
(errmsg("could not get language from locale \"%s\": %s", (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 * Pretend the error came from ucol_open(), for consistent error
* message across ICU versions. * message across ICU versions.
*/ */
if (U_FAILURE(status)) if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
{ {
ucol_close(collator); ucol_close(collator);
ereport(ERROR, ereport(ERROR,
@@ -2639,7 +2639,8 @@ icu_from_uchar(char **result, const UChar *buff_uchar, int32_t len_uchar)
status = U_ZERO_ERROR; status = U_ZERO_ERROR;
len_result = ucnv_fromUChars(icu_converter, *result, len_result + 1, len_result = ucnv_fromUChars(icu_converter, *result, len_result + 1,
buff_uchar, len_uchar, &status); buff_uchar, len_uchar, &status);
if (U_FAILURE(status)) if (U_FAILURE(status) ||
status == U_STRING_NOT_TERMINATED_WARNING)
ereport(ERROR, ereport(ERROR,
(errmsg("%s failed: %s", "ucnv_fromUChars", (errmsg("%s failed: %s", "ucnv_fromUChars",
u_errorName(status)))); u_errorName(status))));
@@ -2681,7 +2682,7 @@ icu_set_collation_attributes(UCollator *collator, const char *loc,
icu_locale_id = palloc(len + 1); icu_locale_id = palloc(len + 1);
*status = U_ZERO_ERROR; *status = U_ZERO_ERROR;
len = uloc_canonicalize(loc, icu_locale_id, len + 1, status); 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; return;
lower_str = asc_tolower(icu_locale_id, strlen(icu_locale_id)); 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); pfree(lower_str);
} }
#endif #endif
/* /*
@@ -2789,7 +2789,7 @@ icu_language_tag(const char *loc_str, int elevel)
status = U_ZERO_ERROR; status = U_ZERO_ERROR;
uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status); 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) if (elevel > 0)
ereport(elevel, ereport(elevel,
@@ -2811,19 +2811,12 @@ icu_language_tag(const char *loc_str, int elevel)
langtag = palloc(buflen); langtag = palloc(buflen);
while (true) while (true)
{ {
int32_t len;
status = U_ZERO_ERROR; status = U_ZERO_ERROR;
len = uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status); uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status);
/* /* try again if the buffer is not large enough */
* 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.
*/
if ((status == U_BUFFER_OVERFLOW_ERROR || if ((status == U_BUFFER_OVERFLOW_ERROR ||
(U_SUCCESS(status) && len >= buflen)) && status == U_STRING_NOT_TERMINATED_WARNING) &&
buflen < MaxAllocSize) buflen < MaxAllocSize)
{ {
buflen = Min(buflen * 2, MaxAllocSize); buflen = Min(buflen * 2, MaxAllocSize);
@@ -2878,7 +2871,7 @@ icu_validate_locale(const char *loc_str)
/* validate that we can extract the language */ /* validate that we can extract the language */
status = U_ZERO_ERROR; status = U_ZERO_ERROR;
uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status); 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, ereport(elevel,
(errmsg("could not get language from ICU locale \"%s\": %s", (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; status = U_ZERO_ERROR;
uloc_getLanguage(otherloc, otherlang, ULOC_LANG_CAPACITY, &status); uloc_getLanguage(otherloc, otherlang, ULOC_LANG_CAPACITY, &status);
if (U_FAILURE(status)) if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
continue; continue;
if (strcmp(lang, otherlang) == 0) if (strcmp(lang, otherlang) == 0)

View File

@@ -2252,7 +2252,7 @@ icu_language_tag(const char *loc_str)
status = U_ZERO_ERROR; status = U_ZERO_ERROR;
uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status); 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", pg_fatal("could not get language from locale \"%s\": %s",
loc_str, u_errorName(status)); loc_str, u_errorName(status));
@@ -2272,19 +2272,12 @@ icu_language_tag(const char *loc_str)
langtag = pg_malloc(buflen); langtag = pg_malloc(buflen);
while (true) while (true)
{ {
int32_t len;
status = U_ZERO_ERROR; status = U_ZERO_ERROR;
len = uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status); uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status);
/* /* try again if the buffer is not large enough */
* 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.
*/
if (status == U_BUFFER_OVERFLOW_ERROR || if (status == U_BUFFER_OVERFLOW_ERROR ||
(U_SUCCESS(status) && len >= buflen)) status == U_STRING_NOT_TERMINATED_WARNING)
{ {
buflen = buflen * 2; buflen = buflen * 2;
langtag = pg_realloc(langtag, buflen); langtag = pg_realloc(langtag, buflen);