From 5a7644b2d069d8e834cd41c677ed70e89e66920e Mon Sep 17 00:00:00 2001 From: Joshua Haberman <haberman@google.com> Date: Tue, 6 Sep 2022 10:27:55 -0700 Subject: [PATCH] Fixed fuzz bug in upb. Extending a MessageSet with a non-message extension was causing crashes that would manifest in various ways. PiperOrigin-RevId: 472496259 --- upb/fuzz_test_util.cc | 8 ++++---- upb/mini_table.c | 23 ++++++++++++++++++++--- upb/mini_table.h | 1 + upb/msg_test.cc | 36 ++++++++++++++++++++++++++++++++++++ upbc/file_layout.cc | 6 +++++- 5 files changed, 66 insertions(+), 8 deletions(-) diff --git a/upb/fuzz_test_util.cc b/upb/fuzz_test_util.cc index 12de4a9283..219e4a3294 100644 --- a/upb/fuzz_test_util.cc +++ b/upb/fuzz_test_util.cc @@ -111,8 +111,6 @@ void Builder::BuildEnums() { bool Builder::LinkExtension(upb_MiniTable_Extension* ext) { upb_MiniTable_Field* field = &ext->field; - ext->extendee = NextMiniTable(); - if (!ext->extendee) return false; if (field->descriptortype == kUpb_FieldType_Message || field->descriptortype == kUpb_FieldType_Group) { auto mt = NextMiniTable(); @@ -140,8 +138,10 @@ void Builder::BuildExtensions(upb_ExtensionRegistry** exts) { upb_MiniTable_Extension* ext = reinterpret_cast<upb_MiniTable_Extension*>( upb_Arena_Malloc(arena_, sizeof(*ext))); upb_MiniTable_Sub sub; - ptr = - upb_MiniTable_BuildExtension(ptr, end - ptr, ext, sub, status.ptr()); + const upb_MiniTable* extendee = NextMiniTable(); + if (!extendee) break; + ptr = upb_MiniTable_BuildExtension(ptr, end - ptr, ext, extendee, sub, + status.ptr()); if (!ptr) break; if (!LinkExtension(ext)) continue; if (_upb_extreg_get(*exts, ext->extendee, ext->field.number)) continue; diff --git a/upb/mini_table.c b/upb/mini_table.c index 0b59607053..3d13780a04 100644 --- a/upb/mini_table.c +++ b/upb/mini_table.c @@ -1102,6 +1102,7 @@ upb_MiniTable_Enum* upb_MiniTable_BuildEnum(const char* data, size_t len, const char* upb_MiniTable_BuildExtension(const char* data, size_t len, upb_MiniTable_Extension* ext, + const upb_MiniTable* extendee, upb_MiniTable_Sub sub, upb_Status* status) { upb_MtDecoder decoder = { @@ -1117,9 +1118,25 @@ const char* upb_MiniTable_BuildExtension(const char* data, size_t len, uint16_t count = 0; const char* ret = upb_MtDecoder_Parse(&decoder, data, len, ext, sizeof(*ext), &count, NULL); - ext->field.mode |= kUpb_LabelFlags_IsExtension; - ext->field.offset = 0; - ext->field.presence = 0; + if (!ret) return NULL; + + upb_MiniTable_Field* f = &ext->field; + + f->mode |= kUpb_LabelFlags_IsExtension; + f->offset = 0; + f->presence = 0; + + if (extendee->ext & kUpb_ExtMode_IsMessageSet) { + // Extensions of MessageSet must be messages. + if (!upb_IsSubMessage(f)) return NULL; + + // Extensions of MessageSet must be non-repeating. + if ((f->mode & kUpb_FieldMode_Mask) == kUpb_FieldMode_Array) return NULL; + } + + ext->extendee = extendee; + ext->sub = sub; + return ret; } diff --git a/upb/mini_table.h b/upb/mini_table.h index 238c6d7222..d71ebcf320 100644 --- a/upb/mini_table.h +++ b/upb/mini_table.h @@ -156,6 +156,7 @@ void upb_MiniTable_SetSubEnum(upb_MiniTable* table, upb_MiniTable_Field* field, const char* upb_MiniTable_BuildExtension(const char* data, size_t len, upb_MiniTable_Extension* ext, + const upb_MiniTable* extendee, upb_MiniTable_Sub sub, upb_Status* status); diff --git a/upb/msg_test.cc b/upb/msg_test.cc index 68fd6267ee..9e1548ae9d 100644 --- a/upb/msg_test.cc +++ b/upb/msg_test.cc @@ -534,4 +534,40 @@ TEST(MessageTest, MapField) { // "\010\002", 342248070, -806315555); // } // +// TEST(FuzzTest, DecodeExtendMessageSetWithNonMessage) { +// DecodeEncodeArbitrarySchemaAndPayload( +// {{"\n"}, {""}, ".\244", {}}, "\013\032\005\212a#\365\336\020\001\226", +// 14803219, 670718349); +// } +// +// TEST(FuzzTest, DecodeExtendMessageSetWithNonMessage2) { +// DecodeEncodeArbitrarySchemaAndPayload({{"\n", "G", "\n", "\274", ""}, +// {"", "\030"}, +// "_@", +// {4294967295, 2147483647}}, +// std::string("\013\032\000\220", 4), +// 279975758, 1647495141); +// } +// +// TEST(FuzzTest, DecodeExtendMessageSetWithNonMessage3) { +// DecodeEncodeArbitrarySchemaAndPayload( +// {{"\n"}, {"B", ""}, "\212:b", {11141121}}, +// "\013\032\004\357;7\363\020\001\346\240\200\201\271", 399842149, +// -452966025); +// } +// +// TEST(FuzzTest, DecodeExtendMessageSetWithNonMessage4) { +// DecodeEncodeArbitrarySchemaAndPayload( +// {{"\n", "3\340", "\354"}, {}, "B}G", {4294967295, 4082331310}}, +// "\013\032\004\244B\331\255\020\001\220\224\243\350\t", -561523015, +// 1683327312); +// } +// +// TEST(FuzzTest, DecodeExtendMessageSetWithNonMessage5) { +// DecodeEncodeArbitrarySchemaAndPayload( +// {{"\n"}, {""}, "kB", {0}}, +// "x\203\251\006\013\032\002S\376\010\273\'\020\014\365\207\244\234", +// -696925610, -654590577); +// } +// // end:google_only diff --git a/upbc/file_layout.cc b/upbc/file_layout.cc index 49268aab27..f9dbac39f0 100644 --- a/upbc/file_layout.cc +++ b/upbc/file_layout.cc @@ -265,8 +265,12 @@ void FilePlatformLayout::BuildExtensions(const protobuf::FileDescriptor* fd) { GetFieldModifiers(f)); upb_MiniTable_Extension& ext = extension_map_[f]; upb_MiniTable_Sub sub; + // The extendee may be from another file, so we build a temporary MiniTable + // for it, just for the purpose of building the extension. + // Note, we are not caching so this could use more memory than is necessary. + upb_MiniTable* extendee = MakeMiniTable(f->containing_type()); bool ok = upb_MiniTable_BuildExtension(e.data().data(), e.data().size(), - &ext, sub, status.ptr()); + &ext, extendee, sub, status.ptr()); if (!ok) { // TODO(haberman): Use ABSL CHECK() when it is available. fprintf(stderr, "Error building mini-table: %s\n",