Skip to content

Commit 4029df0

Browse files
habermancopybara-github
authored andcommitted
Fixed upb's parser to detect missing required fields in missing map values.
PiperOrigin-RevId: 855944266
1 parent 8cfe0c8 commit 4029df0

File tree

4 files changed

+70
-5
lines changed

4 files changed

+70
-5
lines changed

upb/test/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ cc_test(
292292
"//upb/mem",
293293
"//upb/message",
294294
"//upb/port",
295+
"//upb/wire",
295296
"@googletest//:gtest",
296297
"@googletest//:gtest_main",
297298
],

upb/test/test.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,17 @@ message ModelWithSubMessages {
9090
repeated ModelWithExtensions items = 6;
9191
}
9292

93+
message ModelWithRequiredFields {
94+
required int32 id = 1;
95+
}
96+
9397
message ModelWithMaps {
9498
optional int32 id = 1;
9599
map<string, bytes> map_sb = 2;
96100
map<string, string> map_ss = 3;
97101
map<int32, int32> map_ii = 4;
98102
map<int32, ModelWithExtensions> map_im = 5;
103+
map<int32, ModelWithRequiredFields> map_im_required = 6;
99104
}
100105

101106
message ExtremeDefaults {

upb/test/test_generated_code.cc

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,23 @@
1212

1313
#include <cstddef>
1414
#include <cstdint>
15+
#include <cstdlib>
16+
#include <cstring>
1517

1618
#include <gtest/gtest.h>
1719
#include "google/protobuf/test_messages_proto2.upb.h"
1820
#include "google/protobuf/test_messages_proto3.upb.h"
1921
#include "upb/base/status.h"
2022
#include "upb/base/string_view.h"
23+
#include "upb/mem/alloc.h"
24+
#include "upb/mem/arena.h"
2125
#include "upb/mem/arena.hpp"
2226
#include "upb/message/array.h"
2327
#include "upb/message/map.h"
28+
#include "upb/message/message.h"
2429
#include "upb/test/test.upb.h"
30+
#include "upb/wire/decode.h"
31+
#include "upb/wire/encode.h"
2532

2633
// Must be last.
2734
#include "upb/port/def.inc"
@@ -910,3 +917,40 @@ TEST(GeneratedCode, Maps) {
910917

911918
upb_Map_Set(sb, key, val, arena.ptr());
912919
}
920+
921+
TEST(GeneratedCode, MapWithRequiredFields) {
922+
upb::Arena arena;
923+
upb_test_ModelWithMaps* msg = upb_test_ModelWithMaps_new(arena.ptr());
924+
925+
auto im_required =
926+
_upb_test_ModelWithMaps_map_im_required_mutable_upb_map(msg, arena.ptr());
927+
928+
ASSERT_NE(im_required, nullptr);
929+
930+
upb_MessageValue key, val;
931+
key.int32_val = 0;
932+
val.msg_val =
933+
(const upb_Message*)upb_test_ModelWithRequiredFields_new(arena.ptr());
934+
935+
upb_Map_Set(im_required, key, val, arena.ptr());
936+
937+
// Serializing fails if we are checking required fields, but succeeds if we
938+
// don't.
939+
size_t size;
940+
char* serialized = upb_test_ModelWithMaps_serialize_ex(
941+
msg, kUpb_EncodeOption_CheckRequired, arena.ptr(), &size);
942+
ASSERT_EQ(serialized, nullptr);
943+
944+
serialized = upb_test_ModelWithMaps_serialize_ex(msg, 0, arena.ptr(), &size);
945+
ASSERT_NE(serialized, nullptr);
946+
947+
// Likewise, parsing fails if we are checking required fields, but succeeds
948+
// if we don't.
949+
upb_test_ModelWithMaps* msg2 = upb_test_ModelWithMaps_parse_ex(
950+
serialized, size, nullptr, kUpb_DecodeOption_CheckRequired, arena.ptr());
951+
ASSERT_EQ(msg2, nullptr);
952+
953+
msg2 = upb_test_ModelWithMaps_parse_ex(serialized, size, nullptr, 0,
954+
arena.ptr());
955+
ASSERT_NE(msg2, nullptr);
956+
}

upb/wire/decode.c

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -538,17 +538,32 @@ static const char* _upb_Decoder_DecodeToMap(upb_Decoder* d, const char* ptr,
538538
// Parse map entry.
539539
memset(&ent, 0, sizeof(ent));
540540

541-
if (entry->UPB_PRIVATE(fields)[1].UPB_PRIVATE(descriptortype) ==
541+
bool value_is_message =
542+
entry->UPB_PRIVATE(fields)[1].UPB_PRIVATE(descriptortype) ==
542543
kUpb_FieldType_Message ||
543544
entry->UPB_PRIVATE(fields)[1].UPB_PRIVATE(descriptortype) ==
544-
kUpb_FieldType_Group) {
545+
kUpb_FieldType_Group;
546+
const upb_MiniTable* sub_table =
547+
value_is_message
548+
? upb_MiniTable_GetSubMessageTable(&entry->UPB_PRIVATE(fields)[1])
549+
: NULL;
550+
upb_Message* sub_msg = NULL;
551+
552+
if (sub_table) {
545553
// Create proactively to handle the case where it doesn't appear.
546-
upb_Message* msg;
547-
_upb_Decoder_NewSubMessage(d, &entry->UPB_PRIVATE(fields)[1], &msg);
548-
ent.v.val = upb_value_ptr(msg);
554+
_upb_Decoder_NewSubMessage(d, &entry->UPB_PRIVATE(fields)[1], &sub_msg);
555+
ent.v.val = upb_value_ptr(sub_msg);
549556
}
550557

551558
ptr = _upb_Decoder_DecodeSubMessage(d, ptr, &ent.message, field, val->size);
559+
560+
if (sub_msg && sub_table->UPB_PRIVATE(required_count)) {
561+
// If the map entry did not contain a value on the wire, `sub_msg` is an
562+
// empty message; we must check if it is missing any required fields. If the
563+
// value was present, this check is redundant but harmless.
564+
_upb_Decoder_CheckRequired(d, ptr, sub_msg, sub_table);
565+
}
566+
552567
if (upb_Message_HasUnknown(&ent.message)) {
553568
_upb_Decoder_AddMapEntryUnknown(d, msg, field, &ent.message, entry);
554569
} else {

0 commit comments

Comments
 (0)