1 /*
2 * Copyright (c) 2009-2021, 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/util/def_to_proto.h"
29
30 #include <memory>
31 #include <string>
32
33 #include "google/protobuf/descriptor.pb.h"
34 #include "google/protobuf/descriptor.upbdefs.h"
35 #include "gmock/gmock.h"
36 #include "gtest/gtest.h"
37 #include "google/protobuf/dynamic_message.h"
38 #include "google/protobuf/util/message_differencer.h"
39 #include "upb/reflection/def.hpp"
40 #include "upb/test/parse_text_proto.h"
41 #include "upb/upb.hpp"
42 #include "upb/util/def_to_proto_test.h"
43 #include "upb/util/def_to_proto_test.upbdefs.h"
44
45 namespace upb_test {
46
47 // Loads and retrieves a descriptor for `msgdef` into the given `pool`.
AddMessageDescriptor(upb::MessageDefPtr msgdef,google::protobuf::DescriptorPool * pool)48 const google::protobuf::Descriptor* AddMessageDescriptor(
49 upb::MessageDefPtr msgdef, google::protobuf::DescriptorPool* pool) {
50 upb::Arena tmp_arena;
51 upb::FileDefPtr file = msgdef.file();
52 google_protobuf_FileDescriptorProto* upb_proto =
53 upb_FileDef_ToProto(file.ptr(), tmp_arena.ptr());
54 size_t size;
55 const char* buf = google_protobuf_FileDescriptorProto_serialize(
56 upb_proto, tmp_arena.ptr(), &size);
57 google::protobuf::FileDescriptorProto google_proto;
58 google_proto.ParseFromArray(buf, size);
59 const google::protobuf::FileDescriptor* file_desc =
60 pool->BuildFile(google_proto);
61 EXPECT_TRUE(file_desc != nullptr);
62 return pool->FindMessageTypeByName(msgdef.full_name());
63 }
64
65 // Converts a upb `msg` (with type `msgdef`) into a protobuf Message object from
66 // the given factory and descriptor.
ToProto(const upb_Message * msg,const upb_MessageDef * msgdef,const google::protobuf::Descriptor * desc,google::protobuf::MessageFactory * factory)67 std::unique_ptr<google::protobuf::Message> ToProto(
68 const upb_Message* msg, const upb_MessageDef* msgdef,
69 const google::protobuf::Descriptor* desc,
70 google::protobuf::MessageFactory* factory) {
71 upb::Arena arena;
72 EXPECT_TRUE(desc != nullptr);
73 std::unique_ptr<google::protobuf::Message> google_msg(
74 factory->GetPrototype(desc)->New());
75 char* buf;
76 size_t size;
77 upb_EncodeStatus status = upb_Encode(msg, upb_MessageDef_MiniTable(msgdef), 0,
78 arena.ptr(), &buf, &size);
79 EXPECT_EQ(status, kUpb_EncodeStatus_Ok);
80 google_msg->ParseFromArray(buf, size);
81 return google_msg;
82 }
83
84 // A gtest matcher that verifies that a proto is equal to `proto`. Both `proto`
85 // and `arg` must be messages of type `msgdef_func` (a .upbdefs.h function that
86 // loads a known msgdef into the given defpool).
87 MATCHER_P2(EqualsUpbProto, proto, msgdef_func,
88 negation ? "are not equal" : "are equal") {
89 upb::DefPool defpool;
90 google::protobuf::DescriptorPool pool;
91 google::protobuf::DynamicMessageFactory factory;
92 upb::MessageDefPtr msgdef(msgdef_func(defpool.ptr()));
93 EXPECT_TRUE(msgdef.ptr() != nullptr);
94 const google::protobuf::Descriptor* desc =
95 AddMessageDescriptor(msgdef, &pool);
96 EXPECT_TRUE(desc != nullptr);
97 std::unique_ptr<google::protobuf::Message> m1(
98 ToProto(proto, msgdef.ptr(), desc, &factory));
99 std::unique_ptr<google::protobuf::Message> m2(
100 ToProto(arg, msgdef.ptr(), desc, &factory));
101 std::string differences;
102 google::protobuf::util::MessageDifferencer differencer;
103 differencer.ReportDifferencesToString(&differences);
104 bool eq = differencer.Compare(*m2, *m1);
105 if (!eq) {
106 *result_listener << differences;
107 }
108 return eq;
109 }
110
111 // Verifies that the given upb FileDef can be converted to a proto that matches
112 // `proto`.
CheckFile(const upb::FileDefPtr file,const google_protobuf_FileDescriptorProto * proto)113 void CheckFile(const upb::FileDefPtr file,
114 const google_protobuf_FileDescriptorProto* proto) {
115 upb::Arena arena;
116 google_protobuf_FileDescriptorProto* proto2 =
117 upb_FileDef_ToProto(file.ptr(), arena.ptr());
118 ASSERT_THAT(
119 proto,
120 EqualsUpbProto(proto2, google_protobuf_FileDescriptorProto_getmsgdef));
121 }
122
123 // Verifies that upb/util/def_to_proto_test.proto can round-trip:
124 // serialized descriptor -> upb def -> serialized descriptor
TEST(DefToProto,Test)125 TEST(DefToProto, Test) {
126 upb::Arena arena;
127 upb::DefPool defpool;
128 upb_StringView test_file_desc =
129 upb_util_def_to_proto_test_proto_upbdefinit.descriptor;
130 const auto* file_desc = google_protobuf_FileDescriptorProto_parse(
131 test_file_desc.data, test_file_desc.size, arena.ptr());
132
133 upb::MessageDefPtr msgdef(pkg_Message_getmsgdef(defpool.ptr()));
134 upb::FileDefPtr file = msgdef.file();
135 CheckFile(file, file_desc);
136 }
137
138 // Like the previous test, but uses a message layout built at runtime.
TEST(DefToProto,TestRuntimeReflection)139 TEST(DefToProto, TestRuntimeReflection) {
140 upb::Arena arena;
141 upb::DefPool defpool;
142 upb_StringView test_file_desc =
143 upb_util_def_to_proto_test_proto_upbdefinit.descriptor;
144 const auto* file_desc = google_protobuf_FileDescriptorProto_parse(
145 test_file_desc.data, test_file_desc.size, arena.ptr());
146
147 _upb_DefPool_LoadDefInitEx(
148 defpool.ptr(),
149 &upb_util_def_to_proto_test_proto_upbdefinit, true);
150 upb::FileDefPtr file = defpool.FindFileByName(
151 upb_util_def_to_proto_test_proto_upbdefinit.filename);
152 CheckFile(file, file_desc);
153 }
154
155 // Fuzz test regressions.
156
TEST(FuzzTest,EmptyPackage)157 TEST(FuzzTest, EmptyPackage) {
158 RoundTripDescriptor(ParseTextProtoOrDie(R"pb(file { package: "" })pb"));
159 }
160
TEST(FuzzTest,EmptyName)161 TEST(FuzzTest, EmptyName) {
162 RoundTripDescriptor(ParseTextProtoOrDie(R"pb(file { name: "" })pb"));
163 }
164
TEST(FuzzTest,EmptyPackage2)165 TEST(FuzzTest, EmptyPackage2) {
166 RoundTripDescriptor(
167 ParseTextProtoOrDie(R"pb(file { name: "n" package: "" })pb"));
168 }
169
TEST(FuzzTest,FileNameEmbeddedNull)170 TEST(FuzzTest, FileNameEmbeddedNull) {
171 RoundTripDescriptor(ParseTextProtoOrDie(R"pb(file { name: "\000" })pb"));
172 }
173
TEST(FuzzTest,EditionEmbeddedNull)174 TEST(FuzzTest, EditionEmbeddedNull) {
175 RoundTripDescriptor(
176 ParseTextProtoOrDie(R"pb(file { name: "n" edition: "\000" })pb"));
177 }
178
TEST(FuzzTest,DuplicateOneofIndex)179 TEST(FuzzTest, DuplicateOneofIndex) {
180 RoundTripDescriptor(ParseTextProtoOrDie(
181 R"pb(file {
182 name: "F"
183 message_type {
184 name: "M"
185 oneof_decl { name: "O" }
186 field { name: "f1" number: 1 type: TYPE_INT32 oneof_index: 0 }
187 field { name: "f2" number: 1 type: TYPE_INT32 oneof_index: 0 }
188 }
189 })pb"));
190 }
191
TEST(FuzzTest,NanValue)192 TEST(FuzzTest, NanValue) {
193 RoundTripDescriptor(ParseTextProtoOrDie(
194 R"pb(file {
195 enum_type {
196 value {
197 number: 0
198 options { uninterpreted_option { double_value: nan } }
199 }
200 }
201 })pb"));
202 }
203
TEST(FuzzTest,EnumValueEmbeddedNull)204 TEST(FuzzTest, EnumValueEmbeddedNull) {
205 RoundTripDescriptor(ParseTextProtoOrDie(
206 R"pb(file {
207 name: "\035"
208 enum_type {
209 name: "f"
210 value { name: "\000" number: 0 }
211 }
212 })pb"));
213 }
214
TEST(FuzzTest,EnumValueNoNumber)215 TEST(FuzzTest, EnumValueNoNumber) {
216 RoundTripDescriptor(ParseTextProtoOrDie(
217 R"pb(file {
218 name: "\035"
219 enum_type {
220 name: "f"
221 value { name: "abc" }
222 }
223 })pb"));
224 }
225
TEST(FuzzTest,DefaultWithUnterminatedHex)226 TEST(FuzzTest, DefaultWithUnterminatedHex) {
227 RoundTripDescriptor(ParseTextProtoOrDie(
228 R"pb(file {
229 name: "\035"
230 message_type {
231 name: "A"
232 field {
233 name: "f"
234 number: 1
235 label: LABEL_OPTIONAL
236 type: TYPE_BYTES
237 default_value: "\\x"
238 }
239 }
240 })pb"));
241 }
242
TEST(FuzzTest,DefaultWithValidHexEscape)243 TEST(FuzzTest, DefaultWithValidHexEscape) {
244 RoundTripDescriptor(ParseTextProtoOrDie(
245 R"pb(file {
246 name: "\035"
247 message_type {
248 name: "A"
249 field {
250 name: "f"
251 number: 1
252 label: LABEL_OPTIONAL
253 type: TYPE_BYTES
254 default_value: "\\x03"
255 }
256 }
257 })pb"));
258 }
259
TEST(FuzzTest,DefaultWithValidHexEscapePrintable)260 TEST(FuzzTest, DefaultWithValidHexEscapePrintable) {
261 RoundTripDescriptor(ParseTextProtoOrDie(
262 R"pb(file {
263 name: "\035"
264 message_type {
265 name: "A"
266 field {
267 name: "f"
268 number: 1
269 label: LABEL_OPTIONAL
270 type: TYPE_BYTES
271 default_value: "\\x23" # 0x32 = '#'
272 }
273 }
274 })pb"));
275 }
276
277 // begin:google_only
278 // TEST(FuzzTest, DependencyWithEmbeddedNull) {
279 // RoundTripDescriptor(ParseTextProtoOrDie(R"pb(file {
280 // name: "a"
281 // dependency: "a\000"
282 // options { cc_api_version: 0 }
283 // weak_dependency: 0
284 // })pb"));
285 // }
286 //
287 // TEST(FuzzTest, NanInOptions) {
288 // RoundTripDescriptor(
289 // ParseTextProtoOrDie(R"pb(file {
290 // name: ""
291 // service {
292 // name: "A"
293 // options { failure_detection_delay: nan }
294 // }
295 // })pb"));
296 // }
297 // end:google_only
298
TEST(FuzzTest,PackageStartsWithNumber)299 TEST(FuzzTest, PackageStartsWithNumber) {
300 RoundTripDescriptor(
301 ParseTextProtoOrDie(R"pb(file { name: "" package: "0" })pb"));
302 }
303
TEST(FuzzTest,RoundTripDescriptorRegression)304 TEST(FuzzTest, RoundTripDescriptorRegression) {
305 RoundTripDescriptor(ParseTextProtoOrDie(R"pb(file {
306 name: ""
307 message_type {
308 name: "A"
309 field {
310 name: "B"
311 number: 1
312 type: TYPE_BYTES
313 default_value: "\007"
314 }
315 }
316 })pb"));
317 }
318
319 // Multiple oneof fields which have the same name.
TEST(FuzzTest,RoundTripDescriptorRegressionOneofSameName)320 TEST(FuzzTest, RoundTripDescriptorRegressionOneofSameName) {
321 RoundTripDescriptor(ParseTextProtoOrDie(
322 R"pb(file {
323 name: "N"
324 package: ""
325 message_type {
326 name: "b"
327 field { name: "W" number: 1 type: TYPE_BYTES oneof_index: 0 }
328 field { name: "W" number: 17 type: TYPE_UINT32 oneof_index: 0 }
329 oneof_decl { name: "k" }
330 }
331 })pb"));
332 }
333
334 } // namespace upb_test
335