Skip to content

Commit 1054bbf

Browse files
Abseil Teamcopybara-github
authored andcommitted
Fix missing NUL-termination in Demangle() on parse failure and make ParseAbiTags() restore pre-parse state correctly
PiperOrigin-RevId: 947849322 Change-Id: I899ab7a5fa16f478fdfa7765a36418224208f1d9
1 parent de3b7c9 commit 1054bbf

2 files changed

Lines changed: 42 additions & 16 deletions

File tree

absl/debugging/internal/demangle.cc

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "absl/debugging/internal/demangle.h"
1919

20+
#include <algorithm>
2021
#include <cstddef>
2122
#include <cstdint>
2223
#include <cstdio>
@@ -456,22 +457,35 @@ static bool ZeroOrMore(ParseFunc parse_func, State *state) {
456457
}
457458

458459
// Append "str" at "out_cur_idx". If there is an overflow, out_cur_idx is
459-
// set to out_end_idx+1. The output string is ensured to
460-
// always terminate with '\0' as long as there is no overflow.
460+
// set to out_end_idx+1. The output buffer is always terminated with '\0' if it
461+
// has nonzero length.
461462
static void Append(State *state, const char *const str, const size_t length) {
462-
for (size_t i = 0; i < length; ++i) {
463-
if (state->parse_state.out_cur_idx + 1 <
464-
state->out_end_idx) { // +1 for '\0'
465-
state->out[state->parse_state.out_cur_idx++] = str[i];
466-
} else {
467-
// signal overflow
468-
state->parse_state.out_cur_idx = state->out_end_idx + 1;
469-
break;
470-
}
463+
if (length == 0) {
464+
return;
465+
}
466+
467+
// Figure out how much space is remaining in the output buffer to copy into.
468+
const int cap = state->out_end_idx - state->parse_state.out_cur_idx;
469+
470+
// If overflow was already signaled (negative value, set further below) or
471+
// there is zero space to write into, we cannot do anything.
472+
if (cap <= 0) {
473+
return;
471474
}
472-
if (state->parse_state.out_cur_idx < state->out_end_idx) {
473-
state->out[state->parse_state.out_cur_idx] =
474-
'\0'; // Terminate it with '\0'
475+
476+
// Copy the number of characters requested, capped by the amount of space
477+
// remaining.
478+
std::char_traits<char>::copy(state->out + state->parse_state.out_cur_idx, str,
479+
(std::min)(length, static_cast<size_t>(cap)));
480+
481+
// Did we copy everything we needed to, with enough room to NUL-terminate?
482+
if (length < static_cast<size_t>(cap)) {
483+
state->parse_state.out_cur_idx += static_cast<int>(length);
484+
state->out[state->parse_state.out_cur_idx] = '\0';
485+
} else {
486+
// No, we ran out of space. Signal overflow, and NUL-terminate for safety.
487+
state->parse_state.out_cur_idx = state->out_end_idx + 1;
488+
state->out[state->out_end_idx - 1] = '\0';
475489
}
476490
}
477491

@@ -893,8 +907,11 @@ static bool ParseAbiTags(State *state) {
893907
ComplexityGuard guard(state);
894908
if (guard.IsTooComplex()) return false;
895909

896-
while (ParseOneCharToken(state, 'B')) {
897-
ParseState copy = state->parse_state;
910+
for (;;) {
911+
const ParseState copy = state->parse_state;
912+
if (!ParseOneCharToken(state, 'B')) {
913+
break;
914+
}
898915
MaybeAppend(state, "[abi:");
899916

900917
if (!ParseSourceName(state)) {

absl/debugging/internal/demangle_test.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "absl/debugging/internal/demangle.h"
1616

17+
#include <array>
1718
#include <cstdlib>
1819
#include <memory>
1920
#include <string>
@@ -30,6 +31,7 @@ ABSL_NAMESPACE_BEGIN
3031
namespace debugging_internal {
3132
namespace {
3233

34+
using ::testing::Contains;
3335
using ::testing::ContainsRegex;
3436

3537
TEST(Demangle, FunctionTemplate) {
@@ -1908,6 +1910,13 @@ TEST(Demangle, DelegatesToDemangleRustSymbolEncoding) {
19081910
EXPECT_STREQ("my_crate::my_func", tmp);
19091911
}
19101912

1913+
TEST(Demangle, DemanglingNulTerminatesOnParsingFailure) {
1914+
std::array buf = {'\xAA', '\xAA', '\xAA', '\xAA'};
1915+
EXPECT_FALSE(Demangle("_ZN1xBE", std::data(buf), std::size(buf)));
1916+
// Ensure string is properly NUL-terminated despite parsing failure.
1917+
EXPECT_THAT(buf, Contains('\0'));
1918+
}
1919+
19111920
// Tests that verify that Demangle footprint is within some limit.
19121921
// They are not to be run under sanitizers as the sanitizers increase
19131922
// stack consumption by about 4x.

0 commit comments

Comments
 (0)