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 {
