Skip to content

Commit 2ac862f

Browse files
habermancopybara-github
authored andcommitted
Fixed a missing check in wire format verification.
Also added some extra DCHECKs to more easily catch issues like this in the future. PiperOrigin-RevId: 688347347
1 parent f92335b commit 2ac862f

File tree

5 files changed

+77
-30
lines changed

5 files changed

+77
-30
lines changed

src/google/protobuf/compiler/cpp/file_unittest.cc

+12-19
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77

88
#include "google/protobuf/compiler/cpp/file.h"
99

10-
#include <algorithm>
11-
#include <cstddef>
10+
#include <string>
1211
#include <vector>
1312

13+
#include <gmock/gmock.h>
1414
#include <gtest/gtest.h>
15-
#include "absl/strings/match.h"
1615
#include "absl/strings/string_view.h"
16+
#include "absl/strings/strip.h"
1717
#include "google/protobuf/descriptor.h"
1818
#include "google/protobuf/unittest.pb.h"
1919

@@ -94,6 +94,7 @@ TEST(FileTest, TopologicallyOrderedDescriptors) {
9494
"TestExtensionInsideTable",
9595
"TestEmptyMessageWithExtensions",
9696
"TestEmptyMessage",
97+
"TestEagerlyVerifiedLazyMessage.LazyMessage",
9798
"TestDynamicExtensions.DynamicMessageType",
9899
"TestDupFieldNumber.Foo",
99100
"TestDupFieldNumber.Bar",
@@ -149,6 +150,7 @@ TEST(FileTest, TopologicallyOrderedDescriptors) {
149150
"TestGroup",
150151
"TestForeignNested",
151152
"TestFieldOrderings",
153+
"TestEagerlyVerifiedLazyMessage",
152154
"TestEagerMaybeLazy.NestedMessage",
153155
"TestDynamicExtensions",
154156
"TestDupFieldNumber",
@@ -196,23 +198,14 @@ TEST(FileTest, TopologicallyOrderedDescriptors) {
196198
"TestLazyMessageRepeated",
197199
"TestNestedRequiredForeign",
198200
};
199-
static constexpr size_t kExpectedDescriptorCount =
200-
std::end(kExpectedDescriptorOrder) - std::begin(kExpectedDescriptorOrder);
201-
std::vector<const Descriptor*> actual_descriptor_order =
202-
FileGeneratorFriendForTesting::MessagesInTopologicalOrder(fgen);
203-
EXPECT_TRUE(kExpectedDescriptorCount == actual_descriptor_order.size())
204-
<< "Expected: " << kExpectedDescriptorCount
205-
<< ", got: " << actual_descriptor_order.size();
206-
207-
auto limit =
208-
std::min(kExpectedDescriptorCount, actual_descriptor_order.size());
209-
for (auto i = 0u; i < limit; ++i) {
210-
const Descriptor* desc = actual_descriptor_order[i];
211-
bool match = absl::EndsWith(desc->full_name(), kExpectedDescriptorOrder[i]);
212-
EXPECT_TRUE(match) << "failed to match; expected "
213-
<< kExpectedDescriptorOrder[i] << ", got "
214-
<< desc->full_name();
201+
std::vector<std::string> actual_order;
202+
for (const Descriptor* desc :
203+
FileGeneratorFriendForTesting::MessagesInTopologicalOrder(fgen)) {
204+
actual_order.emplace_back(
205+
absl::StripPrefix(desc->full_name(), "protobuf_unittest."));
215206
}
207+
EXPECT_THAT(actual_order,
208+
::testing::ElementsAreArray(kExpectedDescriptorOrder));
216209
}
217210

218211
} // namespace

src/google/protobuf/edition_unittest.proto

+8
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,14 @@ message TestMessageSize {
11771177
int64 m6 = 6;
11781178
}
11791179

1180+
// Tests eager verification of a lazy message field.
1181+
message TestEagerlyVerifiedLazyMessage {
1182+
message LazyMessage {
1183+
bytes bytes_field = 1;
1184+
}
1185+
LazyMessage lazy_message = 1 [lazy = true];
1186+
}
1187+
11801188
// Test that RPC services work.
11811189
message FooRequest {}
11821190
message FooResponse {}

src/google/protobuf/message_unittest.inc

+24
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include "google/protobuf/message.h"
5252
#include "google/protobuf/reflection_ops.h"
5353
#include "google/protobuf/test_util2.h"
54+
#include "google/protobuf/wire_format_lite.h"
5455

5556

5657
// Must be included last.
@@ -364,6 +365,29 @@ TEST(MESSAGE_TEST_NAME, ExplicitLazyExceedRecursionLimit) {
364365
EXPECT_NE(parsed.lazy_child().child().payload().optional_int32(), -1);
365366
}
366367

368+
TEST(MESSAGE_TEST_NAME, ExplicitLazyBadLengthDelimitedSize) {
369+
std::string serialized;
370+
371+
// This is a regression test for a bug in lazy field verification. It
372+
// requires invalid wire format to trigger the bug.
373+
374+
// NestedMessage optional_lazy_message = 27 [lazy=true];
375+
uint32_t tag = internal::WireFormatLite::MakeTag(
376+
1, internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED);
377+
ASSERT_LT(tag, INT8_MAX);
378+
serialized.push_back(tag);
379+
serialized.push_back(6);
380+
381+
// bytes bytes_field = 1;
382+
serialized.push_back(tag);
383+
384+
// To trigger this bug, we need an overlong size.
385+
serialized.append(5, 0xff);
386+
387+
UNITTEST::TestLazyMessage parsed;
388+
EXPECT_FALSE(parsed.ParseFromString(serialized));
389+
}
390+
367391
TEST(MESSAGE_TEST_NAME, NestedLazyRecursionLimit) {
368392
UNITTEST::NestedTestAllTypes original, parsed;
369393
original.mutable_lazy_child()

src/google/protobuf/parse_context.h

+25-11
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,18 @@
88
#ifndef GOOGLE_PROTOBUF_PARSE_CONTEXT_H__
99
#define GOOGLE_PROTOBUF_PARSE_CONTEXT_H__
1010

11+
#include <algorithm>
12+
#include <climits>
13+
#include <cstddef>
1114
#include <cstdint>
1215
#include <cstring>
16+
#include <limits>
1317
#include <string>
1418
#include <type_traits>
1519
#include <utility>
1620

1721
#include "absl/base/config.h"
22+
#include "absl/base/prefetch.h"
1823
#include "absl/log/absl_check.h"
1924
#include "absl/log/absl_log.h"
2025
#include "absl/strings/cord.h"
@@ -27,9 +32,11 @@
2732
#include "google/protobuf/inlined_string_field.h"
2833
#include "google/protobuf/io/coded_stream.h"
2934
#include "google/protobuf/io/zero_copy_stream.h"
35+
#include "google/protobuf/message_lite.h"
3036
#include "google/protobuf/metadata_lite.h"
3137
#include "google/protobuf/port.h"
3238
#include "google/protobuf/repeated_field.h"
39+
#include "google/protobuf/repeated_ptr_field.h"
3340
#include "google/protobuf/wire_format_lite.h"
3441

3542

@@ -103,7 +110,7 @@ class PROTOBUF_EXPORT EpsCopyInputStream {
103110
ABSL_DCHECK(ptr <= buffer_end_ + kSlopBytes);
104111
int count;
105112
if (next_chunk_ == patch_buffer_) {
106-
count = static_cast<int>(buffer_end_ + kSlopBytes - ptr);
113+
count = BytesAvailable(ptr);
107114
} else {
108115
count = size_ + static_cast<int>(buffer_end_ - ptr);
109116
}
@@ -170,14 +177,14 @@ class PROTOBUF_EXPORT EpsCopyInputStream {
170177
}
171178

172179
PROTOBUF_NODISCARD const char* Skip(const char* ptr, int size) {
173-
if (size <= buffer_end_ + kSlopBytes - ptr) {
180+
if (size <= BytesAvailable(ptr)) {
174181
return ptr + size;
175182
}
176183
return SkipFallback(ptr, size);
177184
}
178185
PROTOBUF_NODISCARD const char* ReadString(const char* ptr, int size,
179186
std::string* s) {
180-
if (size <= buffer_end_ + kSlopBytes - ptr) {
187+
if (size <= BytesAvailable(ptr)) {
181188
// Fundamentally we just want to do assign to the string.
182189
// However micro-benchmarks regress on string reading cases. So we copy
183190
// the same logic from the old CodedInputStream ReadString. Note: as of
@@ -191,7 +198,7 @@ class PROTOBUF_EXPORT EpsCopyInputStream {
191198
}
192199
PROTOBUF_NODISCARD const char* AppendString(const char* ptr, int size,
193200
std::string* s) {
194-
if (size <= buffer_end_ + kSlopBytes - ptr) {
201+
if (size <= BytesAvailable(ptr)) {
195202
s->append(ptr, size);
196203
return ptr + size;
197204
}
@@ -204,8 +211,7 @@ class PROTOBUF_EXPORT EpsCopyInputStream {
204211

205212
PROTOBUF_NODISCARD const char* ReadCord(const char* ptr, int size,
206213
::absl::Cord* cord) {
207-
if (size <= std::min<int>(static_cast<int>(buffer_end_ + kSlopBytes - ptr),
208-
kMaxCordBytesToCopy)) {
214+
if (size <= std::min<int>(BytesAvailable(ptr), kMaxCordBytesToCopy)) {
209215
*cord = absl::string_view(ptr, size);
210216
return ptr + size;
211217
}
@@ -345,6 +351,14 @@ class PROTOBUF_EXPORT EpsCopyInputStream {
345351
// systems. TODO do we need to set this as build flag?
346352
enum { kSafeStringSize = 50000000 };
347353

354+
int BytesAvailable(const char* ptr) const {
355+
ABSL_DCHECK_NE(ptr, nullptr);
356+
ptrdiff_t available = buffer_end_ + kSlopBytes - ptr;
357+
ABSL_DCHECK_GE(available, 0);
358+
ABSL_DCHECK_LE(available, INT_MAX);
359+
return static_cast<int>(available);
360+
}
361+
348362
// Advances to next buffer chunk returns a pointer to the same logical place
349363
// in the stream as set by overrun. Overrun indicates the position in the slop
350364
// region the parse was left (0 <= overrun <= kSlopBytes). Returns true if at
@@ -382,7 +396,7 @@ class PROTOBUF_EXPORT EpsCopyInputStream {
382396

383397
template <typename A>
384398
const char* AppendSize(const char* ptr, int size, const A& append) {
385-
int chunk_size = static_cast<int>(buffer_end_ + kSlopBytes - ptr);
399+
int chunk_size = BytesAvailable(ptr);
386400
do {
387401
ABSL_DCHECK(size > chunk_size);
388402
if (next_chunk_ == nullptr) return nullptr;
@@ -396,7 +410,7 @@ class PROTOBUF_EXPORT EpsCopyInputStream {
396410
ptr = Next();
397411
if (ptr == nullptr) return nullptr; // passed the limit
398412
ptr += kSlopBytes;
399-
chunk_size = static_cast<int>(buffer_end_ + kSlopBytes - ptr);
413+
chunk_size = BytesAvailable(ptr);
400414
} while (size > chunk_size);
401415
append(ptr, size);
402416
return ptr + size;
@@ -411,7 +425,7 @@ class PROTOBUF_EXPORT EpsCopyInputStream {
411425
const char* AppendUntilEnd(const char* ptr, const A& append) {
412426
if (ptr - buffer_end_ > limit_) return nullptr;
413427
while (limit_ > kSlopBytes) {
414-
size_t chunk_size = buffer_end_ + kSlopBytes - ptr;
428+
size_t chunk_size = BytesAvailable(ptr);
415429
append(ptr, chunk_size);
416430
ptr = Next();
417431
if (ptr == nullptr) return limit_end_;
@@ -1163,7 +1177,7 @@ template <typename T>
11631177
const char* EpsCopyInputStream::ReadPackedFixed(const char* ptr, int size,
11641178
RepeatedField<T>* out) {
11651179
GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
1166-
int nbytes = static_cast<int>(buffer_end_ + kSlopBytes - ptr);
1180+
int nbytes = BytesAvailable(ptr);
11671181
while (size > nbytes) {
11681182
int num = nbytes / sizeof(T);
11691183
int old_entries = out->size();
@@ -1181,7 +1195,7 @@ const char* EpsCopyInputStream::ReadPackedFixed(const char* ptr, int size,
11811195
ptr = Next();
11821196
if (ptr == nullptr) return nullptr;
11831197
ptr += kSlopBytes - (nbytes - block_size);
1184-
nbytes = static_cast<int>(buffer_end_ + kSlopBytes - ptr);
1198+
nbytes = BytesAvailable(ptr);
11851199
}
11861200
int num = size / sizeof(T);
11871201
int block_size = num * sizeof(T);

src/google/protobuf/unittest.proto

+8
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,14 @@ message OpenEnumMessage {
17251725
repeated ForeignEnum repeated_closed = 4;
17261726
}
17271727

1728+
// Tests eager verification of a lazy message field.
1729+
message TestEagerlyVerifiedLazyMessage {
1730+
message LazyMessage {
1731+
bytes bytes_field = 1;
1732+
}
1733+
LazyMessage lazy_message = 1 [lazy = true];
1734+
}
1735+
17281736
// Test that RPC services work.
17291737
message FooRequest {
17301738
}

0 commit comments

Comments
 (0)