Index: source/common/locid.cpp |
diff --git a/source/common/locid.cpp b/source/common/locid.cpp |
index 27e403065f2e933c99c215e6d2de93f856c5981f..d2781db95bdd8cf8b4e0c31f9659406add9e299e 100644 |
--- a/source/common/locid.cpp |
+++ b/source/common/locid.cpp |
@@ -1,6 +1,8 @@ |
+// Copyright (C) 2016 and later: Unicode, Inc. and others. |
+// License & terms of use: http://www.unicode.org/copyright.html |
/* |
********************************************************************** |
- * Copyright (C) 1997-2015, International Business Machines |
+ * Copyright (C) 1997-2016, International Business Machines |
* Corporation and others. All Rights Reserved. |
********************************************************************** |
* |
@@ -42,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); |
@@ -57,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 { |
@@ -283,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. |
@@ -297,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; |
} |
@@ -318,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--; |
@@ -342,70 +364,56 @@ 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); |
- } |
- } |
- |
- togo[0] = 0; |
- |
// 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); |
} |
} |
@@ -536,7 +544,7 @@ Locale& Locale::init(const char* localeID, UBool canonicalize) |
/* after uloc_getName/canonicalize() we know that only '_' are separators */ |
separator = field[0] = fullName; |
fieldIdx = 1; |
- while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR)) && fieldIdx < (int32_t)(sizeof(field)/sizeof(field[0]))-1) { |
+ while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR)) && fieldIdx < UPRV_LENGTHOF(field)-1) { |
field[fieldIdx] = separator + 1; |
fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]); |
fieldIdx++; |
@@ -662,6 +670,7 @@ Locale::setToBogus() { |
*script = 0; |
*country = 0; |
fIsBogus = TRUE; |
+ variantBegin = 0; |
} |
const Locale& U_EXPORT2 |