Skip to content

Commit 4618865

Browse files
derekmaurocopybara-github
authored andcommitted
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
1 parent c14dfbf commit 4618865

File tree

1 file changed

+22
-4
lines changed

1 file changed

+22
-4
lines changed

absl/strings/escaping.cc

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ std::string CEscapeInternal(absl::string_view src, bool use_hex,
367367
}
368368

369369
/* clang-format off */
370-
constexpr unsigned char c_escaped_len[256] = {
370+
constexpr unsigned char kCEscapedLen[256] = {
371371
4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 2, 4, 4, // \t, \n, \r
372372
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
373373
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] = {
392392
// that UTF-8 bytes are not handled specially.
393393
inline size_t CEscapedLength(absl::string_view src) {
394394
size_t escaped_len = 0;
395-
for (char c : src)
396-
escaped_len += c_escaped_len[static_cast<unsigned char>(c)];
395+
// The maximum value of kCEscapedLen[x] is 4, so we can escape any string of
396+
// length size_t_max/4 without checking for overflow.
397+
size_t unchecked_limit =
398+
std::min<size_t>(src.size(), std::numeric_limits<size_t>::max() / 4);
399+
size_t i = 0;
400+
while (i < unchecked_limit) {
401+
// Common case: No need to check for overflow.
402+
escaped_len += kCEscapedLen[static_cast<unsigned char>(src[i++])];
403+
}
404+
while (i < src.size()) {
405+
// Beyond unchecked_limit we need to check for overflow before adding.
406+
size_t char_len = kCEscapedLen[static_cast<unsigned char>(src[i++])];
407+
ABSL_INTERNAL_CHECK(
408+
escaped_len <= std::numeric_limits<size_t>::max() - char_len,
409+
"escaped_len overflow");
410+
escaped_len += char_len;
411+
}
397412
return escaped_len;
398413
}
399414

@@ -406,12 +421,15 @@ void CEscapeAndAppendInternal(absl::string_view src,
406421
}
407422

408423
size_t cur_dest_len = dest->size();
424+
ABSL_INTERNAL_CHECK(
425+
cur_dest_len <= std::numeric_limits<size_t>::max() - escaped_len,
426+
"std::string size overflow");
409427
strings_internal::STLStringResizeUninitialized(dest,
410428
cur_dest_len + escaped_len);
411429
char* append_ptr = &(*dest)[cur_dest_len];
412430

413431
for (char c : src) {
414-
size_t char_len = c_escaped_len[static_cast<unsigned char>(c)];
432+
size_t char_len = kCEscapedLen[static_cast<unsigned char>(c)];
415433
if (char_len == 1) {
416434
*append_ptr++ = c;
417435
} else if (char_len == 2) {

0 commit comments

Comments
 (0)