1 /*
2  * Copyright 2015 Google Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "flatbuffers/reflection.h"
18 
19 #include "flatbuffers/util.h"
20 
21 // Helper functionality for reflection.
22 
23 namespace flatbuffers {
24 
GetAnyValueI(reflection::BaseType type,const uint8_t * data)25 int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data) {
26   // clang-format off
27   #define FLATBUFFERS_GET(T) static_cast<int64_t>(ReadScalar<T>(data))
28   switch (type) {
29     case reflection::UType:
30     case reflection::Bool:
31     case reflection::UByte:  return FLATBUFFERS_GET(uint8_t);
32     case reflection::Byte:   return FLATBUFFERS_GET(int8_t);
33     case reflection::Short:  return FLATBUFFERS_GET(int16_t);
34     case reflection::UShort: return FLATBUFFERS_GET(uint16_t);
35     case reflection::Int:    return FLATBUFFERS_GET(int32_t);
36     case reflection::UInt:   return FLATBUFFERS_GET(uint32_t);
37     case reflection::Long:   return FLATBUFFERS_GET(int64_t);
38     case reflection::ULong:  return FLATBUFFERS_GET(uint64_t);
39     case reflection::Float:  return FLATBUFFERS_GET(float);
40     case reflection::Double: return FLATBUFFERS_GET(double);
41     case reflection::String: {
42       auto s = reinterpret_cast<const String *>(ReadScalar<uoffset_t>(data) +
43                                                 data);
44       return s ? StringToInt(s->c_str()) : 0;
45     }
46     default: return 0;  // Tables & vectors do not make sense.
47   }
48   #undef FLATBUFFERS_GET
49   // clang-format on
50 }
51 
GetAnyValueF(reflection::BaseType type,const uint8_t * data)52 double GetAnyValueF(reflection::BaseType type, const uint8_t *data) {
53   switch (type) {
54     case reflection::Float: return static_cast<double>(ReadScalar<float>(data));
55     case reflection::Double: return ReadScalar<double>(data);
56     case reflection::String: {
57       auto s =
58           reinterpret_cast<const String *>(ReadScalar<uoffset_t>(data) + data);
59       if (s) {
60         double d;
61         StringToNumber(s->c_str(), &d);
62         return d;
63       } else {
64         return 0.0;
65       }
66     }
67     default: return static_cast<double>(GetAnyValueI(type, data));
68   }
69 }
70 
GetAnyValueS(reflection::BaseType type,const uint8_t * data,const reflection::Schema * schema,int type_index)71 std::string GetAnyValueS(reflection::BaseType type, const uint8_t *data,
72                          const reflection::Schema *schema, int type_index) {
73   switch (type) {
74     case reflection::Float:
75     case reflection::Double: return NumToString(GetAnyValueF(type, data));
76     case reflection::String: {
77       auto s =
78           reinterpret_cast<const String *>(ReadScalar<uoffset_t>(data) + data);
79       return s ? s->c_str() : "";
80     }
81     case reflection::Obj:
82       if (schema) {
83         // Convert the table to a string. This is mostly for debugging purposes,
84         // and does NOT promise to be JSON compliant.
85         // Also prefixes the type.
86         auto &objectdef = *schema->objects()->Get(type_index);
87         auto s = objectdef.name()->str();
88         if (objectdef.is_struct()) {
89           s += "(struct)";  // TODO: implement this as well.
90         } else {
91           auto table_field = reinterpret_cast<const Table *>(
92               ReadScalar<uoffset_t>(data) + data);
93           s += " { ";
94           auto fielddefs = objectdef.fields();
95           for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
96             auto &fielddef = **it;
97             if (!table_field->CheckField(fielddef.offset())) continue;
98             auto val = GetAnyFieldS(*table_field, fielddef, schema);
99             if (fielddef.type()->base_type() == reflection::String) {
100               std::string esc;
101               flatbuffers::EscapeString(val.c_str(), val.length(), &esc, true,
102                                         false);
103               val = esc;
104             }
105             s += fielddef.name()->str();
106             s += ": ";
107             s += val;
108             s += ", ";
109           }
110           s += "}";
111         }
112         return s;
113       } else {
114         return "(table)";
115       }
116     case reflection::Vector:
117       return "[(elements)]";                   // TODO: implement this as well.
118     case reflection::Union: return "(union)";  // TODO: implement this as well.
119     default: return NumToString(GetAnyValueI(type, data));
120   }
121 }
122 
SetAnyValueI(reflection::BaseType type,uint8_t * data,int64_t val)123 void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val) {
124   // clang-format off
125   #define FLATBUFFERS_SET(T) WriteScalar(data, static_cast<T>(val))
126   switch (type) {
127     case reflection::UType:
128     case reflection::Bool:
129     case reflection::UByte:  FLATBUFFERS_SET(uint8_t ); break;
130     case reflection::Byte:   FLATBUFFERS_SET(int8_t  ); break;
131     case reflection::Short:  FLATBUFFERS_SET(int16_t ); break;
132     case reflection::UShort: FLATBUFFERS_SET(uint16_t); break;
133     case reflection::Int:    FLATBUFFERS_SET(int32_t ); break;
134     case reflection::UInt:   FLATBUFFERS_SET(uint32_t); break;
135     case reflection::Long:   FLATBUFFERS_SET(int64_t ); break;
136     case reflection::ULong:  FLATBUFFERS_SET(uint64_t); break;
137     case reflection::Float:  FLATBUFFERS_SET(float   ); break;
138     case reflection::Double: FLATBUFFERS_SET(double  ); break;
139     // TODO: support strings
140     default: break;
141   }
142   #undef FLATBUFFERS_SET
143   // clang-format on
144 }
145 
SetAnyValueF(reflection::BaseType type,uint8_t * data,double val)146 void SetAnyValueF(reflection::BaseType type, uint8_t *data, double val) {
147   switch (type) {
148     case reflection::Float: WriteScalar(data, static_cast<float>(val)); break;
149     case reflection::Double: WriteScalar(data, val); break;
150     // TODO: support strings.
151     default: SetAnyValueI(type, data, static_cast<int64_t>(val)); break;
152   }
153 }
154 
SetAnyValueS(reflection::BaseType type,uint8_t * data,const char * val)155 void SetAnyValueS(reflection::BaseType type, uint8_t *data, const char *val) {
156   switch (type) {
157     case reflection::Float:
158     case reflection::Double: {
159       double d;
160       StringToNumber(val, &d);
161       SetAnyValueF(type, data, d);
162       break;
163     }
164     // TODO: support strings.
165     default: SetAnyValueI(type, data, StringToInt(val)); break;
166   }
167 }
168 
169 // Resize a FlatBuffer in-place by iterating through all offsets in the buffer
170 // and adjusting them by "delta" if they straddle the start offset.
171 // Once that is done, bytes can now be inserted/deleted safely.
172 // "delta" may be negative (shrinking).
173 // Unless "delta" is a multiple of the largest alignment, you'll create a small
174 // amount of garbage space in the buffer (usually 0..7 bytes).
175 // If your FlatBuffer's root table is not the schema's root table, you should
176 // pass in your root_table type as well.
177 class ResizeContext {
178  public:
ResizeContext(const reflection::Schema & schema,uoffset_t start,int delta,std::vector<uint8_t> * flatbuf,const reflection::Object * root_table=nullptr)179   ResizeContext(const reflection::Schema &schema, uoffset_t start, int delta,
180                 std::vector<uint8_t> *flatbuf,
181                 const reflection::Object *root_table = nullptr)
182       : schema_(schema),
183         startptr_(vector_data(*flatbuf) + start),
184         delta_(delta),
185         buf_(*flatbuf),
186         dag_check_(flatbuf->size() / sizeof(uoffset_t), false) {
187     auto mask = static_cast<int>(sizeof(largest_scalar_t) - 1);
188     delta_ = (delta_ + mask) & ~mask;
189     if (!delta_) return;  // We can't shrink by less than largest_scalar_t.
190     // Now change all the offsets by delta_.
191     auto root = GetAnyRoot(vector_data(buf_));
192     Straddle<uoffset_t, 1>(vector_data(buf_), root, vector_data(buf_));
193     ResizeTable(root_table ? *root_table : *schema.root_table(), root);
194     // We can now add or remove bytes at start.
195     if (delta_ > 0)
196       buf_.insert(buf_.begin() + start, delta_, 0);
197     else
198       buf_.erase(buf_.begin() + start, buf_.begin() + start - delta_);
199   }
200 
201   // Check if the range between first (lower address) and second straddles
202   // the insertion point. If it does, change the offset at offsetloc (of
203   // type T, with direction D).
204   template<typename T, int D>
Straddle(const void * first,const void * second,void * offsetloc)205   void Straddle(const void *first, const void *second, void *offsetloc) {
206     if (first <= startptr_ && second >= startptr_) {
207       WriteScalar<T>(offsetloc, ReadScalar<T>(offsetloc) + delta_ * D);
208       DagCheck(offsetloc) = true;
209     }
210   }
211 
212   // This returns a boolean that records if the corresponding offset location
213   // has been modified already. If so, we can't even read the corresponding
214   // offset, since it is pointing to a location that is illegal until the
215   // resize actually happens.
216   // This must be checked for every offset, since we can't know which offsets
217   // will straddle and which won't.
DagCheck(const void * offsetloc)218   uint8_t &DagCheck(const void *offsetloc) {
219     auto dag_idx = reinterpret_cast<const uoffset_t *>(offsetloc) -
220                    reinterpret_cast<const uoffset_t *>(vector_data(buf_));
221     return dag_check_[dag_idx];
222   }
223 
ResizeTable(const reflection::Object & objectdef,Table * table)224   void ResizeTable(const reflection::Object &objectdef, Table *table) {
225     if (DagCheck(table)) return;  // Table already visited.
226     auto vtable = table->GetVTable();
227     // Early out: since all fields inside the table must point forwards in
228     // memory, if the insertion point is before the table we can stop here.
229     auto tableloc = reinterpret_cast<uint8_t *>(table);
230     if (startptr_ <= tableloc) {
231       // Check if insertion point is between the table and a vtable that
232       // precedes it. This can't happen in current construction code, but check
233       // just in case we ever change the way flatbuffers are built.
234       Straddle<soffset_t, -1>(vtable, table, table);
235     } else {
236       // Check each field.
237       auto fielddefs = objectdef.fields();
238       for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
239         auto &fielddef = **it;
240         auto base_type = fielddef.type()->base_type();
241         // Ignore scalars.
242         if (base_type <= reflection::Double) continue;
243         // Ignore fields that are not stored.
244         auto offset = table->GetOptionalFieldOffset(fielddef.offset());
245         if (!offset) continue;
246         // Ignore structs.
247         auto subobjectdef =
248             base_type == reflection::Obj
249                 ? schema_.objects()->Get(fielddef.type()->index())
250                 : nullptr;
251         if (subobjectdef && subobjectdef->is_struct()) continue;
252         // Get this fields' offset, and read it if safe.
253         auto offsetloc = tableloc + offset;
254         if (DagCheck(offsetloc)) continue;  // This offset already visited.
255         auto ref = offsetloc + ReadScalar<uoffset_t>(offsetloc);
256         Straddle<uoffset_t, 1>(offsetloc, ref, offsetloc);
257         // Recurse.
258         switch (base_type) {
259           case reflection::Obj: {
260             ResizeTable(*subobjectdef, reinterpret_cast<Table *>(ref));
261             break;
262           }
263           case reflection::Vector: {
264             auto elem_type = fielddef.type()->element();
265             if (elem_type != reflection::Obj && elem_type != reflection::String)
266               break;
267             auto vec = reinterpret_cast<Vector<uoffset_t> *>(ref);
268             auto elemobjectdef =
269                 elem_type == reflection::Obj
270                     ? schema_.objects()->Get(fielddef.type()->index())
271                     : nullptr;
272             if (elemobjectdef && elemobjectdef->is_struct()) break;
273             for (uoffset_t i = 0; i < vec->size(); i++) {
274               auto loc = vec->Data() + i * sizeof(uoffset_t);
275               if (DagCheck(loc)) continue;  // This offset already visited.
276               auto dest = loc + vec->Get(i);
277               Straddle<uoffset_t, 1>(loc, dest, loc);
278               if (elemobjectdef)
279                 ResizeTable(*elemobjectdef, reinterpret_cast<Table *>(dest));
280             }
281             break;
282           }
283           case reflection::Union: {
284             ResizeTable(GetUnionType(schema_, objectdef, fielddef, *table),
285                         reinterpret_cast<Table *>(ref));
286             break;
287           }
288           case reflection::String: break;
289           default: FLATBUFFERS_ASSERT(false);
290         }
291       }
292       // Check if the vtable offset points beyond the insertion point.
293       // Must do this last, since GetOptionalFieldOffset above still reads
294       // this value.
295       Straddle<soffset_t, -1>(table, vtable, table);
296     }
297   }
298 
299   void operator=(const ResizeContext &rc);
300 
301  private:
302   const reflection::Schema &schema_;
303   uint8_t *startptr_;
304   int delta_;
305   std::vector<uint8_t> &buf_;
306   std::vector<uint8_t> dag_check_;
307 };
308 
SetString(const reflection::Schema & schema,const std::string & val,const String * str,std::vector<uint8_t> * flatbuf,const reflection::Object * root_table)309 void SetString(const reflection::Schema &schema, const std::string &val,
310                const String *str, std::vector<uint8_t> *flatbuf,
311                const reflection::Object *root_table) {
312   auto delta = static_cast<int>(val.size()) - static_cast<int>(str->size());
313   auto str_start = static_cast<uoffset_t>(
314       reinterpret_cast<const uint8_t *>(str) - vector_data(*flatbuf));
315   auto start = str_start + static_cast<uoffset_t>(sizeof(uoffset_t));
316   if (delta) {
317     // Clear the old string, since we don't want parts of it remaining.
318     memset(vector_data(*flatbuf) + start, 0, str->size());
319     // Different size, we must expand (or contract).
320     ResizeContext(schema, start, delta, flatbuf, root_table);
321     // Set the new length.
322     WriteScalar(vector_data(*flatbuf) + str_start,
323                 static_cast<uoffset_t>(val.size()));
324   }
325   // Copy new data. Safe because we created the right amount of space.
326   memcpy(vector_data(*flatbuf) + start, val.c_str(), val.size() + 1);
327 }
328 
ResizeAnyVector(const reflection::Schema & schema,uoffset_t newsize,const VectorOfAny * vec,uoffset_t num_elems,uoffset_t elem_size,std::vector<uint8_t> * flatbuf,const reflection::Object * root_table)329 uint8_t *ResizeAnyVector(const reflection::Schema &schema, uoffset_t newsize,
330                          const VectorOfAny *vec, uoffset_t num_elems,
331                          uoffset_t elem_size, std::vector<uint8_t> *flatbuf,
332                          const reflection::Object *root_table) {
333   auto delta_elem = static_cast<int>(newsize) - static_cast<int>(num_elems);
334   auto delta_bytes = delta_elem * static_cast<int>(elem_size);
335   auto vec_start =
336       reinterpret_cast<const uint8_t *>(vec) - vector_data(*flatbuf);
337   auto start = static_cast<uoffset_t>(vec_start + sizeof(uoffset_t) +
338                                       elem_size * num_elems);
339   if (delta_bytes) {
340     if (delta_elem < 0) {
341       // Clear elements we're throwing away, since some might remain in the
342       // buffer.
343       auto size_clear = -delta_elem * elem_size;
344       memset(vector_data(*flatbuf) + start - size_clear, 0, size_clear);
345     }
346     ResizeContext(schema, start, delta_bytes, flatbuf, root_table);
347     WriteScalar(vector_data(*flatbuf) + vec_start, newsize);  // Length field.
348     // Set new elements to 0.. this can be overwritten by the caller.
349     if (delta_elem > 0) {
350       memset(vector_data(*flatbuf) + start, 0, delta_elem * elem_size);
351     }
352   }
353   return vector_data(*flatbuf) + start;
354 }
355 
AddFlatBuffer(std::vector<uint8_t> & flatbuf,const uint8_t * newbuf,size_t newlen)356 const uint8_t *AddFlatBuffer(std::vector<uint8_t> &flatbuf,
357                              const uint8_t *newbuf, size_t newlen) {
358   // Align to sizeof(uoffset_t) past sizeof(largest_scalar_t) since we're
359   // going to chop off the root offset.
360   while ((flatbuf.size() & (sizeof(uoffset_t) - 1)) ||
361          !(flatbuf.size() & (sizeof(largest_scalar_t) - 1))) {
362     flatbuf.push_back(0);
363   }
364   auto insertion_point = static_cast<uoffset_t>(flatbuf.size());
365   // Insert the entire FlatBuffer minus the root pointer.
366   flatbuf.insert(flatbuf.end(), newbuf + sizeof(uoffset_t), newbuf + newlen);
367   auto root_offset = ReadScalar<uoffset_t>(newbuf) - sizeof(uoffset_t);
368   return vector_data(flatbuf) + insertion_point + root_offset;
369 }
370 
CopyInline(FlatBufferBuilder & fbb,const reflection::Field & fielddef,const Table & table,size_t align,size_t size)371 void CopyInline(FlatBufferBuilder &fbb, const reflection::Field &fielddef,
372                 const Table &table, size_t align, size_t size) {
373   fbb.Align(align);
374   fbb.PushBytes(table.GetStruct<const uint8_t *>(fielddef.offset()), size);
375   fbb.TrackField(fielddef.offset(), fbb.GetSize());
376 }
377 
CopyTable(FlatBufferBuilder & fbb,const reflection::Schema & schema,const reflection::Object & objectdef,const Table & table,bool use_string_pooling)378 Offset<const Table *> CopyTable(FlatBufferBuilder &fbb,
379                                 const reflection::Schema &schema,
380                                 const reflection::Object &objectdef,
381                                 const Table &table, bool use_string_pooling) {
382   // Before we can construct the table, we have to first generate any
383   // subobjects, and collect their offsets.
384   std::vector<uoffset_t> offsets;
385   auto fielddefs = objectdef.fields();
386   for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
387     auto &fielddef = **it;
388     // Skip if field is not present in the source.
389     if (!table.CheckField(fielddef.offset())) continue;
390     uoffset_t offset = 0;
391     switch (fielddef.type()->base_type()) {
392       case reflection::String: {
393         offset = use_string_pooling
394                      ? fbb.CreateSharedString(GetFieldS(table, fielddef)).o
395                      : fbb.CreateString(GetFieldS(table, fielddef)).o;
396         break;
397       }
398       case reflection::Obj: {
399         auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index());
400         if (!subobjectdef.is_struct()) {
401           offset =
402               CopyTable(fbb, schema, subobjectdef, *GetFieldT(table, fielddef))
403                   .o;
404         }
405         break;
406       }
407       case reflection::Union: {
408         auto &subobjectdef = GetUnionType(schema, objectdef, fielddef, table);
409         offset =
410             CopyTable(fbb, schema, subobjectdef, *GetFieldT(table, fielddef)).o;
411         break;
412       }
413       case reflection::Vector: {
414         auto vec =
415             table.GetPointer<const Vector<Offset<Table>> *>(fielddef.offset());
416         auto element_base_type = fielddef.type()->element();
417         auto elemobjectdef =
418             element_base_type == reflection::Obj
419                 ? schema.objects()->Get(fielddef.type()->index())
420                 : nullptr;
421         switch (element_base_type) {
422           case reflection::String: {
423             std::vector<Offset<const String *>> elements(vec->size());
424             auto vec_s = reinterpret_cast<const Vector<Offset<String>> *>(vec);
425             for (uoffset_t i = 0; i < vec_s->size(); i++) {
426               elements[i] = use_string_pooling
427                                 ? fbb.CreateSharedString(vec_s->Get(i)).o
428                                 : fbb.CreateString(vec_s->Get(i)).o;
429             }
430             offset = fbb.CreateVector(elements).o;
431             break;
432           }
433           case reflection::Obj: {
434             if (!elemobjectdef->is_struct()) {
435               std::vector<Offset<const Table *>> elements(vec->size());
436               for (uoffset_t i = 0; i < vec->size(); i++) {
437                 elements[i] =
438                     CopyTable(fbb, schema, *elemobjectdef, *vec->Get(i));
439               }
440               offset = fbb.CreateVector(elements).o;
441               break;
442             }
443           }
444             FLATBUFFERS_FALLTHROUGH();  // fall thru
445           default: {                    // Scalars and structs.
446             auto element_size = GetTypeSize(element_base_type);
447             if (elemobjectdef && elemobjectdef->is_struct())
448               element_size = elemobjectdef->bytesize();
449             fbb.StartVector(vec->size(), element_size);
450             fbb.PushBytes(vec->Data(), element_size * vec->size());
451             offset = fbb.EndVector(vec->size());
452             break;
453           }
454         }
455         break;
456       }
457       default:  // Scalars.
458         break;
459     }
460     if (offset) { offsets.push_back(offset); }
461   }
462   // Now we can build the actual table from either offsets or scalar data.
463   auto start = objectdef.is_struct() ? fbb.StartStruct(objectdef.minalign())
464                                      : fbb.StartTable();
465   size_t offset_idx = 0;
466   for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
467     auto &fielddef = **it;
468     if (!table.CheckField(fielddef.offset())) continue;
469     auto base_type = fielddef.type()->base_type();
470     switch (base_type) {
471       case reflection::Obj: {
472         auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index());
473         if (subobjectdef.is_struct()) {
474           CopyInline(fbb, fielddef, table, subobjectdef.minalign(),
475                      subobjectdef.bytesize());
476           break;
477         }
478       }
479         FLATBUFFERS_FALLTHROUGH();  // fall thru
480       case reflection::Union:
481       case reflection::String:
482       case reflection::Vector:
483         fbb.AddOffset(fielddef.offset(), Offset<void>(offsets[offset_idx++]));
484         break;
485       default: {  // Scalars.
486         auto size = GetTypeSize(base_type);
487         CopyInline(fbb, fielddef, table, size, size);
488         break;
489       }
490     }
491   }
492   FLATBUFFERS_ASSERT(offset_idx == offsets.size());
493   if (objectdef.is_struct()) {
494     fbb.ClearOffsets();
495     return fbb.EndStruct();
496   } else {
497     return fbb.EndTable(start);
498   }
499 }
500 
VerifyStruct(flatbuffers::Verifier & v,const flatbuffers::Table & parent_table,voffset_t field_offset,const reflection::Object & obj,bool required)501 bool VerifyStruct(flatbuffers::Verifier &v,
502                   const flatbuffers::Table &parent_table,
503                   voffset_t field_offset, const reflection::Object &obj,
504                   bool required) {
505   auto offset = parent_table.GetOptionalFieldOffset(field_offset);
506   if (required && !offset) { return false; }
507 
508   return !offset || v.Verify(reinterpret_cast<const uint8_t *>(&parent_table),
509                              offset, obj.bytesize());
510 }
511 
VerifyVectorOfStructs(flatbuffers::Verifier & v,const flatbuffers::Table & parent_table,voffset_t field_offset,const reflection::Object & obj,bool required)512 bool VerifyVectorOfStructs(flatbuffers::Verifier &v,
513                            const flatbuffers::Table &parent_table,
514                            voffset_t field_offset,
515                            const reflection::Object &obj, bool required) {
516   auto p = parent_table.GetPointer<const uint8_t *>(field_offset);
517   if (required && !p) { return false; }
518 
519   return !p || v.VerifyVectorOrString(p, obj.bytesize());
520 }
521 
522 // forward declare to resolve cyclic deps between VerifyObject and VerifyVector
523 bool VerifyObject(flatbuffers::Verifier &v, const reflection::Schema &schema,
524                   const reflection::Object &obj,
525                   const flatbuffers::Table *table, bool required);
526 
VerifyUnion(flatbuffers::Verifier & v,const reflection::Schema & schema,uint8_t utype,const uint8_t * elem,const reflection::Field & union_field)527 bool VerifyUnion(flatbuffers::Verifier &v, const reflection::Schema &schema,
528                  uint8_t utype, const uint8_t *elem,
529                  const reflection::Field &union_field) {
530   if (!utype) return true;  // Not present.
531   auto fb_enum = schema.enums()->Get(union_field.type()->index());
532   if (utype >= fb_enum->values()->size()) return false;
533   auto elem_type = fb_enum->values()->Get(utype)->union_type();
534   switch (elem_type->base_type()) {
535     case reflection::Obj: {
536       auto elem_obj = schema.objects()->Get(elem_type->index());
537       if (elem_obj->is_struct()) {
538         return v.VerifyFromPointer(elem, elem_obj->bytesize());
539       } else {
540         return VerifyObject(v, schema, *elem_obj,
541                             reinterpret_cast<const flatbuffers::Table *>(elem),
542                             true);
543       }
544     }
545     case reflection::String:
546       return v.VerifyString(
547           reinterpret_cast<const flatbuffers::String *>(elem));
548     default: return false;
549   }
550 }
551 
VerifyVector(flatbuffers::Verifier & v,const reflection::Schema & schema,const flatbuffers::Table & table,const reflection::Field & vec_field)552 bool VerifyVector(flatbuffers::Verifier &v, const reflection::Schema &schema,
553                   const flatbuffers::Table &table,
554                   const reflection::Field &vec_field) {
555   FLATBUFFERS_ASSERT(vec_field.type()->base_type() == reflection::Vector);
556   if (!table.VerifyField<uoffset_t>(v, vec_field.offset())) return false;
557 
558   switch (vec_field.type()->element()) {
559     case reflection::UType:
560       return v.VerifyVector(flatbuffers::GetFieldV<uint8_t>(table, vec_field));
561     case reflection::Bool:
562     case reflection::Byte:
563     case reflection::UByte:
564       return v.VerifyVector(flatbuffers::GetFieldV<int8_t>(table, vec_field));
565     case reflection::Short:
566     case reflection::UShort:
567       return v.VerifyVector(flatbuffers::GetFieldV<int16_t>(table, vec_field));
568     case reflection::Int:
569     case reflection::UInt:
570       return v.VerifyVector(flatbuffers::GetFieldV<int32_t>(table, vec_field));
571     case reflection::Long:
572     case reflection::ULong:
573       return v.VerifyVector(flatbuffers::GetFieldV<int64_t>(table, vec_field));
574     case reflection::Float:
575       return v.VerifyVector(flatbuffers::GetFieldV<float>(table, vec_field));
576     case reflection::Double:
577       return v.VerifyVector(flatbuffers::GetFieldV<double>(table, vec_field));
578     case reflection::String: {
579       auto vec_string =
580           flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::String>>(
581               table, vec_field);
582       if (v.VerifyVector(vec_string) && v.VerifyVectorOfStrings(vec_string)) {
583         return true;
584       } else {
585         return false;
586       }
587     }
588     case reflection::Obj: {
589       auto obj = schema.objects()->Get(vec_field.type()->index());
590       if (obj->is_struct()) {
591         return VerifyVectorOfStructs(v, table, vec_field.offset(), *obj,
592                                      vec_field.required());
593       } else {
594         auto vec =
595             flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::Table>>(
596                 table, vec_field);
597         if (!v.VerifyVector(vec)) return false;
598         if (!vec) return true;
599         for (uoffset_t j = 0; j < vec->size(); j++) {
600           if (!VerifyObject(v, schema, *obj, vec->Get(j), true)) {
601             return false;
602           }
603         }
604         return true;
605       }
606     }
607     case reflection::Union: {
608       auto vec = flatbuffers::GetFieldV<flatbuffers::Offset<uint8_t>>(
609           table, vec_field);
610       if (!v.VerifyVector(vec)) return false;
611       if (!vec) return true;
612       auto type_vec = table.GetPointer<Vector<uint8_t> *>(vec_field.offset() -
613                                                           sizeof(voffset_t));
614       if (!v.VerifyVector(type_vec)) return false;
615       for (uoffset_t j = 0; j < vec->size(); j++) {
616         //  get union type from the prev field
617         auto utype = type_vec->Get(j);
618         auto elem = vec->Get(j);
619         if (!VerifyUnion(v, schema, utype, elem, vec_field)) return false;
620       }
621       return true;
622     }
623     case reflection::Vector:
624     case reflection::None:
625     default: FLATBUFFERS_ASSERT(false); return false;
626   }
627 }
628 
VerifyObject(flatbuffers::Verifier & v,const reflection::Schema & schema,const reflection::Object & obj,const flatbuffers::Table * table,bool required)629 bool VerifyObject(flatbuffers::Verifier &v, const reflection::Schema &schema,
630                   const reflection::Object &obj,
631                   const flatbuffers::Table *table, bool required) {
632   if (!table) return !required;
633   if (!table->VerifyTableStart(v)) return false;
634   for (uoffset_t i = 0; i < obj.fields()->size(); i++) {
635     auto field_def = obj.fields()->Get(i);
636     switch (field_def->type()->base_type()) {
637       case reflection::None: FLATBUFFERS_ASSERT(false); break;
638       case reflection::UType:
639         if (!table->VerifyField<uint8_t>(v, field_def->offset())) return false;
640         break;
641       case reflection::Bool:
642       case reflection::Byte:
643       case reflection::UByte:
644         if (!table->VerifyField<int8_t>(v, field_def->offset())) return false;
645         break;
646       case reflection::Short:
647       case reflection::UShort:
648         if (!table->VerifyField<int16_t>(v, field_def->offset())) return false;
649         break;
650       case reflection::Int:
651       case reflection::UInt:
652         if (!table->VerifyField<int32_t>(v, field_def->offset())) return false;
653         break;
654       case reflection::Long:
655       case reflection::ULong:
656         if (!table->VerifyField<int64_t>(v, field_def->offset())) return false;
657         break;
658       case reflection::Float:
659         if (!table->VerifyField<float>(v, field_def->offset())) return false;
660         break;
661       case reflection::Double:
662         if (!table->VerifyField<double>(v, field_def->offset())) return false;
663         break;
664       case reflection::String:
665         if (!table->VerifyField<uoffset_t>(v, field_def->offset()) ||
666             !v.VerifyString(flatbuffers::GetFieldS(*table, *field_def))) {
667           return false;
668         }
669         break;
670       case reflection::Vector:
671         if (!VerifyVector(v, schema, *table, *field_def)) return false;
672         break;
673       case reflection::Obj: {
674         auto child_obj = schema.objects()->Get(field_def->type()->index());
675         if (child_obj->is_struct()) {
676           if (!VerifyStruct(v, *table, field_def->offset(), *child_obj,
677                             field_def->required())) {
678             return false;
679           }
680         } else {
681           if (!VerifyObject(v, schema, *child_obj,
682                             flatbuffers::GetFieldT(*table, *field_def),
683                             field_def->required())) {
684             return false;
685           }
686         }
687         break;
688       }
689       case reflection::Union: {
690         //  get union type from the prev field
691         voffset_t utype_offset = field_def->offset() - sizeof(voffset_t);
692         auto utype = table->GetField<uint8_t>(utype_offset, 0);
693         auto uval = reinterpret_cast<const uint8_t *>(
694             flatbuffers::GetFieldT(*table, *field_def));
695         if (!VerifyUnion(v, schema, utype, uval, *field_def)) { return false; }
696         break;
697       }
698       default: FLATBUFFERS_ASSERT(false); break;
699     }
700   }
701 
702   if (!v.EndTable()) return false;
703 
704   return true;
705 }
706 
Verify(const reflection::Schema & schema,const reflection::Object & root,const uint8_t * buf,size_t length)707 bool Verify(const reflection::Schema &schema, const reflection::Object &root,
708             const uint8_t *buf, size_t length) {
709   Verifier v(buf, length);
710   return VerifyObject(v, schema, root, flatbuffers::GetAnyRoot(buf), true);
711 }
712 
713 }  // namespace flatbuffers
714