Change SkipRestOfBlock to be iterative instead of recursive to avoid stack

overflows on invalid input with many unbalanced `{` characters.

PiperOrigin-RevId: 562806350
pull/13857/head
Protobuf Team Bot 1 year ago committed by Copybara-Service
parent 91114633bb
commit f75a800be0
  1. 6
      src/google/protobuf/compiler/parser.cc
  2. 18
      src/google/protobuf/compiler/parser_unittest.cc

@ -38,6 +38,7 @@
#include <float.h> #include <float.h>
#include <cstddef>
#include <cstdint> #include <cstdint>
#include <limits> #include <limits>
#include <string> #include <string>
@ -555,14 +556,15 @@ void Parser::SkipStatement() {
} }
void Parser::SkipRestOfBlock() { void Parser::SkipRestOfBlock() {
size_t block_count = 1;
while (true) { while (true) {
if (AtEnd()) { if (AtEnd()) {
return; return;
} else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) { } else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) {
if (TryConsumeEndOfDeclaration("}", nullptr)) { if (TryConsumeEndOfDeclaration("}", nullptr)) {
return; if (--block_count == 0) break;
} else if (TryConsume("{")) { } else if (TryConsume("{")) {
SkipRestOfBlock(); ++block_count;
} }
} }
input_->Next(); input_->Next();

@ -117,8 +117,9 @@ class ParserTest : public testing::Test {
ParserTest() : require_syntax_identifier_(false) {} ParserTest() : require_syntax_identifier_(false) {}
// Set up the parser to parse the given text. // Set up the parser to parse the given text.
void SetupParser(const char* text) { void SetupParser(absl::string_view text) {
raw_input_ = absl::make_unique<io::ArrayInputStream>(text, strlen(text)); raw_input_ =
absl::make_unique<io::ArrayInputStream>(text.data(), text.size());
input_ = input_ =
absl::make_unique<io::Tokenizer>(raw_input_.get(), &error_collector_); absl::make_unique<io::Tokenizer>(raw_input_.get(), &error_collector_);
parser_ = absl::make_unique<Parser>(); parser_ = absl::make_unique<Parser>();
@ -169,7 +170,8 @@ class ParserTest : public testing::Test {
// Same as above but does not expect that the parser parses the complete // Same as above but does not expect that the parser parses the complete
// input. // input.
void ExpectHasEarlyExitErrors(const char* text, const char* expected_errors) { void ExpectHasEarlyExitErrors(absl::string_view text,
absl::string_view expected_errors) {
SetupParser(text); SetupParser(text);
SourceLocationTable source_locations; SourceLocationTable source_locations;
parser_->RecordSourceLocationsTo(&source_locations); parser_->RecordSourceLocationsTo(&source_locations);
@ -286,6 +288,16 @@ TEST_F(ParserTest, WarnIfFieldNameContainsNumberImmediatelyFollowUnderscore) {
"song_name_1.") != std::string::npos); "song_name_1.") != std::string::npos);
} }
TEST_F(ParserTest, RegressionNestedOpenBraceDoNotStackOverflow) {
std::string input("edition=\"a\000;", 12);
input += std::string(100000, '{');
ExpectHasEarlyExitErrors(
input,
"0:10: Unexpected end of string.\n"
"0:10: Invalid control characters encountered in text.\n"
"0:12: Expected top-level statement (e.g. \"message\").\n");
}
// =================================================================== // ===================================================================
typedef ParserTest ParseMessageTest; typedef ParserTest ParseMessageTest;

Loading…
Cancel
Save