aboutsummaryrefslogtreecommitdiffstats
path: root/main/icu/CVE-2020-10531.patch
blob: 3daeecd4abfd114d7f13afb01b9ce26d965e9fba (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
Backported of:

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
diff --git a/source/common/unistr.cpp b/source/common/unistr.cpp
index 1bfb71a..e8a7f12 100644
--- a/common/unistr.cpp
+++ b/common/unistr.cpp
@@ -75,6 +75,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) {
+  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;
+}
+
 // Local function definitions for now
 
 // need to copy areas that may overlap
@@ -1546,7 +1557,11 @@ UnicodeString::doAppend(const UChar *srcChars, int32_t srcStart, int32_t srcLeng
   }
 
   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 --git a/source/test/intltest/ustrtest.cpp b/source/test/intltest/ustrtest.cpp
index b361e20..9b613d3 100644
--- a/test/intltest/ustrtest.cpp
+++ b/test/intltest/ustrtest.cpp
@@ -64,6 +64,7 @@ void UnicodeStringTest::runIndexedTest( int32_t index, UBool exec, const char* &
     TESTCASE_AUTO(TestUInt16Pointers);
     TESTCASE_AUTO(TestWCharPointers);
     TESTCASE_AUTO(TestNullPointers);
+    TESTCASE_AUTO(TestLargeAppend);
     TESTCASE_AUTO_END;
 }
 
@@ -2253,3 +2254,64 @@ UnicodeStringTest::TestNullPointers() {
     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 --git a/source/test/intltest/ustrtest.h b/source/test/intltest/ustrtest.h
index 4ba348c..d2d5ee1 100644
--- a/test/intltest/ustrtest.h
+++ b/test/intltest/ustrtest.h
@@ -96,6 +96,7 @@ public:
     void TestUInt16Pointers();
     void TestWCharPointers();
     void TestNullPointers();
+    void TestLargeAppend();
 };
 
 #endif