Merge pull request #1 from valeriak/icu:rosa2019.05

Fix CVEs
This commit is contained in:
abondrov 2020-09-16 11:50:18 +00:00
commit 54da52d6ab
7 changed files with 771 additions and 1 deletions

232
CVE-2016-6293.patch Normal file
View file

@ -0,0 +1,232 @@
Index: /source/common/uloc.cpp
===================================================================
--- /source/common/uloc.cpp (revision 39108)
+++ /source/common/uloc.cpp (revision 39109)
@@ -2247,5 +2247,5 @@
float q;
int32_t dummy; /* to avoid uninitialized memory copy from qsort */
- char *locale;
+ char locale[ULOC_FULLNAME_CAPACITY+1];
} _acceptLangItem;
@@ -2289,7 +2289,5 @@
UErrorCode *status)
{
- _acceptLangItem *j;
- _acceptLangItem smallBuffer[30];
- char **strs;
+ MaybeStackArray<_acceptLangItem, 4> items; // Struct for collecting items.
char tmp[ULOC_FULLNAME_CAPACITY +1];
int32_t n = 0;
@@ -2301,9 +2299,5 @@
int32_t i;
int32_t l = (int32_t)uprv_strlen(httpAcceptLanguage);
- int32_t jSize;
- char *tempstr; /* Use for null pointer check */
-
- j = smallBuffer;
- jSize = UPRV_LENGTHOF(smallBuffer);
+
if(U_FAILURE(*status)) {
return -1;
@@ -2333,25 +2327,27 @@
t++;
}
- j[n].q = (float)_uloc_strtod(t,NULL);
+ items[n].q = (float)_uloc_strtod(t,NULL);
} else {
/* no semicolon - it's 1.0 */
- j[n].q = 1.0f;
+ items[n].q = 1.0f;
paramEnd = itemEnd;
}
- j[n].dummy=0;
+ items[n].dummy=0;
/* eat spaces prior to semi */
for(t=(paramEnd-1);(paramEnd>s)&&isspace(*t);t--)
;
- /* Check for null pointer from uprv_strndup */
- tempstr = uprv_strndup(s,(int32_t)((t+1)-s));
- if (tempstr == NULL) {
- *status = U_MEMORY_ALLOCATION_ERROR;
- return -1;
- }
- j[n].locale = tempstr;
- uloc_canonicalize(j[n].locale,tmp,UPRV_LENGTHOF(tmp),status);
- if(strcmp(j[n].locale,tmp)) {
- uprv_free(j[n].locale);
- j[n].locale=uprv_strdup(tmp);
+ int32_t slen = ((t+1)-s);
+ if(slen > ULOC_FULLNAME_CAPACITY) {
+ *status = U_BUFFER_OVERFLOW_ERROR;
+ return -1; // too big
+ }
+ uprv_strncpy(items[n].locale, s, slen);
+ items[n].locale[slen]=0; // terminate
+ int32_t clen = uloc_canonicalize(items[n].locale, tmp, UPRV_LENGTHOF(tmp)-1, status);
+ if(U_FAILURE(*status)) return -1;
+ if((clen!=slen) || (uprv_strncmp(items[n].locale, tmp, slen))) {
+ // canonicalization had an effect- copy back
+ uprv_strncpy(items[n].locale, tmp, clen);
+ items[n].locale[clen] = 0; // terminate
}
#if defined(ULOC_DEBUG)
@@ -2363,42 +2359,18 @@
s++;
}
- if(n>=jSize) {
- if(j==smallBuffer) { /* overflowed the small buffer. */
- j = static_cast<_acceptLangItem *>(uprv_malloc(sizeof(j[0])*(jSize*2)));
- if(j!=NULL) {
- uprv_memcpy(j,smallBuffer,sizeof(j[0])*jSize);
- }
+ if(n>=items.getCapacity()) { // If we need more items
+ if(NULL == items.resize(items.getCapacity()*2, items.getCapacity())) {
+ *status = U_MEMORY_ALLOCATION_ERROR;
+ return -1;
+ }
#if defined(ULOC_DEBUG)
- fprintf(stderr,"malloced at size %d\n", jSize);
+ fprintf(stderr,"malloced at size %d\n", items.getCapacity());
#endif
- } else {
- j = static_cast<_acceptLangItem *>(uprv_realloc(j, sizeof(j[0])*jSize*2));
-#if defined(ULOC_DEBUG)
- fprintf(stderr,"re-alloced at size %d\n", jSize);
-#endif
- }
- jSize *= 2;
- if(j==NULL) {
- *status = U_MEMORY_ALLOCATION_ERROR;
- return -1;
- }
- }
- }
- uprv_sortArray(j, n, sizeof(j[0]), uloc_acceptLanguageCompare, NULL, TRUE, status);
+ }
+ }
+ uprv_sortArray(items.getAlias(), n, sizeof(items[0]), uloc_acceptLanguageCompare, NULL, TRUE, status);
+ LocalArray<const char*> strs(new const char*[n], *status);
if(U_FAILURE(*status)) {
- if(j != smallBuffer) {
-#if defined(ULOC_DEBUG)
- fprintf(stderr,"freeing j %p\n", j);
-#endif
- uprv_free(j);
- }
- return -1;
- }
- strs = static_cast<char **>(uprv_malloc((size_t)(sizeof(strs[0])*n)));
- /* Check for null pointer */
- if (strs == NULL) {
- uprv_free(j); /* Free to avoid memory leak */
- *status = U_MEMORY_ALLOCATION_ERROR;
- return -1;
+ return -1;
}
for(i=0;i<n;i++) {
@@ -2406,18 +2378,8 @@
/*fprintf(stderr,"%d: s <%s> q <%g>\n", i, j[i].locale, j[i].q);*/
#endif
- strs[i]=j[i].locale;
+ strs[i]=items[i].locale;
}
res = uloc_acceptLanguage(result, resultAvailable, outResult,
- (const char**)strs, n, availableLocales, status);
- for(i=0;i<n;i++) {
- uprv_free(strs[i]);
- }
- uprv_free(strs);
- if(j != smallBuffer) {
-#if defined(ULOC_DEBUG)
- fprintf(stderr,"freeing j %p\n", j);
-#endif
- uprv_free(j);
- }
+ strs.getAlias(), n, availableLocales, status);
return res;
}
Index: /source/test/cintltst/cloctst.c
===================================================================
--- /source/test/cintltst/cloctst.c (revision 39108)
+++ /source/test/cintltst/cloctst.c (revision 39109)
@@ -2776,14 +2776,18 @@
const char *expect; /**< The expected locale result */
UAcceptResult res; /**< The expected error code */
+ UErrorCode expectStatus; /**< expected status */
} tests[] = {
- /*0*/{ 0, NULL, "mt_MT", ULOC_ACCEPT_VALID },
- /*1*/{ 1, NULL, "en", ULOC_ACCEPT_VALID },
- /*2*/{ 2, NULL, "en", ULOC_ACCEPT_FALLBACK },
- /*3*/{ 3, NULL, "", ULOC_ACCEPT_FAILED },
- /*4*/{ 4, NULL, "es", ULOC_ACCEPT_VALID },
-
- /*5*/{ 5, NULL, "en", ULOC_ACCEPT_VALID }, /* XF */
- /*6*/{ 6, NULL, "ja", ULOC_ACCEPT_FALLBACK }, /* XF */
- /*7*/{ 7, NULL, "zh", ULOC_ACCEPT_FALLBACK }, /* XF */
+ /*0*/{ 0, NULL, "mt_MT", ULOC_ACCEPT_VALID, U_ZERO_ERROR},
+ /*1*/{ 1, NULL, "en", ULOC_ACCEPT_VALID, U_ZERO_ERROR},
+ /*2*/{ 2, NULL, "en", ULOC_ACCEPT_FALLBACK, U_ZERO_ERROR},
+ /*3*/{ 3, NULL, "", ULOC_ACCEPT_FAILED, U_ZERO_ERROR},
+ /*4*/{ 4, NULL, "es", ULOC_ACCEPT_VALID, U_ZERO_ERROR},
+ /*5*/{ 5, NULL, "en", ULOC_ACCEPT_VALID, U_ZERO_ERROR}, /* XF */
+ /*6*/{ 6, NULL, "ja", ULOC_ACCEPT_FALLBACK, U_ZERO_ERROR}, /* XF */
+ /*7*/{ 7, NULL, "zh", ULOC_ACCEPT_FALLBACK, U_ZERO_ERROR}, /* XF */
+ /*8*/{ 8, NULL, "", ULOC_ACCEPT_FAILED, U_ZERO_ERROR }, /* */
+ /*9*/{ 9, NULL, "", ULOC_ACCEPT_FAILED, U_ZERO_ERROR }, /* */
+ /*10*/{10, NULL, "", ULOC_ACCEPT_FAILED, U_BUFFER_OVERFLOW_ERROR }, /* */
+ /*11*/{11, NULL, "", ULOC_ACCEPT_FAILED, U_BUFFER_OVERFLOW_ERROR }, /* */
};
const int32_t numTests = UPRV_LENGTHOF(tests);
@@ -2801,8 +2805,23 @@
"xxx-yyy;q=.01, xxx-yyy;q=.01, xxx-yyy;q=.01, xx-yy;q=.1, "
"es",
-
/*5*/ "zh-xx;q=0.9, en;q=0.6",
/*6*/ "ja-JA",
/*7*/ "zh-xx;q=0.9",
+ /*08*/ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", // 156
+ /*09*/ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB", // 157 (this hits U_STRING_NOT_TERMINATED_WARNING )
+ /*10*/ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABC", // 158
+ /*11*/ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", // 163 bytes
};
@@ -2819,5 +2838,9 @@
uenum_close(available);
log_verbose(" got %s, %s [%s]\n", tmp[0]?tmp:"(EMPTY)", acceptResult(outResult), u_errorName(status));
- if(outResult != tests[i].res) {
+ if(status != tests[i].expectStatus) {
+ log_err_status(status, "FAIL: expected status %s but got %s\n", u_errorName(tests[i].expectStatus), u_errorName(status));
+ } else if(U_SUCCESS(tests[i].expectStatus)) {
+ /* don't check content if expected failure */
+ if(outResult != tests[i].res) {
log_err_status(status, "FAIL: #%d: expected outResult of %s but got %s\n", i,
acceptResult( tests[i].res),
@@ -2825,9 +2848,10 @@
log_info("test #%d: http[%s], ICU[%s], expect %s, %s\n",
i, http[tests[i].httpSet], tests[i].icuSet, tests[i].expect,acceptResult(tests[i].res));
- }
- if((outResult>0)&&uprv_strcmp(tmp, tests[i].expect)) {
- log_err_status(status, "FAIL: #%d: expected %s but got %s\n", i, tests[i].expect, tmp);
- log_info("test #%d: http[%s], ICU[%s], expect %s, %s\n",
- i, http[tests[i].httpSet], tests[i].icuSet, tests[i].expect, acceptResult(tests[i].res));
+ }
+ if((outResult>0)&&uprv_strcmp(tmp, tests[i].expect)) {
+ log_err_status(status, "FAIL: #%d: expected %s but got %s\n", i, tests[i].expect, tmp);
+ log_info("test #%d: http[%s], ICU[%s], expect %s, %s\n",
+ i, http[tests[i].httpSet], tests[i].icuSet, tests[i].expect, acceptResult(tests[i].res));
+ }
}
}

176
CVE-2016-7415.patch Normal file
View file

@ -0,0 +1,176 @@
Index: source/common/locid.cpp
===================================================================
--- a/source/common/locid.cpp (revision 39353)
+++ b/source/common/locid.cpp (revision 39356)
@@ -43,4 +43,5 @@
#include "ucln_cmn.h"
#include "ustr_imp.h"
+#include "charstr.h"
U_CDECL_BEGIN
@@ -57,4 +58,10 @@
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
@@ -284,5 +291,5 @@
else
{
- MaybeStackArray<char, ULOC_FULLNAME_CAPACITY> togo;
+ UErrorCode status = U_ZERO_ERROR;
int32_t size = 0;
int32_t lsize = 0;
@@ -290,5 +297,4 @@
int32_t vsize = 0;
int32_t ksize = 0;
- char *p;
// Calculate the size of the resulting string.
@@ -298,6 +304,12 @@
{
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
@@ -305,4 +317,8 @@
{
csize = (int32_t)uprv_strlen(newCountry);
+ if ( csize < 0 || csize > ULOC_STRING_LIMIT ) { // int32 wrap
+ setToBogus();
+ return;
+ }
size += csize;
}
@@ -319,4 +335,8 @@
// 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) )
{
@@ -343,48 +363,34 @@
{
ksize = (int32_t)uprv_strlen(newKeywords);
+ if ( ksize < 0 || ksize > ULOC_STRING_LIMIT ) {
+ setToBogus();
+ return;
+ }
size += ksize + 1;
}
-
+ if (size < 0) {
+ setToBogus();
+ return;
+ }
// 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);
}
@@ -392,19 +398,23 @@
{
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: source/test/intltest/numfmtst.cpp
===================================================================
--- a/source/test/intltest/numfmtst.cpp (revision 39353)
+++ b/source/test/intltest/numfmtst.cpp (revision 39356)
@@ -2421,9 +2421,10 @@
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);

10
CVE-2017-14952.patch Normal file
View file

@ -0,0 +1,10 @@
Index: source/i18n/zonemeta.cpp
===================================================================
--- a/source/i18n/zonemeta.cpp (revision 40283)
+++ b/source/i18n/zonemeta.cpp (revision 40324)
@@ -682,5 +682,4 @@
if (U_FAILURE(status)) {
delete mzMappings;
- deleteOlsonToMetaMappingEntry(entry);
uprv_free(entry);
break;

118
CVE-2017-15422.patch Normal file
View file

@ -0,0 +1,118 @@
Index: icu4c/source/i18n/gregoimp.cpp
===================================================================
--- icu4c/source/i18n/gregoimp.cpp (revision 40653)
+++ icu4c/source/i18n/gregoimp.cpp (revision 40654)
@@ -29,6 +29,11 @@ int32_t ClockMath::floorDivide(int32_t n
numerator / denominator : ((numerator + 1) / denominator) - 1;
}
+int64_t ClockMath::floorDivide(int64_t numerator, int64_t denominator) {
+ return (numerator >= 0) ?
+ numerator / denominator : ((numerator + 1) / denominator) - 1;
+}
+
int32_t ClockMath::floorDivide(double numerator, int32_t denominator,
int32_t& remainder) {
double quotient;
Index: icu4c/source/i18n/gregoimp.h
===================================================================
--- icu4c/source/i18n/gregoimp.h (revision 40653)
+++ icu4c/source/i18n/gregoimp.h (revision 40654)
@@ -39,6 +39,17 @@ class ClockMath {
static int32_t floorDivide(int32_t numerator, int32_t denominator);
/**
+ * Divide two integers, returning the floor of the quotient.
+ * Unlike the built-in division, this is mathematically
+ * well-behaved. E.g., <code>-1/4</code> => 0 but
+ * <code>floorDivide(-1,4)</code> => -1.
+ * @param numerator the numerator
+ * @param denominator a divisor which must be != 0
+ * @return the floor of the quotient
+ */
+ static int64_t floorDivide(int64_t numerator, int64_t denominator);
+
+ /**
* Divide two numbers, returning the floor of the quotient.
* Unlike the built-in division, this is mathematically
* well-behaved. E.g., <code>-1/4</code> => 0 but
Index: icu4c/source/i18n/persncal.cpp
===================================================================
--- icu4c/source/i18n/persncal.cpp (revision 40653)
+++ icu4c/source/i18n/persncal.cpp (revision 40654)
@@ -211,7 +211,7 @@ void PersianCalendar::handleComputeField
int32_t year, month, dayOfMonth, dayOfYear;
int32_t daysSinceEpoch = julianDay - PERSIAN_EPOCH;
- year = 1 + ClockMath::floorDivide(33 * daysSinceEpoch + 3, 12053);
+ year = 1 + (int32_t)ClockMath::floorDivide(33 * (int64_t)daysSinceEpoch + 3, (int64_t)12053);
int32_t farvardin1 = 365 * (year - 1) + ClockMath::floorDivide(8 * year + 21, 33);
dayOfYear = (daysSinceEpoch - farvardin1); // 0-based
Index: icu4c/source/test/intltest/calregts.cpp
===================================================================
--- icu4c/source/test/intltest/calregts.cpp (revision 40653)
+++ icu4c/source/test/intltest/calregts.cpp (revision 40654)
@@ -10,6 +10,7 @@
#include "calregts.h"
+#include "unicode/calendar.h"
#include "unicode/gregocal.h"
#include "unicode/simpletz.h"
#include "unicode/smpdtfmt.h"
@@ -88,6 +89,7 @@ CalendarRegressionTest::runIndexedTest(
CASE(48,TestT8596);
CASE(49,Test9019);
CASE(50,TestT9452);
+ CASE(51,TestPersianCalOverflow);
default: name = ""; break;
}
}
@@ -2944,4 +2946,34 @@ void CalendarRegressionTest::TestT9452(v
}
}
+/**
+ * @bug ticket 13454
+ */
+void CalendarRegressionTest::TestPersianCalOverflow(void) {
+ const char* localeID = "bs_Cyrl@calendar=persian";
+ UErrorCode status = U_ZERO_ERROR;
+ Calendar* cal = Calendar::createInstance(Locale(localeID), status);
+ if(U_FAILURE(status)) {
+ dataerrln("FAIL: Calendar::createInstance for localeID %s: %s", localeID, u_errorName(status));
+ } else {
+ int32_t maxMonth = cal->getMaximum(UCAL_MONTH);
+ int32_t maxDayOfMonth = cal->getMaximum(UCAL_DATE);
+ int32_t jd, month, dayOfMonth;
+ for (jd = 67023580; jd <= 67023584; jd++) { // year 178171, int32_t overflow if jd >= 67023582
+ status = U_ZERO_ERROR;
+ cal->clear();
+ cal->set(UCAL_JULIAN_DAY, jd);
+ month = cal->get(UCAL_MONTH, status);
+ dayOfMonth = cal->get(UCAL_DATE, status);
+ if ( U_FAILURE(status) ) {
+ errln("FAIL: Calendar->get MONTH/DATE for localeID %s, julianDay %d, status %s\n", localeID, jd, u_errorName(status));
+ } else if (month > maxMonth || dayOfMonth > maxDayOfMonth) {
+ errln("FAIL: localeID %s, julianDay %d; maxMonth %d, got month %d; maxDayOfMonth %d, got dayOfMonth %d\n",
+ localeID, jd, maxMonth, month, maxDayOfMonth, dayOfMonth);
+ }
+ }
+ delete cal;
+ }
+}
+
#endif /* #if !UCONFIG_NO_FORMATTING */
Index: icu4c/source/test/intltest/calregts.h
===================================================================
--- icu4c/source/test/intltest/calregts.h (revision 40653)
+++ icu4c/source/test/intltest/calregts.h (revision 40654)
@@ -75,6 +75,7 @@ public:
void TestT8596(void);
void Test9019(void);
void TestT9452(void);
+ void TestPersianCalOverflow(void);
void printdate(GregorianCalendar *cal, const char *string);
void dowTest(UBool lenient) ;

View file

@ -0,0 +1,176 @@
Index: icu/source/test/intltest/utxttest.h
===================================================================
--- icu/source/test/intltest/utxttest.h (revision 39670)
+++ icu/source/test/intltest/utxttest.h (revision 39671)
@@ -36,6 +36,7 @@
void Ticket10562();
void Ticket10983();
void Ticket12130();
+ void Ticket12888();
private:
struct m { // Map between native indices & code points.
Index: icu/source/test/intltest/utxttest.cpp
===================================================================
--- icu/source/test/intltest/utxttest.cpp (revision 39670)
+++ icu/source/test/intltest/utxttest.cpp (revision 39671)
@@ -65,6 +65,8 @@
if (exec) Ticket10983(); break;
case 7: name = "Ticket12130";
if (exec) Ticket12130(); break;
+ case 8: name = "Ticket12888";
+ if (exec) Ticket12888(); break;
default: name = ""; break;
}
}
@@ -1581,3 +1583,63 @@
}
utext_close(&ut);
}
+
+// Ticket 12888: bad handling of illegal utf-8 containing many instances of the archaic, now illegal,
+// six byte utf-8 forms. Original implementation had an assumption that
+// there would be at most three utf-8 bytes per UTF-16 code unit.
+// The five and six byte sequences map to a single replacement character.
+
+void UTextTest::Ticket12888() {
+ const char *badString =
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80"
+ "\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80\xfd\x80\x80\x80\x80\x80";
+
+ UErrorCode status = U_ZERO_ERROR;
+ LocalUTextPointer ut(utext_openUTF8(NULL, badString, -1, &status));
+ TEST_SUCCESS(status);
+ for (;;) {
+ UChar32 c = utext_next32(ut.getAlias());
+ if (c == U_SENTINEL) {
+ break;
+ }
+ }
+ int32_t endIdx = utext_getNativeIndex(ut.getAlias());
+ if (endIdx != (int32_t)strlen(badString)) {
+ errln("%s:%d expected=%d, actual=%d", __FILE__, __LINE__, strlen(badString), endIdx);
+ return;
+ }
+
+ for (int32_t prevIndex = endIdx; prevIndex>0;) {
+ UChar32 c = utext_previous32(ut.getAlias());
+ int32_t currentIndex = utext_getNativeIndex(ut.getAlias());
+ if (c != 0xfffd) {
+ errln("%s:%d (expected, actual, index) = (%d, %d, %d)\n",
+ __FILE__, __LINE__, 0xfffd, c, currentIndex);
+ break;
+ }
+ if (currentIndex != prevIndex - 6) {
+ errln("%s:%d: wrong index. Expected, actual = %d, %d",
+ __FILE__, __LINE__, prevIndex - 6, currentIndex);
+ break;
+ }
+ prevIndex = currentIndex;
+ }
+}
Index: icu/source/common/utext.cpp
===================================================================
--- icu/source/common/utext.cpp (revision 39670)
+++ icu/source/common/utext.cpp (revision 39671)
@@ -845,9 +845,15 @@
//------------------------------------------------------------------------------
// Chunk size.
-// Must be less than 85, because of byte mapping from UChar indexes to native indexes.
-// Worst case is three native bytes to one UChar. (Supplemenaries are 4 native bytes
-// to two UChars.)
+// Must be less than 42 (256/6), because of byte mapping from UChar indexes to native indexes.
+// Worst case there are six UTF-8 bytes per UChar.
+// obsolete 6 byte form fd + 5 trails maps to fffd
+// obsolete 5 byte form fc + 4 trails maps to fffd
+// non-shortest 4 byte forms maps to fffd
+// normal supplementaries map to a pair of utf-16, two utf8 bytes per utf-16 unit
+// mapToUChars array size must allow for the worst case, 6.
+// This could be brought down to 4, by treating fd and fc as pure illegal,
+// rather than obsolete lead bytes. But that is not compatible with the utf-8 access macros.
//
enum { UTF8_TEXT_CHUNK_SIZE=32 };
@@ -887,7 +893,7 @@
// Requires two extra slots,
// one for a supplementary starting in the last normal position,
// and one for an entry for the buffer limit position.
- uint8_t mapToUChars[UTF8_TEXT_CHUNK_SIZE*3+6]; // Map native offset from bufNativeStart to
+ uint8_t mapToUChars[UTF8_TEXT_CHUNK_SIZE*6+6]; // Map native offset from bufNativeStart to
// correspoding offset in filled part of buf.
int32_t align;
};
@@ -1030,6 +1036,7 @@
// Requested index is in this buffer.
u8b = (UTF8Buf *)ut->p; // the current buffer
mapIndex = ix - u8b->toUCharsMapStart;
+ U_ASSERT(mapIndex < (int32_t)sizeof(UTF8Buf::mapToUChars));
ut->chunkOffset = u8b->mapToUChars[mapIndex] - u8b->bufStartIdx;
return TRUE;
@@ -1296,6 +1303,10 @@
// Can only do this if the incoming index is somewhere in the interior of the string.
// If index is at the end, there is no character there to look at.
if (ix != ut->b) {
+ // Note: this function will only move the index back if it is on a trail byte
+ // and there is a preceding lead byte and the sequence from the lead
+ // through this trail could be part of a valid UTF-8 sequence
+ // Otherwise the index remains unchanged.
U8_SET_CP_START(s8, 0, ix);
}
@@ -1309,7 +1320,10 @@
UChar *buf = u8b->buf;
uint8_t *mapToNative = u8b->mapToNative;
uint8_t *mapToUChars = u8b->mapToUChars;
- int32_t toUCharsMapStart = ix - (UTF8_TEXT_CHUNK_SIZE*3 + 1);
+ int32_t toUCharsMapStart = ix - sizeof(UTF8Buf::mapToUChars) + 1;
+ // Note that toUCharsMapStart can be negative. Happens when the remaining
+ // text from current position to the beginning is less than the buffer size.
+ // + 1 because mapToUChars must have a slot at the end for the bufNativeLimit entry.
int32_t destIx = UTF8_TEXT_CHUNK_SIZE+2; // Start in the overflow region
// at end of buffer to leave room
// for a surrogate pair at the
@@ -1336,6 +1350,7 @@
if (c<0x80) {
// Special case ASCII range for speed.
buf[destIx] = (UChar)c;
+ U_ASSERT(toUCharsMapStart <= srcIx);
mapToUChars[srcIx - toUCharsMapStart] = (uint8_t)destIx;
mapToNative[destIx] = (uint8_t)(srcIx - toUCharsMapStart);
} else {
@@ -1365,6 +1380,7 @@
do {
mapToUChars[sIx-- - toUCharsMapStart] = (uint8_t)destIx;
} while (sIx >= srcIx);
+ U_ASSERT(toUCharsMapStart <= (srcIx+1));
// Set native indexing limit to be the current position.
// We are processing a non-ascii, non-native-indexing char now;
@@ -1539,6 +1555,7 @@
U_ASSERT(index>=ut->chunkNativeStart+ut->nativeIndexingLimit);
U_ASSERT(index<=ut->chunkNativeLimit);
int32_t mapIndex = index - u8b->toUCharsMapStart;
+ U_ASSERT(mapIndex < (int32_t)sizeof(UTF8Buf::mapToUChars));
int32_t offset = u8b->mapToUChars[mapIndex] - u8b->bufStartIdx;
U_ASSERT(offset>=0 && offset<=ut->chunkLength);
return offset;

44
CVE-2020-10531.patch Normal file
View file

@ -0,0 +1,44 @@
From b7d08bc04a4296982fcef8b6b8a354a9e4e7afca Mon Sep 17 00:00:00 2001
From: Frank Tang <ftang@chromium.org>
Date: Sat, 1 Feb 2020 02:39:04 +0000
Subject: [PATCH] ICU-20958 Prevent SEGV_MAPERR in append
See #971
---
source/common/unistr.cpp | 6 ++-
diff --git a/source/common/unistr.cpp b/source/common/unistr.cpp
index 901bb3358ba..077b4d6ef20 100644
--- a/source/common/unistr.cpp
+++ b/source/common/unistr.cpp
@@ -73,6 +73,17 @@ print(const UChar *s,
// END DEBUGGING
#endif
+// Adding this function as support of CVE-2020-10531
+// since this version has not uprv_add32_overflow
+// implement it here.
+UBool uprv_add32_overflow(int32_t a, int32_t b, int32_t* res) {
+ int64_t a64 = static_cast<int64_t>(a);
+ int64_t b64 = static_cast<int64_t>(b);
+ int64_t res64 = a64 + b64;
+ *res = static_cast<int32_t>(res64);
+ return res64 != *res;
+}
+
// Local function definitions for now
// need to copy areas that may overlap
@@ -1510,7 +1510,11 @@ UnicodeString::doAppend(const UChar *src
}
int32_t oldLength = length();
- int32_t newLength = oldLength + srcLength;
+ int32_t newLength;
+ if (uprv_add32_overflow(oldLength, srcLength, &newLength)) {
+ setToBogus();
+ return *this;
+ }
// optimize append() onto a large-enough, owned string
if((newLength <= getCapacity() && isBufferWritable()) ||
cloneArrayIfNeeded(newLength, newLength + (newLength >> 2) + kGrowSize)) {

View file

@ -17,7 +17,7 @@ Summary: International Components for Unicode
Name: icu
Epoch: 1
Version: 57.1
Release: 8
Release: 9
License: MIT
Group: System/Libraries
Url: http://www.icu-project.org/index.html
@ -29,6 +29,14 @@ Patch1: icu-57.1-ICU-12936.patch
Patch10: icu.7601.Indic-ccmp.patch
Patch11: icu.8198.revert.icu5431.patch
Patch12: icu.8800.freeserif.crash.patch
# From Debian
Patch20: CVE-2016-6293.patch
Patch21: CVE-2016-7415.patch
Patch22: CVE-2017-7867_CVE-2017-7868.patch
Patch23: CVE-2017-14952.patch
Patch24: CVE-2017-15422.patch
Patch25: CVE-2020-10531.patch
BuildRequires: doxygen
%description
@ -213,6 +221,12 @@ Development files and headers for the International Components for Unicode.
%patch10 -p1
%patch11 -p2 -R
%patch12 -p1
%patch20 -p1
%patch21 -p1
%patch22 -p1
%patch23 -p1
%patch24 -p1
%patch25 -p1
mkdir -p docs
cd docs