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