Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 28 additions & 25 deletions src/google/protobuf/text_format.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -1959,23 +1966,19 @@ TextFormat::Parser::Parser()
allow_singular_overwrites_(false),
recursion_limit_(kDefaultRecursionLimit) {}

namespace {

template <typename T>
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<int64_t>(input.size()),
" bytes", " > ", INT_MAX, " bytes."));
ReportErrorImpl(-1, 0,
absl::StrCat("Input size too large: ",
static_cast<int64_t>(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();
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down
3 changes: 3 additions & 0 deletions src/google/protobuf/text_format.h
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,9 @@ class PROTOBUF_EXPORT TextFormat {
// representations (see text_format.cc for implementation).
class ParserImpl;

template <typename T>
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,
Expand Down
59 changes: 59 additions & 0 deletions src/google/protobuf/text_format_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<int>::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<int>::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;
Expand Down
Loading