Skip to content

Commit

Permalink
Cherry-pick: ICU-21449 Infinite loop can occur with locale IDs that c…
Browse files Browse the repository at this point in the history
…ontain 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:
unicode-org/icu#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.
  • Loading branch information
jefgen authored Feb 1, 2021
1 parent 2f7df17 commit 8912565
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 3 deletions.
2 changes: 2 additions & 0 deletions icu/icu4c/source/common/uresbund.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
}
Expand Down
16 changes: 14 additions & 2 deletions icu/icu4c/source/test/cintltst/cloctst.c
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ void addLocaleTest(TestNode** root)
TESTCASE(TestBug20370);
TESTCASE(TestBug20321UnicodeLocaleKey);
TESTCASE(TestUsingDefaultWarning);
TESTCASE(TestBug21449InfiniteLoop);
}


Expand Down Expand Up @@ -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) {
Expand All @@ -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);
}
2 changes: 1 addition & 1 deletion icu/icu4c/source/test/cintltst/cloctst.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 8912565

Please sign in to comment.