diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc index 8d2a031329220..e43c1a9fb5262 100644 --- a/src/google/protobuf/text_format.cc +++ b/src/google/protobuf/text_format.cc @@ -337,6 +337,24 @@ const Descriptor* DefaultFinderFindAnyType(const Message& message, const std::string& name) { return message.GetDescriptor()->file()->pool()->FindMessageTypeByName(name); } + +void ReportErrorImpl(int line, int col, absl::string_view message, + const Descriptor* root_message_type, + io::ErrorCollector* error_collector) { + if (error_collector == nullptr) { + if (line >= 0) { + ABSL_LOG(ERROR) << "Error parsing text-format " + << root_message_type->full_name() << ": " << (line + 1) + << ":" << (col + 1) << ": " << message; + } else { + ABSL_LOG(ERROR) << "Error parsing text-format " + << root_message_type->full_name() << ": " << message; + } + } else { + error_collector->RecordError(line, col, message); + } +} + } // namespace auto TextFormat::Parser::UnsetFieldsMetadata::GetUnsetFieldId( @@ -454,18 +472,7 @@ class TextFormat::Parser::ParserImpl { void ReportError(int line, int col, absl::string_view message) { had_errors_ = true; - if (error_collector_ == nullptr) { - if (line >= 0) { - ABSL_LOG(ERROR) << "Error parsing text-format " - << root_message_type_->full_name() << ": " << (line + 1) - << ":" << (col + 1) << ": " << message; - } else { - ABSL_LOG(ERROR) << "Error parsing text-format " - << root_message_type_->full_name() << ": " << message; - } - } else { - error_collector_->RecordError(line, col, message); - } + ReportErrorImpl(line, col, message, root_message_type_, error_collector_); } void ReportWarning(int line, int col, const absl::string_view message) { @@ -1959,23 +1966,19 @@ TextFormat::Parser::Parser() allow_singular_overwrites_(false), recursion_limit_(kDefaultRecursionLimit) {} -namespace { - template -bool CheckParseInputSize(T& input, io::ErrorCollector* error_collector) { +bool TextFormat::Parser::CheckParseInputSize(T& input, Message* output) const { if (input.size() > INT_MAX) { - error_collector->RecordError( - -1, 0, - absl::StrCat( - "Input size too large: ", static_cast(input.size()), - " bytes", " > ", INT_MAX, " bytes.")); + ReportErrorImpl(-1, 0, + absl::StrCat("Input size too large: ", + static_cast(input.size()), " bytes", + " > ", INT_MAX, " bytes."), + output->GetDescriptor(), error_collector_); return false; } return true; } -} // namespace - bool TextFormat::Parser::Parse(io::ZeroCopyInputStream* input, Message* output) { output->Clear(); @@ -1995,14 +1998,14 @@ bool TextFormat::Parser::Parse(io::ZeroCopyInputStream* input, bool TextFormat::Parser::ParseFromString(absl::string_view input, Message* output) { - DO(CheckParseInputSize(input, error_collector_)); + DO(CheckParseInputSize(input, output)); io::ArrayInputStream input_stream(input.data(), input.size()); return Parse(&input_stream, output); } bool TextFormat::Parser::ParseFromCord(const absl::Cord& input, Message* output) { - DO(CheckParseInputSize(input, error_collector_)); + DO(CheckParseInputSize(input, output)); io::CordInputStream input_stream(&input); return Parse(&input_stream, output); } @@ -2020,7 +2023,7 @@ bool TextFormat::Parser::Merge(io::ZeroCopyInputStream* input, bool TextFormat::Parser::MergeFromString(absl::string_view input, Message* output) { - DO(CheckParseInputSize(input, error_collector_)); + DO(CheckParseInputSize(input, output)); io::ArrayInputStream input_stream(input.data(), input.size()); return Merge(&input_stream, output); } diff --git a/src/google/protobuf/text_format.h b/src/google/protobuf/text_format.h index b940b247ab2a7..ed9dbba6dc696 100644 --- a/src/google/protobuf/text_format.h +++ b/src/google/protobuf/text_format.h @@ -863,6 +863,9 @@ class PROTOBUF_EXPORT TextFormat { // representations (see text_format.cc for implementation). class ParserImpl; + template + bool CheckParseInputSize(T& input, Message* output) const; + // Like TextFormat::Merge(). The provided implementation is used // to do the parsing. bool MergeUsingImpl(io::ZeroCopyInputStream* input, Message* output, diff --git a/src/google/protobuf/text_format_unittest.cc b/src/google/protobuf/text_format_unittest.cc index 7fe18eb904e55..109257a2dc66f 100644 --- a/src/google/protobuf/text_format_unittest.cc +++ b/src/google/protobuf/text_format_unittest.cc @@ -82,6 +82,7 @@ namespace text_format_unittest { using ::absl_testing::IsOk; using ::absl_testing::StatusIs; using ::google::protobuf::internal::UnsetFieldsMetadataTextFormatTestUtil; +using ::testing::_; using ::testing::AllOf; using ::testing::HasSubstr; using ::testing::UnorderedElementsAre; @@ -295,6 +296,64 @@ TEST_F(TextFormatTest, ShortFormat) { value_replacement, kTextMarkerRegex))); } +TEST_F(TextFormatTest, InputTooLarge) { + if (sizeof(size_t) <= sizeof(int)) { + GTEST_SKIP() << "Not supported in platform."; + } + unittest::TestAllTypes msg; + + constexpr absl::string_view kError = "Input size too large"; + + const auto expect_error = [&](auto f) { + { + // First without a collector. + TextFormat::Parser parser; + absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected); + EXPECT_CALL(log, Log(absl::LogSeverity::kError, _, HasSubstr(kError))) + .Times(1); + log.StartCapturingLogs(); + EXPECT_FALSE(f(parser)); + } + + // Then with a collector. + TextFormat::Parser parser; + class MockErrorCollector : public io::ErrorCollector { + public: + MockErrorCollector() = default; + ~MockErrorCollector() override = default; + + std::string text_; + + // implements ErrorCollector ------------------------------------- + void RecordError(int line, int column, + absl::string_view message) override { + text_ = absl::StrCat(message); + } + + void RecordWarning(int line, int column, + absl::string_view message) override {} + }; + + MockErrorCollector error_collector; + parser.RecordErrorsTo(&error_collector); + EXPECT_FALSE(f(parser)); + EXPECT_THAT(error_collector.text_, HasSubstr(kError)); + }; + + // Use a fake string_view with very large size. + // The contents don't matter because it should fail just by the size. + const absl::string_view too_large_sv( + "asdf", size_t{std::numeric_limits::max()} + 1); + expect_error([&](auto& p) { return p.ParseFromString(too_large_sv, &msg); }); + expect_error([&](auto& p) { return p.MergeFromString(too_large_sv, &msg); }); + + absl::Cord too_large_cord("sdaf"); + while (too_large_cord.size() < std::numeric_limits::max()) { + too_large_cord.Append(too_large_cord); + } + expect_error([&](auto& p) { return p.ParseFromCord(too_large_cord, &msg); }); +} + TEST_F(TextFormatTest, Utf8Format) { unittest::RedactedFields proto; unittest::TestNestedMessageRedaction redacted_nested_proto;