Description: CVE-2013-0900: Race condition in ICU. Credit to Google Chrome Security Team (Inferno) Patch taken from backport done by Fedora 17 Origin: upstream, http://www.icu-project.org/trac/changeset/32865, http://www.icu-project.org/trac/changeset/32908 Bug-Debian: http://bugs.debian.org/702346 Bug-RedHat: https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2013-0900 Index: icu-4.8.1.1/source/common/locid.cpp =================================================================== --- icu-4.8.1.1.orig/source/common/locid.cpp 2013-03-21 11:21:45.053153118 -0400 +++ icu-4.8.1.1/source/common/locid.cpp 2013-03-21 11:21:45.029153121 -0400 @@ -1,6 +1,6 @@ /* ********************************************************************** - * Copyright (C) 1997-2010, International Business Machines + * Copyright (C) 1997-2012, International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************** * @@ -32,6 +32,7 @@ #include "unicode/locid.h" #include "unicode/uloc.h" +#include "mutex.h" #include "umutex.h" #include "uassert.h" #include "cmemory.h" @@ -41,6 +42,21 @@ #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) +U_CDECL_BEGIN +static UBool U_CALLCONV locale_cleanup(void); +U_CDECL_END + +U_NAMESPACE_BEGIN + +static U_NAMESPACE_QUALIFIER Locale *gLocaleCache = NULL; + +// gDefaultLocaleMutex protects all access to gDefaultLocalesHashT and gDefaultLocale. +static UMTX gDefaultLocaleMutex = 0; +static UHashtable *gDefaultLocalesHashT = NULL; +static U_NAMESPACE_QUALIFIER Locale *gDefaultLocale = NULL; + +U_NAMESPACE_END + typedef enum ELocalePos { eENGLISH, eFRENCH, @@ -75,10 +91,6 @@ UBool valuesToo, UErrorCode *status); -static U_NAMESPACE_QUALIFIER Locale *gLocaleCache = NULL; -static U_NAMESPACE_QUALIFIER Locale *gDefaultLocale = NULL; -static UHashtable *gDefaultLocalesHashT = NULL; - U_CDECL_BEGIN // // Deleter function for Locales owned by the default Locale hash table/ @@ -100,24 +112,19 @@ if (gDefaultLocalesHashT) { uhash_close(gDefaultLocalesHashT); // Automatically deletes all elements, using deleter func. gDefaultLocalesHashT = NULL; + gDefaultLocale = NULL; } - else if (gDefaultLocale) { - // The cache wasn't created, and only one default locale was created. - delete gDefaultLocale; - } - gDefaultLocale = NULL; return TRUE; } U_CDECL_END U_NAMESPACE_BEGIN -// -// locale_set_default_internal. -// -void locale_set_default_internal(const char *id) -{ - UErrorCode status = U_ZERO_ERROR; + +Locale *locale_set_default_internal(const char *id, UErrorCode& status) { + // Synchronize this entire function. + Mutex lock(&gDefaultLocaleMutex); + UBool canonicalize = FALSE; // If given a NULL string for the locale id, grab the default @@ -125,17 +132,10 @@ // (Different from most other locale APIs, where a null name means use // the current ICU default locale.) if (id == NULL) { - umtx_lock(NULL); - id = uprv_getDefaultLocaleID(); - umtx_unlock(NULL); + id = uprv_getDefaultLocaleID(); // This function not thread safe? TODO: verify. canonicalize = TRUE; // always canonicalize host ID } - // put the locale id into a canonical form, - // in preparation for looking up this locale in the hash table of - // already-created locale objects. - // - status = U_ZERO_ERROR; char localeNameBuf[512]; if (canonicalize) { @@ -146,100 +146,37 @@ localeNameBuf[sizeof(localeNameBuf)-1] = 0; // Force null termination in event of // a long name filling the buffer. // (long names are truncated.) + // + if (U_FAILURE(status)) { + return gDefaultLocale; + } - // Lazy creation of the hash table itself, if needed. - UBool isOnlyLocale; - UMTX_CHECK(NULL, (gDefaultLocale == NULL), isOnlyLocale); - if (isOnlyLocale) { - // We haven't seen this locale id before. - // Create a new Locale object for it. - Locale *newFirstDefault = new Locale(Locale::eBOGUS); - if (newFirstDefault == NULL) { - // No way to report errors from here. - return; - } - newFirstDefault->init(localeNameBuf, FALSE); - umtx_lock(NULL); - if (gDefaultLocale == NULL) { - gDefaultLocale = newFirstDefault; // Assignment to gDefaultLocale must happen inside mutex - newFirstDefault = NULL; - ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); - } - // Else some other thread raced us through here, and set the new Locale. - // Use the hash table next. - umtx_unlock(NULL); - if (newFirstDefault == NULL) { - // We were successful in setting the locale, and we were the first one to set it. - return; - } - // else start using the hash table. - } - - // Lazy creation of the hash table itself, if needed. - UBool hashTableNeedsInit; - UMTX_CHECK(NULL, (gDefaultLocalesHashT == NULL), hashTableNeedsInit); - if (hashTableNeedsInit) { - status = U_ZERO_ERROR; - UHashtable *tHashTable = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); + if (gDefaultLocalesHashT == NULL) { + gDefaultLocalesHashT = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); if (U_FAILURE(status)) { - return; - } - uhash_setValueDeleter(tHashTable, deleteLocale); - umtx_lock(NULL); - if (gDefaultLocalesHashT == NULL) { - gDefaultLocalesHashT = tHashTable; - ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); - } else { - uhash_close(tHashTable); - hashTableNeedsInit = FALSE; + return gDefaultLocale; } - umtx_unlock(NULL); + uhash_setValueDeleter(gDefaultLocalesHashT, deleteLocale); + ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); } - // Hash table lookup, key is the locale full name - umtx_lock(NULL); Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf); - if (newDefault != NULL) { - // We have the requested locale in the hash table already. - // Just set it as default. Inside the mutex lock, for those troublesome processors. - gDefaultLocale = newDefault; - umtx_unlock(NULL); - } else { - umtx_unlock(NULL); - // We haven't seen this locale id before. - // Create a new Locale object for it. + if (newDefault == NULL) { newDefault = new Locale(Locale::eBOGUS); if (newDefault == NULL) { - // No way to report errors from here. - return; + status = U_MEMORY_ALLOCATION_ERROR; + return gDefaultLocale; } newDefault->init(localeNameBuf, FALSE); - - // Add newly created Locale to the hash table of default Locales - const char *key = newDefault->getName(); - U_ASSERT(uprv_strcmp(key, localeNameBuf) == 0); - umtx_lock(NULL); - Locale *hashTableVal = (Locale *)uhash_get(gDefaultLocalesHashT, key); - if (hashTableVal == NULL) { - if (hashTableNeedsInit) { - // This is the second request to set the locale. - // Cache the first one. - uhash_put(gDefaultLocalesHashT, (void *)gDefaultLocale->getName(), gDefaultLocale, &status); - } - uhash_put(gDefaultLocalesHashT, (void *)key, newDefault, &status); - gDefaultLocale = newDefault; - // ignore errors from hash table insert. (Couldn't do anything anyway) - // We can still set the default Locale, - // it just wont be cached, and will eventually leak. - } else { - // Some other thread raced us through here, and got the new Locale - // into the hash table before us. Use that one. - gDefaultLocale = hashTableVal; // Assignment to gDefaultLocale must happen inside mutex - delete newDefault; + uhash_put(gDefaultLocalesHashT, (char*) newDefault->getName(), newDefault, &status); + if (U_FAILURE(status)) { + return gDefaultLocale; } - umtx_unlock(NULL); } + gDefaultLocale = newDefault; + return gDefaultLocale; } + U_NAMESPACE_END /* sfb 07/21/99 */ @@ -247,7 +184,8 @@ locale_set_default(const char *id) { U_NAMESPACE_USE - locale_set_default_internal(id); + UErrorCode status = U_ZERO_ERROR; + locale_set_default_internal(id, status); } /* end */ @@ -255,7 +193,6 @@ locale_get_default(void) { U_NAMESPACE_USE - return Locale::getDefault().getName(); } @@ -661,19 +598,14 @@ const Locale& U_EXPORT2 Locale::getDefault() { - const Locale *retLocale; - UMTX_CHECK(NULL, gDefaultLocale, retLocale); - if (retLocale == NULL) { - locale_set_default_internal(NULL); - umtx_lock(NULL); - // Need a mutex in case some other thread set a new - // default inbetween when we set and when we get the new default. For - // processors with weak memory coherency, we might not otherwise see all - // of the newly created new default locale. - retLocale = gDefaultLocale; - umtx_unlock(NULL); + { + Mutex lock(&gDefaultLocaleMutex); + if (gDefaultLocale != NULL) { + return *gDefaultLocale; + } } - return *retLocale; + UErrorCode status = U_ZERO_ERROR; + return *locale_set_default_internal(NULL, status); } @@ -690,7 +622,7 @@ * This is a convenient way to access the default locale caching mechanisms. */ const char *localeID = newLocale.getName(); - locale_set_default_internal(localeID); + locale_set_default_internal(localeID, status); } Locale U_EXPORT2 Index: icu-4.8.1.1/source/common/unicode/locid.h =================================================================== --- icu-4.8.1.1.orig/source/common/unicode/locid.h 2013-03-21 11:21:45.053153118 -0400 +++ icu-4.8.1.1/source/common/unicode/locid.h 2013-03-21 11:21:45.041153115 -0400 @@ -732,7 +732,7 @@ * A friend to allow the default locale to be set by either the C or C++ API. * @internal */ - friend void locale_set_default_internal(const char *); + friend Locale *locale_set_default_internal(const char *, UErrorCode& status); }; inline UBool