Skip to content

Commit 92db7ff

Browse files
sbenzaquencopybara-github
authored andcommitted
Fix off-by-one check in required field count check.
upb has an implementation-specific maximum of 63 required fields per message. We need to verify this limit when building a MiniTable. PiperOrigin-RevId: 850455994
1 parent ced5509 commit 92db7ff

File tree

3 files changed

+41
-1
lines changed

3 files changed

+41
-1
lines changed

upb/mini_descriptor/decode.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,12 +623,13 @@ static void upb_MtDecoder_AssignHasbits(upb_MtDecoder* d) {
623623
field->presence = 0;
624624
}
625625
}
626-
if (last_hasbit > kUpb_Reserved_Hasbits + 63) {
626+
if (last_hasbit >= kUpb_Reserved_Hasbits + 63) {
627627
upb_MdDecoder_ErrorJmp(&d->base, "Too many required fields");
628628
}
629629

630630
d->table.UPB_PRIVATE(required_count) =
631631
last_hasbit - (kUpb_Reserved_Hasbits - 1);
632+
UPB_ASSERT(d->table.UPB_PRIVATE(required_count) < 64);
632633

633634
// Next assign non-required hasbit fields.
634635
for (int i = 0; i < n; i++) {

upb/reflection/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ cc_test(
191191
"@abseil-cpp//absl/log:check",
192192
"@abseil-cpp//absl/status",
193193
"@abseil-cpp//absl/status:statusor",
194+
"@abseil-cpp//absl/strings",
195+
"@abseil-cpp//absl/strings:str_format",
194196
"@googletest//:gtest",
195197
"@googletest//:gtest_main",
196198
],

upb/reflection/reflection_test.cc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
#include "google/protobuf/descriptor.pb.h"
66
#include "google/protobuf/descriptor.upb.h"
7+
#include <gmock/gmock.h>
78
#include <gtest/gtest.h>
89
#include "absl/log/absl_check.h"
910
#include "absl/status/status.h"
1011
#include "absl/status/statusor.h"
12+
#include "absl/strings/str_format.h"
13+
#include "absl/strings/str_join.h"
1114
#include "upb/base/status.hpp"
1215
#include "upb/mem/arena.hpp"
1316
#include "upb/reflection/def.hpp"
@@ -16,6 +19,8 @@
1619
namespace upb_test {
1720
namespace {
1821

22+
using testing::HasSubstr;
23+
1924
google_protobuf_FileDescriptorProto* ToUpbDescriptorSet(
2025
const google::protobuf::FileDescriptorProto& proto, upb::Arena& arena) {
2126
std::string serialized;
@@ -128,5 +133,37 @@ TEST(ReflectionTest, ImplicitPresenceWithNonZeroDefaultEnum) {
128133
"with a non-zero default (FooEnum)");
129134
}
130135

136+
TEST(ReflectionTest, TooManyRequiredFieldsFailGracefully) {
137+
const auto make_desc = [](int n) {
138+
std::vector<std::string> fields;
139+
for (int i = 1; i <= n; ++i) {
140+
fields.push_back(absl::StrFormat(R"pb(
141+
field {
142+
name: "f%d"
143+
number: %d
144+
type: TYPE_INT32
145+
label: LABEL_REQUIRED
146+
})pb",
147+
i, i));
148+
}
149+
return absl::StrFormat(
150+
R"pb(
151+
syntax: "proto2"
152+
name: "F"
153+
message_type { name: "FooMessage" %s }
154+
)pb",
155+
absl::StrJoin(fields, "\n"));
156+
};
157+
// 63 required fields is ok.
158+
upb::DefPool good = LoadDescriptorProto(make_desc(63)).value();
159+
auto m = good.FindMessageByName("FooMessage");
160+
auto f = m.FindFieldByNumber(63);
161+
EXPECT_STREQ(f.full_name(), "FooMessage.f63");
162+
163+
// 64 is too much.
164+
EXPECT_THAT(LoadDescriptorProto(make_desc(64)).status().message(),
165+
HasSubstr("Too many required fields"));
166+
}
167+
131168
} // namespace
132169
} // namespace upb_test

0 commit comments

Comments
 (0)