1 /*
2 * Copyright (c) 2009-2022, Google LLC
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of Google LLC nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "upb/test/fuzz_util.h"
29
30 #include "upb/message/message.h"
31 #include "upb/mini_table/decode.h"
32 #include "upb/mini_table/extension_internal.h"
33 #include "upb/mini_table/extension_registry.h"
34 #include "upb/upb.hpp"
35
36 // Must be last
37 #include "upb/port/def.inc"
38
39 namespace upb {
40 namespace fuzz {
41
42 namespace {
43
44 class Builder {
45 public:
Builder(const MiniTableFuzzInput & input,upb_Arena * arena)46 Builder(const MiniTableFuzzInput& input, upb_Arena* arena)
47 : input_(&input), arena_(arena) {}
48
Build(upb_ExtensionRegistry ** exts)49 const upb_MiniTable* Build(upb_ExtensionRegistry** exts) {
50 BuildMessages();
51 BuildEnums();
52 BuildExtensions(exts);
53 if (!LinkMessages()) return nullptr;
54 return mini_tables_.empty() ? nullptr : mini_tables_.front();
55 }
56
57 private:
58 void BuildMessages();
59 void BuildEnums();
60 void BuildExtensions(upb_ExtensionRegistry** exts);
61 bool LinkExtension(upb_MiniTableExtension* ext);
62 bool LinkMessages();
63
NextLink()64 size_t NextLink() {
65 if (input_->links.empty()) return 0;
66 if (link_ == input_->links.size()) link_ = 0;
67 return input_->links[link_++];
68 }
69
NextMiniTable()70 const upb_MiniTable* NextMiniTable() {
71 return mini_tables_.empty()
72 ? nullptr
73 : mini_tables_[NextLink() % mini_tables_.size()];
74 }
75
NextEnumTable()76 const upb_MiniTableEnum* NextEnumTable() {
77 return enum_tables_.empty()
78 ? nullptr
79 : enum_tables_[NextLink() % enum_tables_.size()];
80 }
81
82 const MiniTableFuzzInput* input_;
83 upb_Arena* arena_;
84 std::vector<const upb_MiniTable*> mini_tables_;
85 std::vector<const upb_MiniTableEnum*> enum_tables_;
86 size_t link_ = 0;
87 };
88
BuildMessages()89 void Builder::BuildMessages() {
90 upb::Status status;
91 mini_tables_.reserve(input_->mini_descriptors.size());
92 for (const auto& d : input_->mini_descriptors) {
93 upb_MiniTable* table =
94 upb_MiniTable_Build(d.data(), d.size(), arena_, status.ptr());
95 if (table) mini_tables_.push_back(table);
96 }
97 }
98
BuildEnums()99 void Builder::BuildEnums() {
100 upb::Status status;
101 enum_tables_.reserve(input_->enum_mini_descriptors.size());
102 for (const auto& d : input_->enum_mini_descriptors) {
103 upb_MiniTableEnum* enum_table =
104 upb_MiniTableEnum_Build(d.data(), d.size(), arena_, status.ptr());
105 if (enum_table) enum_tables_.push_back(enum_table);
106 }
107 }
108
LinkExtension(upb_MiniTableExtension * ext)109 bool Builder::LinkExtension(upb_MiniTableExtension* ext) {
110 upb_MiniTableField* field = &ext->field;
111 if (upb_MiniTableField_CType(field) == kUpb_CType_Message) {
112 auto mt = NextMiniTable();
113 if (!mt) field->UPB_PRIVATE(descriptortype) = kUpb_FieldType_Int32;
114 ext->sub.submsg = mt;
115 }
116 if (upb_MiniTableField_IsClosedEnum(field)) {
117 auto et = NextEnumTable();
118 if (!et) field->UPB_PRIVATE(descriptortype) = kUpb_FieldType_Int32;
119 ext->sub.subenum = et;
120 }
121 return true;
122 }
123
BuildExtensions(upb_ExtensionRegistry ** exts)124 void Builder::BuildExtensions(upb_ExtensionRegistry** exts) {
125 upb::Status status;
126 if (input_->extensions.empty()) {
127 *exts = nullptr;
128 } else {
129 *exts = upb_ExtensionRegistry_New(arena_);
130 const char* ptr = input_->extensions.data();
131 const char* end = ptr + input_->extensions.size();
132 // Iterate through the buffer, building extensions as long as we can.
133 while (ptr < end) {
134 upb_MiniTableExtension* ext = reinterpret_cast<upb_MiniTableExtension*>(
135 upb_Arena_Malloc(arena_, sizeof(*ext)));
136 upb_MiniTableSub sub;
137 const upb_MiniTable* extendee = NextMiniTable();
138 if (!extendee) break;
139 ptr = upb_MiniTableExtension_Init(ptr, end - ptr, ext, extendee, sub,
140 status.ptr());
141 if (!ptr) break;
142 if (!LinkExtension(ext)) continue;
143 if (upb_ExtensionRegistry_Lookup(*exts, ext->extendee, ext->field.number))
144 continue;
145 upb_ExtensionRegistry_AddArray(
146 *exts, const_cast<const upb_MiniTableExtension**>(&ext), 1);
147 }
148 }
149 }
150
LinkMessages()151 bool Builder::LinkMessages() {
152 for (auto* t : mini_tables_) {
153 upb_MiniTable* table = const_cast<upb_MiniTable*>(t);
154 // For each field that requires a sub-table, assign one as appropriate.
155 for (size_t i = 0; i < table->field_count; i++) {
156 upb_MiniTableField* field =
157 const_cast<upb_MiniTableField*>(&table->fields[i]);
158 if (link_ == input_->links.size()) link_ = 0;
159 if (upb_MiniTableField_CType(field) == kUpb_CType_Message &&
160 !upb_MiniTable_SetSubMessage(table, field, NextMiniTable())) {
161 return false;
162 }
163 if (upb_MiniTableField_IsClosedEnum(field)) {
164 auto* et = NextEnumTable();
165 if (et) {
166 if (!upb_MiniTable_SetSubEnum(table, field, et)) return false;
167 } else {
168 // We don't have any sub-enums. Override the field type so that it is
169 // not needed.
170 field->UPB_PRIVATE(descriptortype) = kUpb_FieldType_Int32;
171 }
172 }
173 }
174 }
175 return true;
176 }
177
178 } // namespace
179
BuildMiniTable(const MiniTableFuzzInput & input,upb_ExtensionRegistry ** exts,upb_Arena * arena)180 const upb_MiniTable* BuildMiniTable(const MiniTableFuzzInput& input,
181 upb_ExtensionRegistry** exts,
182 upb_Arena* arena) {
183 Builder builder(input, arena);
184 return builder.Build(exts);
185 }
186
187 } // namespace fuzz
188 } // namespace upb
189