Prevent size_t overflow in StrAppend and AppendPieces on 32-bit platforms

PiperOrigin-RevId: 946021594
Change-Id: Ib377ddc3d9aaf13ec1d7f73ad063e8e4e1e0d842
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index 546b8ae..b79078d 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -26,6 +26,8 @@
 #include "absl/base/config.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/base/nullability.h"
+#include "absl/base/optimization.h"
+#include "absl/base/throw_delegate.h"
 #include "absl/strings/internal/append_and_overwrite.h"
 #include "absl/strings/resize_and_overwrite.h"
 #include "absl/strings/string_view.h"
@@ -53,6 +55,26 @@
   return after;
 }
 
+// Safely adds size_t values, throwing std::length_error if overflow occurs.
+inline size_t SafeAdd(size_t a, size_t b) {
+  const uint64_t sum = static_cast<uint64_t>(a) + b;
+  if (ABSL_PREDICT_FALSE(sum > (std::numeric_limits<size_t>::max)())) {
+    ThrowStdLengthError("absl string append length overflow");
+  }
+  return static_cast<size_t>(sum);
+}
+
+inline size_t SafeAdd(std::initializer_list<size_t> sizes) {
+  uint64_t sum = 0;
+  for (size_t size : sizes) {
+    sum += size;
+  }
+  if (ABSL_PREDICT_FALSE(sum > (std::numeric_limits<size_t>::max)())) {
+    ThrowStdLengthError("absl string append length overflow");
+  }
+  return static_cast<size_t>(sum);
+}
+
 }  // namespace
 
 std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
@@ -163,7 +185,7 @@
   size_t to_append = 0;
   for (absl::string_view piece : pieces) {
     ASSERT_NO_OVERLAP(*dest, piece);
-    to_append += piece.size();
+    to_append = SafeAdd(to_append, piece.size());
   }
   StringAppendAndOverwrite(*dest, to_append,
                            [&pieces](char* const buf, size_t buf_size) {
@@ -198,7 +220,8 @@
   ASSERT_NO_OVERLAP(*dest, a);
   ASSERT_NO_OVERLAP(*dest, b);
   strings_internal::StringAppendAndOverwrite(
-      *dest, a.size() + b.size(), [&a, &b](char* const buf, size_t buf_size) {
+      *dest, SafeAdd(a.size(), b.size()),
+      [&a, &b](char* const buf, size_t buf_size) {
         char* out = buf;
         out = Append(out, a);
         out = Append(out, b);
@@ -213,7 +236,7 @@
   ASSERT_NO_OVERLAP(*dest, b);
   ASSERT_NO_OVERLAP(*dest, c);
   strings_internal::StringAppendAndOverwrite(
-      *dest, a.size() + b.size() + c.size(),
+      *dest, SafeAdd({a.size(), b.size(), c.size()}),
       [&a, &b, &c](char* const buf, size_t buf_size) {
         char* out = buf;
         out = Append(out, a);
@@ -231,7 +254,7 @@
   ASSERT_NO_OVERLAP(*dest, c);
   ASSERT_NO_OVERLAP(*dest, d);
   strings_internal::StringAppendAndOverwrite(
-      *dest, a.size() + b.size() + c.size() + d.size(),
+      *dest, SafeAdd({a.size(), b.size(), c.size(), d.size()}),
       [&a, &b, &c, &d](char* const buf, size_t buf_size) {
         char* out = buf;
         out = Append(out, a);