]> granicus.if.org Git - icu/commitdiff
ICU-12745 string limit on Locale(char*)
authorSteven R. Loomis <srl@icu-project.org>
Mon, 26 Sep 2016 22:09:14 +0000 (22:09 +0000)
committerSteven R. Loomis <srl@icu-project.org>
Mon, 26 Sep 2016 22:09:14 +0000 (22:09 +0000)
X-SVN-Rev: 39356

icu4c/source/common/locid.cpp
icu4c/source/test/intltest/numfmtst.cpp

index 0941fea8f24712c769002eb16e2b2fe1d65375b6..714776a85ceb1187890e1eee34a83f36121bdd28 100644 (file)
@@ -44,6 +44,7 @@
 #include "uhash.h"
 #include "ucln_cmn.h"
 #include "ustr_imp.h"
+#include "charstr.h"
 
 U_CDECL_BEGIN
 static UBool U_CALLCONV locale_cleanup(void);
@@ -59,6 +60,12 @@ static UMutex gDefaultLocaleMutex = U_MUTEX_INITIALIZER;
 static UHashtable *gDefaultLocalesHashT = NULL;
 static Locale *gDefaultLocale = NULL;
 
+/**
+ * \def ULOC_STRING_LIMIT
+ * strings beyond this value crash in CharString
+ */
+#define ULOC_STRING_LIMIT 357913941
+
 U_NAMESPACE_END
 
 typedef enum ELocalePos {
@@ -285,13 +292,12 @@ Locale::Locale( const   char * newLanguage,
     }
     else
     {
-        MaybeStackArray<char, ULOC_FULLNAME_CAPACITY> togo;
+        UErrorCode status = U_ZERO_ERROR;
         int32_t size = 0;
         int32_t lsize = 0;
         int32_t csize = 0;
         int32_t vsize = 0;
         int32_t ksize = 0;
-        char    *p;
 
         // Calculate the size of the resulting string.
 
@@ -299,13 +305,23 @@ Locale::Locale( const   char * newLanguage,
         if ( newLanguage != NULL )
         {
             lsize = (int32_t)uprv_strlen(newLanguage);
+            if ( lsize < 0 || lsize > ULOC_STRING_LIMIT ) { // int32 wrap
+                setToBogus();
+                return;
+            }
             size = lsize;
         }
 
+        CharString togo(newLanguage, lsize, status); // start with newLanguage
+
         // _Country
         if ( newCountry != NULL )
         {
             csize = (int32_t)uprv_strlen(newCountry);
+            if ( csize < 0 || csize > ULOC_STRING_LIMIT ) { // int32 wrap
+                setToBogus();
+                return;
+            }
             size += csize;
         }
 
@@ -320,6 +336,10 @@ Locale::Locale( const   char * newLanguage,
 
             // remove trailing _'s
             vsize = (int32_t)uprv_strlen(newVariant);
+            if ( vsize < 0 || vsize > ULOC_STRING_LIMIT ) { // int32 wrap
+                setToBogus();
+                return;
+            }
             while( (vsize>1) && (newVariant[vsize-1] == SEP_CHAR) )
             {
                 vsize--;
@@ -344,70 +364,60 @@ Locale::Locale( const   char * newLanguage,
         if ( newKeywords != NULL)
         {
             ksize = (int32_t)uprv_strlen(newKeywords);
+            if ( ksize < 0 || ksize > ULOC_STRING_LIMIT ) {
+              setToBogus();
+              return;
+            }
             size += ksize + 1;
         }
 
-
-        //  NOW we have the full locale string..
-
-        /*if the whole string is longer than our internal limit, we need
-        to go to the heap for temporary buffers*/
-        if (size >= togo.getCapacity())
-        {
-            // If togo_heap could not be created, initialize with default settings.
-            if (togo.resize(size+1) == NULL) {
-                init(NULL, FALSE);
-            }
+        if (size < 0) {
+            setToBogus();
+            return;
         }
-
-        togo[0] = 0;
-
+        //  NOW we have the full locale string..
         // Now, copy it back.
-        p = togo.getAlias();
-        if ( lsize != 0 )
-        {
-            uprv_strcpy(p, newLanguage);
-            p += lsize;
-        }
+
+        // newLanguage is already copied
 
         if ( ( vsize != 0 ) || (csize != 0) )  // at least:  __v
         {                                      //            ^
-            *p++ = SEP_CHAR;
+            togo.append(SEP_CHAR, status);
         }
 
         if ( csize != 0 )
         {
-            uprv_strcpy(p, newCountry);
-            p += csize;
+            togo.append(newCountry, status);
         }
 
         if ( vsize != 0)
         {
-            *p++ = SEP_CHAR; // at least: __v
-
-            uprv_strncpy(p, newVariant, vsize);  // Must use strncpy because
-            p += vsize;                          // of trimming (above).
-            *p = 0; // terminate
+            togo.append(SEP_CHAR, status)
+                .append(newVariant, vsize, status);
         }
 
         if ( ksize != 0)
         {
             if (uprv_strchr(newKeywords, '=')) {
-                *p++ = '@'; /* keyword parsing */
+                togo.append('@', status); /* keyword parsing */
             }
             else {
-                *p++ = '_'; /* Variant parsing with a script */
+                togo.append('_', status); /* Variant parsing with a script */
                 if ( vsize == 0) {
-                    *p++ = '_'; /* No country found */
+                    togo.append('_', status); /* No country found */
                 }
             }
-            uprv_strcpy(p, newKeywords);
-            p += ksize;
+            togo.append(newKeywords, status);
         }
 
+        if (U_FAILURE(status)) {
+            // Something went wrong with appending, etc.
+            setToBogus();
+            return;
+        }
         // Parse it, because for example 'language' might really be a complete
         // string.
-        init(togo.getAlias(), FALSE);
+        init(togo.data(), FALSE);
     }
 }
 
index c3230991017b657667641d95e36cdd019d232e4a..015836b83990873cc32b5b2f0b423c9557b18e10 100644 (file)
@@ -2422,11 +2422,12 @@ void NumberFormatTest::TestSymbolsWithBadLocale(void) {
     for (i = 0; i < UPRV_LENGTHOF(badLocales); i++) {
         const char *localeName = badLocales[i];
         Locale locBad(localeName);
+        TEST_ASSERT_TRUE(!locBad.isBogus());
         UErrorCode status = U_ZERO_ERROR;
         UnicodeString intlCurrencySymbol((UChar)0xa4);
 
         intlCurrencySymbol.append((UChar)0xa4);
-
+        
         logln("Current locale is %s", Locale::getDefault().getName());
         Locale::setDefault(locBad, status);
         logln("Current locale is %s", Locale::getDefault().getName());