summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Derek Mauro <dmauro@google.com>2024-02-14 09:54:47 -0800
committerGravatar Copybara-Service <copybara-worker@google.com>2024-02-14 09:56:30 -0800
commit4618865caf8596742a9fd7c28a70a46b5e277794 (patch)
tree948b23d4de133f5b891e43b30b546890cd40769c
parentc14dfbf9c1759c39bf4343b9e54a43975fbba930 (diff)
Prevent overflow in `absl::CEscape()`
Strings larger than 1 GiB on a platform with a 32-bit size_t could potentially overflow size_t in `CEscapedLength()`, resulting in an undersized allocation. The resulting write in `CEscapeAndAppendInternal()` would then write beyond the bounds of the output buffer. A second overflow, where the calculated escaped length is added to the size of the string being appended to, is also fixed. In both cases the program will now abort prior to the overflow. Credit goes to Ronald Crane (Zippenhop LLC) for reporting this issue. PiperOrigin-RevId: 607019573 Change-Id: I97bf246cde96102a793d2db49446cccae08abf59
-rw-r--r--absl/strings/escaping.cc26
1 files changed, 22 insertions, 4 deletions
diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc
index bb58f99b..af50faeb 100644
--- a/absl/strings/escaping.cc
+++ b/absl/strings/escaping.cc
@@ -367,7 +367,7 @@ std::string CEscapeInternal(absl::string_view src, bool use_hex,
}
/* clang-format off */
-constexpr unsigned char c_escaped_len[256] = {
+constexpr unsigned char kCEscapedLen[256] = {
4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 2, 4, 4, // \t, \n, \r
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // ", '
@@ -392,8 +392,23 @@ constexpr unsigned char c_escaped_len[256] = {
// that UTF-8 bytes are not handled specially.
inline size_t CEscapedLength(absl::string_view src) {
size_t escaped_len = 0;
- for (char c : src)
- escaped_len += c_escaped_len[static_cast<unsigned char>(c)];
+ // The maximum value of kCEscapedLen[x] is 4, so we can escape any string of
+ // length size_t_max/4 without checking for overflow.
+ size_t unchecked_limit =
+ std::min<size_t>(src.size(), std::numeric_limits<size_t>::max() / 4);
+ size_t i = 0;
+ while (i < unchecked_limit) {
+ // Common case: No need to check for overflow.
+ escaped_len += kCEscapedLen[static_cast<unsigned char>(src[i++])];
+ }
+ while (i < src.size()) {
+ // Beyond unchecked_limit we need to check for overflow before adding.
+ size_t char_len = kCEscapedLen[static_cast<unsigned char>(src[i++])];
+ ABSL_INTERNAL_CHECK(
+ escaped_len <= std::numeric_limits<size_t>::max() - char_len,
+ "escaped_len overflow");
+ escaped_len += char_len;
+ }
return escaped_len;
}
@@ -406,12 +421,15 @@ void CEscapeAndAppendInternal(absl::string_view src,
}
size_t cur_dest_len = dest->size();
+ ABSL_INTERNAL_CHECK(
+ cur_dest_len <= std::numeric_limits<size_t>::max() - escaped_len,
+ "std::string size overflow");
strings_internal::STLStringResizeUninitialized(dest,
cur_dest_len + escaped_len);
char* append_ptr = &(*dest)[cur_dest_len];
for (char c : src) {
- size_t char_len = c_escaped_len[static_cast<unsigned char>(c)];
+ size_t char_len = kCEscapedLen[static_cast<unsigned char>(c)];
if (char_len == 1) {
*append_ptr++ = c;
} else if (char_len == 2) {