File icu-CVE-2020-10531.patch of Package icu.30242

diff -Nura icu/source/common/putil.cpp icu_new/source/common/putil.cpp
--- icu/source/common/putil.cpp	2017-11-01 03:04:49.000000000 +0800
+++ icu_new/source/common/putil.cpp	2020-03-29 23:03:31.071000000 +0800
@@ -533,6 +533,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	2017-10-12 00:24:35.000000000 +0800
+++ icu_new/source/common/putilimp.h	2020-03-29 23:05:37.853000000 +0800
@@ -391,6 +391,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/unistr.cpp icu_new/source/common/unistr.cpp
--- icu/source/common/unistr.cpp	2017-05-26 03:03:13.000000000 +0800
+++ icu_new/source/common/unistr.cpp	2020-03-27 17:58:18.374000000 +0800
@@ -1544,7 +1544,12 @@
   }
 
   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, getGrowCapacity(newLength))) {
diff -Nura icu/source/i18n/number_decimalquantity.cpp icu_new/source/i18n/number_decimalquantity.cpp
--- icu/source/i18n/number_decimalquantity.cpp	2017-10-26 02:41:15.000000000 +0800
+++ icu_new/source/i18n/number_decimalquantity.cpp	2020-03-30 00:01:12.501000000 +0800
@@ -187,11 +187,14 @@
     return scale + precision - 1;
 }
 
-void DecimalQuantity::adjustMagnitude(int32_t delta) {
+bool DecimalQuantity::adjustMagnitude(int32_t delta) {
     if (precision != 0) {
-        scale += delta;
-        origDelta += delta;
+        // i.e., scale += delta; origDelta += delta
+        bool overflow = uprv_add32_overflow(scale, delta, &scale);
+        overflow = uprv_add32_overflow(origDelta, delta, &origDelta) || overflow;
+        return overflow;
     }
+    return false;
 }
 
 StandardPlural::Form DecimalQuantity::getStandardPlural(const PluralRules *rules) const {
diff -Nura icu/source/i18n/number_decimalquantity.h icu_new/source/i18n/number_decimalquantity.h
--- icu/source/i18n/number_decimalquantity.h	2017-10-12 00:24:33.000000000 +0800
+++ icu_new/source/i18n/number_decimalquantity.h	2020-03-29 23:45:09.313000000 +0800
@@ -101,7 +101,7 @@
      *
      * @param delta The number of magnitudes of ten to change by.
      */
-    void adjustMagnitude(int32_t delta);
+    bool adjustMagnitude(int32_t delta);
 
     /**
      * @return The power of ten corresponding to the most significant nonzero digit.
diff -Nura icu/source/test/cintltst/putiltst.c icu_new/source/test/cintltst/putiltst.c
--- icu/source/test/cintltst/putiltst.c	2017-02-01 03:51:24.000000000 +0800
+++ icu_new/source/test/cintltst/putiltst.c	2020-03-29 23:49:57.308000000 +0800
@@ -128,6 +128,20 @@
         log_err("ERROR: uprv_isInfinite failed.\n");
     }
 
+    log_verbose("Testing the APIs uprv_add32_overflow and uprv_mul32_overflow\n");
+    int32_t overflow_result;
+    doAssert(FALSE, uprv_add32_overflow(INT32_MAX - 2, 1, &overflow_result), "should not overflow");
+    doAssert(INT32_MAX - 1, overflow_result, "should equal INT32_MAX - 1");
+    doAssert(FALSE, uprv_add32_overflow(INT32_MAX - 2, 2, &overflow_result), "should not overflow");
+    doAssert(INT32_MAX, overflow_result, "should equal exactly INT32_MAX");
+    doAssert(TRUE, uprv_add32_overflow(INT32_MAX - 2, 3, &overflow_result), "should overflow");
+    doAssert(FALSE, uprv_mul32_overflow(INT32_MAX / 5, 4, &overflow_result), "should not overflow");
+    doAssert(INT32_MAX / 5 * 4, overflow_result, "should equal INT32_MAX / 5 * 4");
+    doAssert(TRUE, uprv_mul32_overflow(INT32_MAX / 5, 6, &overflow_result), "should overflow");
+    // Test on negative numbers:
+    doAssert(FALSE, uprv_add32_overflow(-3, -2, &overflow_result), "should not overflow");
+    doAssert(-5, overflow_result, "should equal -5");
+
 #if 0
     log_verbose("Testing the API uprv_digitsAfterDecimal()....\n");
     doAssert(uprv_digitsAfterDecimal(value1), 3, "uprv_digitsAfterDecimal() failed.");
diff -Nura icu/source/test/intltest/ustrtest.cpp icu_new/source/test/intltest/ustrtest.cpp
--- icu/source/test/intltest/ustrtest.cpp	2017-10-12 00:24:32.000000000 +0800
+++ icu_new/source/test/intltest/ustrtest.cpp	2020-03-27 18:03:34.407000000 +0800
@@ -64,6 +64,7 @@
     TESTCASE_AUTO(TestUInt16Pointers);
     TESTCASE_AUTO(TestWCharPointers);
     TESTCASE_AUTO(TestNullPointers);
+    TESTCASE_AUTO(TestLargeAppend);
     TESTCASE_AUTO_END;
 }
 
@@ -2248,3 +2249,65 @@
     UnicodeString(u"def").extract(nullptr, 0, errorCode);
     assertEquals("buffer overflow extracting to nullptr", U_BUFFER_OVERFLOW_ERROR, errorCode);
 }
+
+
+void UnicodeStringTest::TestLargeAppend() {
+    if(quick) return;
+
+    IcuTestErrorCode status(*this, "TestLargeAppend");
+    // Make a large UnicodeString
+    int32_t len = 0xAFFFFFF;
+    UnicodeString str;
+    char16_t *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;
+            char16_t *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	2017-03-15 05:02:21.000000000 +0800
+++ icu_new/source/test/intltest/ustrtest.h	2020-03-27 18:04:05.779000000 +0800
@@ -96,6 +96,7 @@
     void TestUInt16Pointers();
     void TestWCharPointers();
     void TestNullPointers();
+    void TestLargeAppend();
 };
 
 #endif
openSUSE Build Service is sponsored by