File icu-CVE-2020-10531.patch of Package icu
diff -Nura icu/source/common/putil.cpp icu_new/source/common/putil.cpp
--- icu/source/common/putil.cpp 2013-10-05 04:49:20.000000000 +0800
+++ icu_new/source/common/putil.cpp 2020-04-14 17:45:09.237000000 +0800
@@ -535,6 +535,30 @@
return (x > y ? y : x);
}
+#include <iostream>
+
+U_CAPI UBool U_EXPORT2
+uprv_add32_overflow(int32_t a, int32_t b, int32_t* res) {
+ // NOTE: Some compilers (GCC, Clang) have primitives available, like __builtin_add_overflow.
+ // This function could be optimized by calling one of those primitives.
+ auto a64 = static_cast<int64_t>(a);
+ auto b64 = static_cast<int64_t>(b);
+ int64_t res64 = a64 + b64;
+ *res = static_cast<int32_t>(res64);
+ return res64 != *res;
+}
+
+U_CAPI UBool U_EXPORT2
+uprv_mul32_overflow(int32_t a, int32_t b, int32_t* res) {
+ // NOTE: Some compilers (GCC, Clang) have primitives available, like __builtin_mul_overflow.
+ // This function could be optimized by calling one of those primitives.
+ auto a64 = static_cast<int64_t>(a);
+ auto b64 = static_cast<int64_t>(b);
+ int64_t res64 = a64 * b64;
+ *res = static_cast<int32_t>(res64);
+ return res64 != *res;
+}
+
/**
* Truncates the given double.
* trunc(3.3) = 3.0, trunc (-3.3) = -3.0
diff -Nura icu/source/common/putilimp.h icu_new/source/common/putilimp.h
--- icu/source/common/putilimp.h 2013-10-05 04:49:30.000000000 +0800
+++ icu_new/source/common/putilimp.h 2020-04-14 22:01:40.868000000 +0800
@@ -392,6 +392,32 @@
*/
U_INTERNAL double U_EXPORT2 uprv_round(double x);
+/**
+ * Adds the signed integers a and b, storing the result in res.
+ * Checks for signed integer overflow.
+ * Similar to the GCC/Clang extension __builtin_add_overflow
+ *
+ * @param a The first operand.
+ * @param b The second operand.
+ * @param res a + b
+ * @return true if overflow occurred; false if no overflow occurred.
+ * @internal
+ */
+U_INTERNAL UBool U_EXPORT2 uprv_add32_overflow(int32_t a, int32_t b, int32_t* res);
+
+/**
+ * Multiplies the signed integers a and b, storing the result in res.
+ * Checks for signed integer overflow.
+ * Similar to the GCC/Clang extension __builtin_mul_overflow
+ *
+ * @param a The first multiplicand.
+ * @param b The second multiplicand.
+ * @param res a * b
+ * @return true if overflow occurred; false if no overflow occurred.
+ * @internal
+ */
+U_INTERNAL UBool U_EXPORT2 uprv_mul32_overflow(int32_t a, int32_t b, int32_t* res);
+
#if 0
/**
* Returns the number of digits after the decimal point in a double number x.
diff -Nura icu/source/common/unicode/unistr.h icu_new/source/common/unicode/unistr.h
--- icu/source/common/unicode/unistr.h 2013-10-05 04:49:10.000000000 +0800
+++ icu_new/source/common/unicode/unistr.h 2020-04-13 10:48:15.740000000 +0800
@@ -3359,6 +3359,9 @@
int32_t srcStart,
int32_t srcLength);
+ UnicodeString& doAppend(const UnicodeString& src, int32_t srcStart, int32_t srcLength);
+ UnicodeString& doAppend(const UChar *srcChars, int32_t srcStart, int32_t srcLength);
+
UnicodeString& doReverse(int32_t start,
int32_t length);
@@ -4337,30 +4340,30 @@
UnicodeString::append(const UnicodeString& srcText,
int32_t srcStart,
int32_t srcLength)
-{ return doReplace(length(), 0, srcText, srcStart, srcLength); }
+{ return doAppend(srcText, srcStart, srcLength); }
inline UnicodeString&
UnicodeString::append(const UnicodeString& srcText)
-{ return doReplace(length(), 0, srcText, 0, srcText.length()); }
+{ return doAppend(srcText, 0, srcText.length()); }
inline UnicodeString&
UnicodeString::append(const UChar *srcChars,
int32_t srcStart,
int32_t srcLength)
-{ return doReplace(length(), 0, srcChars, srcStart, srcLength); }
+{ return doAppend(srcChars, srcStart, srcLength); }
inline UnicodeString&
UnicodeString::append(const UChar *srcChars,
int32_t srcLength)
-{ return doReplace(length(), 0, srcChars, 0, srcLength); }
+{ return doAppend(srcChars, 0, srcLength); }
inline UnicodeString&
UnicodeString::append(UChar srcChar)
-{ return doReplace(length(), 0, &srcChar, 0, 1); }
+{ return doAppend(&srcChar, 0, 1); }
inline UnicodeString&
UnicodeString::operator+= (UChar ch)
-{ return doReplace(length(), 0, &ch, 0, 1); }
+{ return doAppend(&ch, 0, 1); }
inline UnicodeString&
UnicodeString::operator+= (UChar32 ch) {
@@ -4369,7 +4372,7 @@
inline UnicodeString&
UnicodeString::operator+= (const UnicodeString& srcText)
-{ return doReplace(length(), 0, srcText, 0, srcText.length()); }
+{ return doAppend(srcText, 0, srcText.length()); }
inline UnicodeString&
UnicodeString::insert(int32_t start,
diff -Nura icu/source/common/unistr.cpp icu_new/source/common/unistr.cpp
--- icu/source/common/unistr.cpp 2013-10-05 04:49:24.000000000 +0800
+++ icu_new/source/common/unistr.cpp 2020-04-13 17:38:16.052000000 +0800
@@ -216,7 +216,7 @@
: fShortLength(0),
fFlags(kShortString)
{
- doReplace(0, 0, text, 0, -1);
+ doAppend(text, 0, -1);
}
UnicodeString::UnicodeString(const UChar *text,
@@ -224,7 +224,7 @@
: fShortLength(0),
fFlags(kShortString)
{
- doReplace(0, 0, text, 0, textLength);
+ doAppend(text, 0, textLength);
}
UnicodeString::UnicodeString(UBool isTerminated,
@@ -1300,8 +1300,8 @@
UBool isError = FALSE;
U16_APPEND(buffer, _length, U16_MAX_LENGTH, srcChar, isError);
// We test isError so that the compiler does not complain that we don't.
- // If isError then _length==0 which turns the doReplace() into a no-op anyway.
- return isError ? *this : doReplace(length(), 0, buffer, 0, _length);
+ // If isError then _length==0 which turns the doAppend() into a no-op anyway.
+ return isError ? *this : doAppend(buffer, 0, _length);
}
UnicodeString&
@@ -1311,17 +1311,12 @@
int32_t srcStart,
int32_t srcLength)
{
- if(!src.isBogus()) {
- // pin the indices to legal values
- src.pinIndices(srcStart, srcLength);
-
- // get the characters from src
- // and replace the range in ourselves with them
- return doReplace(start, length, src.getArrayStart(), srcStart, srcLength);
- } else {
- // remove the range
- return doReplace(start, length, 0, 0, 0);
- }
+ // pin the indices to legal values
+ src.pinIndices(srcStart, srcLength);
+
+ // get the characters from src
+ // and replace the range in ourselves with them
+ return doReplace(start, length, src.getArrayStart(), srcStart, srcLength);
}
UnicodeString&
@@ -1357,6 +1352,10 @@
}
}
+ if(start == oldLength) {
+ return doAppend(srcChars, srcStart, srcLength);
+ }
+
if(srcChars == 0) {
srcStart = srcLength = 0;
} else if(srcLength < 0) {
@@ -1364,42 +1363,13 @@
srcLength = u_strlen(srcChars + srcStart);
}
- // calculate the size of the string after the replace
- int32_t newLength;
-
- // optimize append() onto a large-enough, owned string
- if(start >= oldLength) {
- if(srcLength == 0) {
- return *this;
- }
- newLength = oldLength + srcLength;
- if(newLength <= getCapacity() && isBufferWritable()) {
- UChar *oldArray = getArrayStart();
- // Do not copy characters when
- // UChar *buffer=str.getAppendBuffer(...);
- // is followed by
- // str.append(buffer, length);
- // or
- // str.appendString(buffer, length)
- // or similar.
- if(srcChars + srcStart != oldArray + start || start > oldLength) {
- us_arrayCopy(srcChars, srcStart, oldArray, oldLength, srcLength);
- }
- setLength(newLength);
- return *this;
- } else {
- // pin the indices to legal values
- start = oldLength;
- length = 0;
- }
- } else {
- // pin the indices to legal values
- pinIndices(start, length);
+ // pin the indices to legal values
+ pinIndices(start, length);
- newLength = oldLength - length + srcLength;
- }
+ // calculate the size of the string after the replace
+ int32_t newLength = oldLength - length + srcLength;
- // the following may change fArray but will not copy the current contents;
+ // cloneArrayIfNeeded(doCopyArray=FALSE) may change fArray but will not copy the current contents;
// therefore we need to keep the current fArray
UChar oldStackBuffer[US_STACKBUF_SIZE];
UChar *oldArray;
@@ -1450,6 +1420,73 @@
return *this;
}
+// Versions of doReplace() only for append() variants.
+// doReplace() and doAppend() optimize for different cases.
+
+UnicodeString&
+UnicodeString::doAppend(const UnicodeString& src, int32_t srcStart, int32_t srcLength) {
+ if(srcLength == 0) {
+ return *this;
+ }
+
+ // pin the indices to legal values
+ src.pinIndices(srcStart, srcLength);
+ return doAppend(src.getArrayStart(), srcStart, srcLength);
+}
+
+UnicodeString&
+UnicodeString::doAppend(const UChar *srcChars, int32_t srcStart, int32_t srcLength) {
+ if(!isWritable() || srcLength == 0 || srcChars == NULL) {
+ return *this;
+ }
+
+ if(srcLength < 0) {
+ // get the srcLength if necessary
+ if((srcLength = u_strlen(srcChars + srcStart)) == 0) {
+ return *this;
+ }
+ }
+
+ int32_t oldLength = length();
+ int32_t newLength;
+ if (uprv_add32_overflow(oldLength, srcLength, &newLength)) {
+ setToBogus();
+ return *this;
+ }
+
+ // Check for append onto ourself
+ const UChar* oldArray = getArrayStart();
+ if (isBufferWritable() &&
+ oldArray < srcChars + srcLength &&
+ srcChars < oldArray + oldLength) {
+ // Copy into a new UnicodeString and start over
+ UnicodeString copy(srcChars, srcLength);
+ if (copy.isBogus()) {
+ setToBogus();
+ return *this;
+ }
+ return doAppend(copy.getArrayStart(), 0, srcLength);
+ }
+
+ // optimize append() onto a large-enough, owned string
+ if((newLength <= getCapacity() && isBufferWritable()) ||
+ cloneArrayIfNeeded(newLength, newLength + (newLength >> 2) + kGrowSize)) {
+ UChar *newArray = getArrayStart();
+ // Do not copy characters when
+ // UChar *buffer=str.getAppendBuffer(...);
+ // is followed by
+ // str.append(buffer, length);
+ // or
+ // str.appendString(buffer, length)
+ // or similar.
+ if(srcChars + srcStart != newArray + oldLength) {
+ us_arrayCopy(srcChars, srcStart, newArray, oldLength, srcLength);
+ }
+ setLength(newLength);
+ }
+ return *this;
+}
+
/**
* Replaceable API
*/
@@ -1746,7 +1783,7 @@
UBool
UnicodeStringAppendable::appendCodeUnit(UChar c) {
- return str.doReplace(str.length(), 0, &c, 0, 1).isWritable();
+ return str.doAppend(&c, 0, 1).isWritable();
}
UBool
@@ -1755,12 +1792,12 @@
int32_t cLength = 0;
UBool isError = FALSE;
U16_APPEND(buffer, cLength, U16_MAX_LENGTH, c, isError);
- return !isError && str.doReplace(str.length(), 0, buffer, 0, cLength).isWritable();
+ return !isError && str.doAppend(buffer, 0, cLength).isWritable();
}
UBool
UnicodeStringAppendable::appendString(const UChar *s, int32_t length) {
- return str.doReplace(str.length(), 0, s, 0, length).isWritable();
+ return str.doAppend(s, 0, length).isWritable();
}
UBool
diff -Nura icu/source/test/intltest/ustrtest.cpp icu_new/source/test/intltest/ustrtest.cpp
--- icu/source/test/intltest/ustrtest.cpp 2013-10-05 04:47:58.000000000 +0800
+++ icu_new/source/test/intltest/ustrtest.cpp 2020-04-15 12:17:33.235000000 +0800
@@ -64,6 +64,7 @@
case 21: name = "TestUnicodeStringImplementsAppendable"; if (exec) TestUnicodeStringImplementsAppendable(); break;
case 22: name = "TestSizeofUnicodeString"; if (exec) TestSizeofUnicodeString(); break;
case 23: name = "TestStartsWithAndEndsWithNulTerminated"; if (exec) TestStartsWithAndEndsWithNulTerminated(); break;
+ case 24: name = "TestLargeAppend"; if (exec) TestLargeAppend(); break;
default: name = ""; break; //needed to end loop
}
@@ -2117,3 +2118,64 @@
errln("sizeof(UnicodeString)=%d, expected %d", (int)sizeofUniStr, (int)expected);
}
}
+
+void UnicodeStringTest::TestLargeAppend() {
+ if(quick) return;
+
+ IcuTestErrorCode status(*this, "TestLargeAppend");
+ // Make a large UnicodeString
+ int32_t len = 0xAFFFFFF;
+ UnicodeString str;
+ UChar *buf = str.getBuffer(len);
+ // A fast way to set buffer to valid Unicode.
+ // 4E4E is a valid unicode character
+ uprv_memset(buf, 0x4e, len * 2);
+ str.releaseBuffer(len);
+ UnicodeString dest;
+ // Append it 16 times
+ // 0xAFFFFFF times 16 is 0xA4FFFFF1,
+ // which is greater than INT32_MAX, which is 0x7FFFFFFF.
+ int64_t total = 0;
+ for (int32_t i = 0; i < 16; i++) {
+ dest.append(str);
+ total += len;
+ if (total <= INT32_MAX) {
+ assertFalse("dest is not bogus", dest.isBogus());
+ } else {
+ assertTrue("dest should be bogus", dest.isBogus());
+ }
+ }
+ dest.remove();
+ total = 0;
+ for (int32_t i = 0; i < 16; i++) {
+ dest.append(str);
+ total += len;
+ if (total + len <= INT32_MAX) {
+ assertFalse("dest is not bogus", dest.isBogus());
+ } else if (total <= INT32_MAX) {
+ // Check that a string of exactly the maximum size works
+ UnicodeString str2;
+ int32_t remain = INT32_MAX - total;
+ UChar *buf2 = str2.getBuffer(remain);
+ if (buf2 == nullptr) {
+ // if somehow memory allocation fail, return the test
+ return;
+ }
+ uprv_memset(buf2, 0x4e, remain * 2);
+ str2.releaseBuffer(remain);
+ dest.append(str2);
+ total += remain;
+ assertEquals("When a string of exactly the maximum size works", (int64_t)INT32_MAX, total);
+ assertEquals("When a string of exactly the maximum size works", INT32_MAX, dest.length());
+ assertFalse("dest is not bogus", dest.isBogus());
+
+ // Check that a string size+1 goes bogus
+ str2.truncate(1);
+ dest.append(str2);
+ total++;
+ assertTrue("dest should be bogus", dest.isBogus());
+ } else {
+ assertTrue("dest should be bogus", dest.isBogus());
+ }
+ }
+}
diff -Nura icu/source/test/intltest/ustrtest.h icu_new/source/test/intltest/ustrtest.h
--- icu/source/test/intltest/ustrtest.h 2013-10-05 04:47:54.000000000 +0800
+++ icu_new/source/test/intltest/ustrtest.h 2020-04-14 23:56:27.312000000 +0800
@@ -88,6 +88,7 @@
void TestAppendable();
void TestUnicodeStringImplementsAppendable();
void TestSizeofUnicodeString();
+ void TestLargeAppend();
};
class StringCaseTest: public IntlTest {