From 89125658852f6895ebdcd684ff8c38b9fee80f31 Mon Sep 17 00:00:00 2001 From: Jeff Genovy <29107334+jefgen@users.noreply.github.com> Date: Mon, 1 Feb 2021 12:14:24 -0800 Subject: [PATCH] Cherry-pick: ICU-21449 Infinite loop can occur with locale IDs that contain RES_PATH_SEPARATOR (#66) This change cherry-picks the upstream fix for the issue of an infinite loop when calling various APIs with locale IDs that contain `RES_PATH_SEPARATOR`. Upstream ticket: https://unicode-org.atlassian.net/browse/ICU-21449 Upstream PR: https://github.com/unicode-org/icu/pull/1549 Detailed Description (from the upstream PR): When calling APIs like `uloc_getDisplayLanguage`, if the input locale ID has `/` (the `RES_PATH_SEPARATOR` value) then the resource look-up code ends up in an infinite loop. For example, the following code will never return: ```c++ UErrorCode status = U_ZERO_ERROR; const char* localeNameTemp = "/"; // RES_PATH_SEPARATOR uloc_getDisplayLanguage(localeNameTemp, localeNameTemp, NULL, 0, &status); ``` The `do {...} while` loop in `ures_getByKeyWithFallback` calls `res_findResource`, but it never checks to see if the returned resource is `RES_BOGUS`, meaning that for some paths it will never terminate. (Thanks to am11 for doing the initial investigation into this issue.) This PR also includes a test case for the issue. Note that there is nothing really "tested" by the test case, as the test is to simply ensure that calling the API doesn't cause a hang due to the infinite loop. --- icu/icu4c/source/common/uresbund.cpp | 2 ++ icu/icu4c/source/test/cintltst/cloctst.c | 16 ++++++++++++++-- icu/icu4c/source/test/cintltst/cloctst.h | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/icu/icu4c/source/common/uresbund.cpp b/icu/icu4c/source/common/uresbund.cpp index 2ece87897d7..f17ba1be895 100644 --- a/icu/icu4c/source/common/uresbund.cpp +++ b/icu/icu4c/source/common/uresbund.cpp @@ -1908,6 +1908,8 @@ ures_getByKeyWithFallback(const UResourceBundle *resB, } else { break; } + } else if (res == RES_BOGUS) { + break; } } while(*myPath); /* Continue until the whole path is consumed */ } diff --git a/icu/icu4c/source/test/cintltst/cloctst.c b/icu/icu4c/source/test/cintltst/cloctst.c index aeb2c2a64d0..cd930bae3ae 100644 --- a/icu/icu4c/source/test/cintltst/cloctst.c +++ b/icu/icu4c/source/test/cintltst/cloctst.c @@ -280,6 +280,7 @@ void addLocaleTest(TestNode** root) TESTCASE(TestBug20370); TESTCASE(TestBug20321UnicodeLocaleKey); TESTCASE(TestUsingDefaultWarning); + TESTCASE(TestBug21449InfiniteLoop); } @@ -6980,11 +6981,12 @@ static void TestUsingDefaultWarning() { log_err("ERROR: in uloc_getDisplayKeywordValue %s %s return len:%d %s with status %d %s\n", keyword_value, keyword, length, errorOutputBuff, status, myErrorName(status)); } -} +} + // Test case for ICU-20575 // This test checks if the environment variable LANG is set, // and if so ensures that both C and C.UTF-8 cause ICU's default locale to be en_US_POSIX. -static void TestCDefaultLocale(){ +static void TestCDefaultLocale() { const char *defaultLocale = uloc_getDefault(); char *env_var = getenv("LANG"); if (env_var == NULL) { @@ -6995,3 +6997,13 @@ static void TestCDefaultLocale(){ log_err("The default locale for LANG=%s should be en_US_POSIX, not %s\n", env_var, defaultLocale); } } + +// Test case for ICU-21449 +static void TestBug21449InfiniteLoop() { + UErrorCode status = U_ZERO_ERROR; + const char* invalidLocaleId = RES_PATH_SEPARATOR_S; + + // The issue causes an infinite loop to occur when looking up a non-existent resource for the invalid locale ID, + // so the test is considered passed if the call to the API below returns anything at all. + uloc_getDisplayLanguage(invalidLocaleId, invalidLocaleId, NULL, 0, &status); +} diff --git a/icu/icu4c/source/test/cintltst/cloctst.h b/icu/icu4c/source/test/cintltst/cloctst.h index af7fa5d06a3..61058e8db15 100644 --- a/icu/icu4c/source/test/cintltst/cloctst.h +++ b/icu/icu4c/source/test/cintltst/cloctst.h @@ -142,7 +142,7 @@ static void TestToUnicodeLocaleType(void); static void TestToLegacyType(void); static void TestBug20149(void); static void TestCDefaultLocale(void); - +static void TestBug21449InfiniteLoop(void); /** * U_USING_DEFAULT_WARNING