Add buffer verification functionality to FlatBuffers
authorWouter van Oortmerssen <wvo@google.com>
Fri, 27 Jun 2014 23:44:53 +0000 (16:44 -0700)
committerWouter van Oortmerssen <wvo@google.com>
Wed, 2 Jul 2014 00:52:16 +0000 (17:52 -0700)
Bug: 15732628
Change-Id: I0b7cb65982d6b8957d5a899cca7d2b5d2ef53206
Tested: On Windows, OS X and Linux

docs/source/CppUsage.md
include/flatbuffers/flatbuffers.h
src/idl_gen_cpp.cpp
tests/MyGame/Example/Monster.java
tests/monster_test.fbs
tests/monster_test_generated.h
tests/monsterdata_test_wire.bin
tests/test.cpp

index f55dd95..5fe14cd 100755 (executable)
@@ -47,7 +47,7 @@ we can construct them in a familiar way.
 We have now serialized the non-scalar components of of the monster
 example, so we could create the monster something like this:
 
-    auto mloc = CreateMonster(fbb, &vec, 150, 80, name, inventory, Color_Red, Offset<void>(0), Any_NONE);
+    auto mloc = CreateMonster(fbb, &vec, 150, 80, name, inventory, Color_Red, 0, Any_NONE);
 
 Note that we're passing `150` for the `mana` field, which happens to be the
 default value: this means the field will not actually be written to the buffer,
@@ -58,7 +58,8 @@ since they won't bloat up the buffer sizes if they're not actually used.
 
 We do something similarly for the union field `test` by specifying a `0` offset
 and the `NONE` enum value (part of every union) to indicate we don't actually
-want to write this field.
+want to write this field. You can use `0` also as a default for other
+non-scalar types, such as strings, vectors and tables.
 
 Tables (like `Monster`) give you full flexibility on what fields you write
 (unlike `Vec3`, which always has all fields set because it is a `struct`).
@@ -155,6 +156,38 @@ machines, so only use tricks like this if you can guarantee you're not
 shipping on a big endian machine (an `assert(FLATBUFFERS_LITTLEENDIAN)`
 would be wise).
 
+### Access of untrusted buffers
+
+The generated accessor functions access fields over offsets, which is
+very quick. These offsets are not verified at run-time, so a malformed
+buffer could cause a program to crash by accessing random memory.
+
+When you're processing large amounts of data from a source you know (e.g.
+your own generated data on disk), this is acceptable, but when reading
+data from the network that can potentially have been modified by an
+attacker, this is undesirable.
+
+For this reason, you can optionally use a buffer verifier before you
+access the data. This verifier will check all offsets, all sizes of
+fields, and null termination of strings to ensure that when a buffer
+is accessed, all reads will end up inside the buffer.
+
+Each root type will have a verification function generated for it,
+e.g. for `Monster`, you can call:
+
+       bool ok = VerifyMonsterBuffer(Verifier(buf, len));
+
+if `ok` is true, the buffer is safe to read.
+
+Besides untrusted data, this function may be useful to call in debug
+mode, as extra insurance against data being corrupted somewhere along
+the way.
+
+While verifying a buffer isn't "free", it is typically faster than
+a full traversal (since any scalar data is not actually touched),
+and since it may cause the buffer to be brought into cache before
+reading, the actual overhead may be even lower than expected.
+
 ## Text & schema parsing
 
 Using binary buffers with the generated header provides a super low
index 47acdf3..5b3c9f1 100644 (file)
@@ -76,7 +76,7 @@ typedef uintmax_t largest_scalar_t;
 template<typename T> struct Offset {
   uoffset_t o;
   Offset() : o(0) {}
-  explicit Offset(uoffset_t _o) : o(_o) {}
+  Offset(uoffset_t _o) : o(_o) {}
   Offset<void> Union() const { return Offset<void>(o); }
 };
 
@@ -407,13 +407,13 @@ class FlatBufferBuilder {
     buf_.fill(numfields * sizeof(voffset_t));
     auto table_object_size = vtableoffsetloc - start;
     assert(table_object_size < 0x10000);  // Vtable use 16bit offsets.
-    PushElement<voffset_t>(table_object_size);
+    PushElement<voffset_t>(static_cast<voffset_t>(table_object_size));
     PushElement<voffset_t>(FieldIndexToOffset(numfields));
     // Write the offsets into the table
     for (auto field_location = offsetbuf_.begin();
               field_location != offsetbuf_.end();
             ++field_location) {
-      auto pos = (vtableoffsetloc - field_location->off);
+      auto pos = static_cast<voffset_t>(vtableoffsetloc - field_location->off);
       // If this asserts, it means you've set a field twice.
       assert(!ReadScalar<voffset_t>(buf_.data() + field_location->id));
       WriteScalar<voffset_t>(buf_.data() + field_location->id, pos);
@@ -563,7 +563,90 @@ template<typename T> const T *GetRoot(const void *buf) {
     EndianScalar(*reinterpret_cast<const uoffset_t *>(buf)));
 }
 
-// "structs_" are flat structures that do not have an offset table, thus
+// Helper class to verify the integrity of a FlatBuffer
+class Verifier {
+ public:
+  Verifier(const uint8_t *buf, size_t buf_len)
+    : buf_(buf), end_(buf + buf_len)
+    {}
+
+  // Verify any range within the buffer.
+  bool Verify(const void *elem, size_t elem_len) const {
+    bool ok = elem >= buf_ && elem <= end_ - elem_len;
+    assert(ok);
+    return ok;
+  }
+
+  // Verify a range indicated by sizeof(T).
+  template<typename T> bool Verify(const void *elem) const {
+    return Verify(elem, sizeof(T));
+  }
+
+  // Verify a pointer (may be NULL) of any vector type.
+  template<typename T> bool Verify(const Vector<T> *vec) const {
+    const uint8_t *end;
+    return !vec ||
+           VerifyVector(reinterpret_cast<const uint8_t *>(vec), sizeof(T),
+                        &end);
+  }
+
+  // Verify a pointer (may be NULL) to string.
+  bool Verify(const String *str) const {
+    const uint8_t *end;
+    return !str ||
+           (VerifyVector(reinterpret_cast<const uint8_t *>(str), 1, &end) &&
+            Verify(end, 1) &&  // Must have terminator
+            *end == '\0');  // Terminating byte must be 0.
+  }
+
+  // Common code between vectors and strings.
+  bool VerifyVector(const uint8_t *vec, size_t elem_size,
+                    const uint8_t **end) const {
+    // Check we can read the size field.
+    if (!Verify<uoffset_t>(vec)) return false;
+    // Check the whole array. If this is a string, the byte past the array
+    // must be 0.
+    auto size = ReadScalar<uoffset_t>(vec);
+    auto byte_size = sizeof(size) + elem_size * size;
+    *end = vec + byte_size;
+    return Verify(vec, byte_size);
+  }
+
+  // Special case for string contents, after the above has been called.
+  bool VerifyVectorOfStrings(const Vector<Offset<String>> *vec) const {
+      if (vec) {
+        for (uoffset_t i = 0; i < vec->Length(); i++) {
+          if (!Verify(vec->Get(i))) return false;
+        }
+      }
+      return true;
+  }
+
+  // Special case for table contents, after the above has been called.
+  template<typename T> bool VerifyVectorOfTables(const Vector<Offset<T>> *vec)
+      const {
+    if (vec) {
+      for (uoffset_t i = 0; i < vec->Length(); i++) {
+        if (!vec->Get(i)->Verify(*this)) return false;
+      }
+    }
+    return true;
+  }
+
+  // Verify this whole buffer, starting with root type T.
+  template<typename T> bool VerifyBuffer() const {
+    // Call T::Verify, which must be in the generated code for this type.
+    return Verify<uoffset_t>(buf_) &&
+      reinterpret_cast<const T *>(buf_ + ReadScalar<uoffset_t>(buf_))->
+        Verify(*this);
+  }
+
+ private:
+  const uint8_t *buf_;
+  const uint8_t *end_;
+};
+
+// "structs" are flat structures that do not have an offset table, thus
 // always have all members present and do not support forwards/backwards
 // compatible extensions.
 
@@ -594,7 +677,7 @@ class Table {
   // if the field was not present.
   voffset_t GetOptionalFieldOffset(voffset_t field) const {
     // The vtable offset is always at the start.
-    auto vtable = &data_ - ReadScalar<soffset_t>(&data_);
+    auto vtable = data_ - ReadScalar<soffset_t>(data_);
     // The first element is the size of the vtable (fields + type id + itself).
     auto vtsize = ReadScalar<voffset_t>(vtable);
     // If the field we're accessing is outside the vtable, we're reading older
@@ -604,12 +687,12 @@ class Table {
 
   template<typename T> T GetField(voffset_t field, T defaultval) const {
     auto field_offset = GetOptionalFieldOffset(field);
-    return field_offset ? ReadScalar<T>(&data_[field_offset]) : defaultval;
+    return field_offset ? ReadScalar<T>(data_ + field_offset) : defaultval;
   }
 
   template<typename P> P GetPointer(voffset_t field) const {
     auto field_offset = GetOptionalFieldOffset(field);
-    auto p = &data_[field_offset];
+    auto p = data_ + field_offset;
     return field_offset
       ? reinterpret_cast<P>(p + ReadScalar<uoffset_t>(p))
       : nullptr;
@@ -617,7 +700,7 @@ class Table {
 
   template<typename P> P GetStruct(voffset_t field) const {
     auto field_offset = GetOptionalFieldOffset(field);
-    return field_offset ? reinterpret_cast<P>(&data_[field_offset]) : nullptr;
+    return field_offset ? reinterpret_cast<P>(data_ + field_offset) : nullptr;
   }
 
   template<typename T> void SetField(voffset_t field, T val) {
@@ -626,18 +709,39 @@ class Table {
     // (or should we return a bool instead?).
     // check if it exists first using CheckField()
     assert(field_offset);
-    WriteScalar(&data_[field_offset], val);
+    WriteScalar(data_ + field_offset, val);
   }
 
   bool CheckField(voffset_t field) const {
     return GetOptionalFieldOffset(field) != 0;
   }
 
+  // Verify the vtable of this table.
+  // Call this once per table, followed by VerifyField once per field.
+  bool VerifyTable(const Verifier &verifier) const {
+    // Check the vtable offset.
+    if (!verifier.Verify<soffset_t>(data_)) return false;
+    auto vtable = data_ - ReadScalar<soffset_t>(data_);
+    // Check the vtable size field, then check vtable fits in its entirety.
+    return verifier.Verify<voffset_t>(vtable) &&
+           verifier.Verify(vtable, ReadScalar<voffset_t>(vtable));
+  }
+
+  // Verify a particular field.
+  template<typename T> bool VerifyField(const Verifier &verifier,
+                                        voffset_t field) const {
+    // Calling GetOptionalFieldOffset should be safe now thanks to
+    // VerifyTable().
+    auto field_offset = GetOptionalFieldOffset(field);
+    // Check the actual field.
+    return !field_offset || verifier.Verify<T>(data_ + field_offset);
+  }
+
  private:
   // private constructor & copy constructor: you obtain instances of this
   // class by pointing to existing data only
-  Table() {};
-  Table(const Table &other) {};
+  Table();
+  Table(const Table &other);
 
   uint8_t data_[1];
 };
@@ -645,10 +749,10 @@ class Table {
 // Utility function for reverse lookups on the EnumNames*() functions
 // (in the generated C++ code)
 // names must be NULL terminated.
-inline size_t LookupEnum(const char **names, const char *name) {
+inline int LookupEnum(const char **names, const char *name) {
   for (const char **p = names; *p; p++)
     if (!strcmp(*p, name))
-      return p - names;
+      return static_cast<int>(p - names);
   return -1;
 }
 
index e0b2be6..bba15af 100644 (file)
@@ -62,6 +62,16 @@ static std::string GenTypeWire(const Type &type, const char *postfix) {
       : "flatbuffers::Offset<" + GenTypePointer(type) + ">" + postfix;
 }
 
+// Return a C++ type for any type (scalar/pointer) that reflects its
+// serialized size.
+static std::string GenTypeSize(const Type &type) {
+  return IsScalar(type.base_type)
+    ? GenTypeBasic(type)
+    : IsStruct(type)
+      ? GenTypePointer(type)
+      : "flatbuffers::uoffset_t";
+}
+
 // Return a C++ type for any type (scalar/pointer) specifically for
 // using a flatbuffer.
 static std::string GenTypeGet(const Type &type, const char *afterbasic,
@@ -82,9 +92,11 @@ static void GenComment(const std::string &dc,
 }
 
 // Generate an enum declaration and an enum string lookup table.
-static void GenEnum(EnumDef &enum_def, std::string *code_ptr) {
+  static void GenEnum(EnumDef &enum_def, std::string *code_ptr,
+                                         std::string *code_ptr_post) {
   if (enum_def.generated) return;
   std::string &code = *code_ptr;
+  std::string &code_post = *code_ptr_post;
   GenComment(enum_def.doc_comment, code_ptr);
   code += "enum {\n";
   for (auto it = enum_def.vals.vec.begin();
@@ -123,6 +135,32 @@ static void GenEnum(EnumDef &enum_def, std::string *code_ptr) {
       code += " - " + enum_def.name + "_" + enum_def.vals.vec.front()->name;
     code += "]; }\n\n";
   }
+  
+  if (enum_def.is_union) {
+    // Generate a verifier function for this union that can be called by the
+    // table verifier functions. It uses a switch case to select a specific
+    // verifier function to call, this should be safe even if the union type
+    // has been corrupted, since the verifiers will simply fail when called
+    // on the wrong type.
+    auto signature = "bool Verify" + enum_def.name +
+                     "(const flatbuffers::Verifier &verifier, " +
+                     "const void *union_obj, uint8_t type)";
+    code += signature + ";\n\n";
+    code_post += signature + " {\n  switch (type) {\n";
+    for (auto it = enum_def.vals.vec.begin();
+         it != enum_def.vals.vec.end();
+         ++it) {
+      auto &ev = **it;
+      code_post += "    case " + enum_def.name + "_" + ev.name;
+      if (!ev.value) {
+        code_post += ": return true;\n";  // "NONE" enum value.
+      } else {
+        code_post += ": return reinterpret_cast<const " + ev.struct_def->name;
+        code_post += " *>(union_obj)->Verify(verifier);\n";
+      }
+    }
+    code_post += "    default: return false;\n  }\n}\n\n";
+  }
 }
 
 // Generate an accessor struct, builder structs & function for a table.
@@ -155,6 +193,58 @@ static void GenTable(StructDef &struct_def, std::string *code_ptr) {
       code += "); }\n";
     }
   }
+  // Generate a verifier function that can check a buffer from an untrusted
+  // source will never cause reads outside the buffer.
+  code += "  bool Verify(const flatbuffers::Verifier &verifier) const {\n";
+  code += "    return VerifyTable(verifier)";
+  std::string prefix = " &&\n           ";
+  for (auto it = struct_def.fields.vec.begin();
+       it != struct_def.fields.vec.end();
+       ++it) {
+    auto &field = **it;
+    if (!field.deprecated) {
+      code += prefix + "VerifyField<" + GenTypeSize(field.value.type);
+      code += ">(verifier, " + NumToString(field.value.offset);
+      code += " /* " + field.name + " */)";
+      switch (field.value.type.base_type) {
+        case BASE_TYPE_UNION:
+          code += prefix + "Verify" + field.value.type.enum_def->name;
+          code += "(verifier, " + field.name + "(), " + field.name + "_type())";
+          break;
+        case BASE_TYPE_STRUCT:
+          if (!field.value.type.struct_def->fixed) {
+            code += prefix + field.value.type.struct_def->name;
+            code += "()->Verify()";
+          }
+          break;
+        case BASE_TYPE_STRING:
+          code += prefix + "verifier.Verify(" + field.name + "())";
+          break;
+        case BASE_TYPE_VECTOR:
+          code += prefix + "verifier.Verify(" + field.name + "())";
+          switch (field.value.type.element) {
+            case BASE_TYPE_STRING: {
+              code += prefix + "verifier.VerifyVectorOfStrings(" + field.name;
+              code += "())";
+              break;
+            }
+            case BASE_TYPE_STRUCT: {
+              if (!field.value.type.struct_def->fixed) {
+                code += prefix + "verifier.VerifyVectorOfTables(" + field.name;
+                code += "())";
+              }
+              break;
+            }
+            default:
+              break;
+          }
+          break;
+        default:
+          break;
+      }
+    }
+  }
+  code += ";\n  }\n";
   code += "};\n\n";
 
   // Generate a builder struct, with methods of the form:
@@ -302,10 +392,10 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
   using namespace cpp;
 
   // Generate code for all the enum declarations.
-  std::string enum_code;
+  std::string enum_code, enum_code_post;
   for (auto it = parser.enums_.vec.begin();
        it != parser.enums_.vec.end(); ++it) {
-    GenEnum(**it, &enum_code);
+    GenEnum(**it, &enum_code, &enum_code_post);
   }
 
   // Generate forward declarations for all structs/tables, since they may
@@ -361,13 +451,20 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
     code += forward_decl_code;
     code += "\n";
     code += decl_code;
+    code += enum_code_post;
 
-    // Generate convenient root datatype accessor.
+    // Generate convenient root datatype accessor, and root verifier.
     if (parser.root_struct_def) {
       code += "inline const " + parser.root_struct_def->name + " *Get";
       code += parser.root_struct_def->name;
       code += "(const void *buf) { return flatbuffers::GetRoot<";
       code += parser.root_struct_def->name + ">(buf); }\n\n";
+
+      code += "inline bool Verify";
+      code += parser.root_struct_def->name;
+      code += "Buffer(const flatbuffers::Verifier &verifier) { "
+              "return verifier.VerifyBuffer<";
+      code += parser.root_struct_def->name + ">(); }\n\n";
     }
 
     // Close the namespaces.
index e07d9b8..d9c59bd 100755 (executable)
@@ -24,8 +24,13 @@ public class Monster extends Table {
   public Test test4(int j) { return test4(new Test(), j); }
   public Test test4(Test obj, int j) { int o = __offset(22); return o != 0 ? obj.__init(__vector(o) + j * 4, bb) : null; }
   public int test4Length() { int o = __offset(22); return o != 0 ? __vector_len(o) : 0; }
+  public String testarrayofstring(int j) { int o = __offset(24); return o != 0 ? __string(__vector(o) + j * 4) : null; }
+  public int testarrayofstringLength() { int o = __offset(24); return o != 0 ? __vector_len(o) : 0; }
+  public Monster testarrayoftables(int j) { return testarrayoftables(new Monster(), j); }
+  public Monster testarrayoftables(Monster obj, int j) { int o = __offset(26); return o != 0 ? obj.__init(__indirect(__vector(o) + j * 4), bb) : null; }
+  public int testarrayoftablesLength() { int o = __offset(26); return o != 0 ? __vector_len(o) : 0; }
 
-  public static void startMonster(FlatBufferBuilder builder) { builder.startObject(10); }
+  public static void startMonster(FlatBufferBuilder builder) { builder.startObject(12); }
   public static void addPos(FlatBufferBuilder builder, int pos) { builder.addStruct(0, pos, 0); }
   public static void addMana(FlatBufferBuilder builder, short mana) { builder.addShort(1, mana, 150); }
   public static void addHp(FlatBufferBuilder builder, short hp) { builder.addShort(2, hp, 100); }
@@ -37,6 +42,10 @@ public class Monster extends Table {
   public static void addTest(FlatBufferBuilder builder, int test) { builder.addOffset(8, test, 0); }
   public static void addTest4(FlatBufferBuilder builder, int test4) { builder.addOffset(9, test4, 0); }
   public static void startTest4Vector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems); }
+  public static void addTestarrayofstring(FlatBufferBuilder builder, int testarrayofstring) { builder.addOffset(10, testarrayofstring, 0); }
+  public static void startTestarrayofstringVector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems); }
+  public static void addTestarrayoftables(FlatBufferBuilder builder, int testarrayoftables) { builder.addOffset(11, testarrayoftables, 0); }
+  public static void startTestarrayoftablesVector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems); }
   public static int endMonster(FlatBufferBuilder builder) { return builder.endObject(); }
 };
 
index d4c3a72..d3735e8 100755 (executable)
@@ -29,6 +29,8 @@ table Monster {
   color:Color = Blue;
   test:Any;
   test4:[Test];
+  testarrayofstring:[string];
+  testarrayoftables:[Monster];
 }
 
 root_type Monster;
index 6b85c6b..7601dc9 100755 (executable)
@@ -33,6 +33,8 @@ inline const char **EnumNamesAny() {
 
 inline const char *EnumNameAny(int e) { return EnumNamesAny()[e]; }
 
+bool VerifyAny(const flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type);
+
 struct Test;
 struct Vec3;
 struct Monster;
@@ -88,6 +90,30 @@ struct Monster : private flatbuffers::Table {
   uint8_t test_type() const { return GetField<uint8_t>(18, 0); }
   const void *test() const { return GetPointer<const void *>(20); }
   const flatbuffers::Vector<const Test *> *test4() const { return GetPointer<const flatbuffers::Vector<const Test *> *>(22); }
+  const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *testarrayofstring() const { return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *>(24); }
+  const flatbuffers::Vector<flatbuffers::Offset<Monster>> *testarrayoftables() const { return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<Monster>> *>(26); }
+  bool Verify(const flatbuffers::Verifier &verifier) const {
+    return VerifyTable(verifier) &&
+           VerifyField<Vec3>(verifier, 4 /* pos */) &&
+           VerifyField<int16_t>(verifier, 6 /* mana */) &&
+           VerifyField<int16_t>(verifier, 8 /* hp */) &&
+           VerifyField<flatbuffers::uoffset_t>(verifier, 10 /* name */) &&
+           verifier.Verify(name()) &&
+           VerifyField<flatbuffers::uoffset_t>(verifier, 14 /* inventory */) &&
+           verifier.Verify(inventory()) &&
+           VerifyField<int8_t>(verifier, 16 /* color */) &&
+           VerifyField<uint8_t>(verifier, 18 /* test_type */) &&
+           VerifyField<flatbuffers::uoffset_t>(verifier, 20 /* test */) &&
+           VerifyAny(verifier, test(), test_type()) &&
+           VerifyField<flatbuffers::uoffset_t>(verifier, 22 /* test4 */) &&
+           verifier.Verify(test4()) &&
+           VerifyField<flatbuffers::uoffset_t>(verifier, 24 /* testarrayofstring */) &&
+           verifier.Verify(testarrayofstring()) &&
+           verifier.VerifyVectorOfStrings(testarrayofstring()) &&
+           VerifyField<flatbuffers::uoffset_t>(verifier, 26 /* testarrayoftables */) &&
+           verifier.Verify(testarrayoftables()) &&
+           verifier.VerifyVectorOfTables(testarrayoftables());
+  }
 };
 
 struct MonsterBuilder {
@@ -102,12 +128,17 @@ struct MonsterBuilder {
   void add_test_type(uint8_t test_type) { fbb_.AddElement<uint8_t>(18, test_type, 0); }
   void add_test(flatbuffers::Offset<void> test) { fbb_.AddOffset(20, test); }
   void add_test4(flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4) { fbb_.AddOffset(22, test4); }
+  void add_testarrayofstring(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> testarrayofstring) { fbb_.AddOffset(24, testarrayofstring); }
+  void add_testarrayoftables(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Monster>>> testarrayoftables) { fbb_.AddOffset(26, testarrayoftables); }
   MonsterBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
-  flatbuffers::Offset<Monster> Finish() { return flatbuffers::Offset<Monster>(fbb_.EndTable(start_, 10)); }
+  MonsterBuilder &operator=(const MonsterBuilder &);
+  flatbuffers::Offset<Monster> Finish() { return flatbuffers::Offset<Monster>(fbb_.EndTable(start_, 12)); }
 };
 
-inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const Vec3 *pos, int16_t mana, int16_t hp, flatbuffers::Offset<flatbuffers::String> name, flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory, int8_t color, uint8_t test_type, flatbuffers::Offset<void> test, flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4) {
+inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const Vec3 *pos, int16_t mana, int16_t hp, flatbuffers::Offset<flatbuffers::String> name, flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory, int8_t color, uint8_t test_type, flatbuffers::Offset<void> test, flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4, flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> testarrayofstring, flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Monster>>> testarrayoftables) {
   MonsterBuilder builder_(_fbb);
+  builder_.add_testarrayoftables(testarrayoftables);
+  builder_.add_testarrayofstring(testarrayofstring);
   builder_.add_test4(test4);
   builder_.add_test(test);
   builder_.add_inventory(inventory);
@@ -120,8 +151,18 @@ inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder
   return builder_.Finish();
 }
 
+bool VerifyAny(const flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type) {
+  switch (type) {
+    case Any_NONE: return true;
+    case Any_Monster: return reinterpret_cast<const Monster *>(union_obj)->Verify(verifier);
+    default: return false;
+  }
+}
+
 inline const Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot<Monster>(buf); }
 
+inline bool VerifyMonsterBuffer(const flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<Monster>(); }
+
 };  // namespace MyGame
 };  // namespace Example
 
index 0d37448..9a7b16d 100755 (executable)
Binary files a/tests/monsterdata_test_wire.bin and b/tests/monsterdata_test_wire.bin differ
index 9be9747..fdb8ba3 100644 (file)
@@ -79,10 +79,19 @@ std::string CreateFlatBufferTest() {
   mb.add_hp(20);
   auto mloc2 = mb.Finish();
 
+  // Create an array of strings:
+  flatbuffers::Offset<flatbuffers::String> strings[2];
+  strings[0] = builder.CreateString("bob");
+  strings[1] = builder.CreateString("fred");
+  auto vecofstrings = builder.CreateVector(strings, 2);
+
+  // Create an array of tables:
+  auto vecoftables = builder.CreateVector(&mloc2, 1);
+
   // shortcut for creating monster with all fields set:
   auto mloc = CreateMonster(builder, &vec, 150, 80, name, inventory, Color_Blue,
                             Any_Monster, mloc2.Union(), // Store a union.
-                            testv);
+                            testv, vecofstrings, vecoftables);
 
   builder.Finish(mloc);
 
@@ -101,6 +110,13 @@ std::string CreateFlatBufferTest() {
 //  example of accessing a buffer loaded in memory:
 void AccessFlatBufferTest(const std::string &flatbuf) {
 
+  // First, verify the buffers integrity (optional)
+  flatbuffers::Verifier verifier(
+    reinterpret_cast<const uint8_t *>(flatbuf.c_str()),
+    flatbuf.length());
+  TEST_EQ(VerifyMonsterBuffer(verifier), true);
+
+  // Access the buffer from the root.
   auto monster = GetMonster(flatbuf.c_str());
 
   TEST_EQ(monster->hp(), 80);
@@ -128,11 +144,22 @@ void AccessFlatBufferTest(const std::string &flatbuf) {
   TEST_NOTNULL(monster2);
   TEST_EQ(monster2->hp(), 20);
 
+  // Example of accessing a vector of strings:
+  auto vecofstrings = monster->testarrayofstring();
+  TEST_EQ(vecofstrings->Length(), 2U);
+  TEST_EQ(strcmp(vecofstrings->Get(0)->c_str(), "bob"), 0);
+  TEST_EQ(strcmp(vecofstrings->Get(1)->c_str(), "fred"), 0);
+
+  // Example of accessing a vector of tables:
+  auto vecoftables = monster->testarrayoftables();
+  TEST_EQ(vecoftables->Length(), 1U);
+  TEST_EQ(vecoftables->Get(0)->hp(), 20);
+
   // Since Flatbuffers uses explicit mechanisms to override the default
   // compiler alignment, double check that the compiler indeed obeys them:
   // (Test consists of a short and byte):
-  TEST_EQ(flatbuffers::AlignOf<Test>(), static_cast<size_t>(2));
-  TEST_EQ(sizeof(Test), static_cast<size_t>(4));
+  TEST_EQ(flatbuffers::AlignOf<Test>(), 2UL);
+  TEST_EQ(sizeof(Test), 4UL);
 
   auto tests = monster->test4();
   TEST_NOTNULL(tests);
@@ -163,6 +190,11 @@ void ParseAndGenerateTextTest() {
 
   // here, parser.builder_ contains a binary buffer that is the parsed data.
 
+  // First, verify it, just in case:
+  flatbuffers::Verifier verifier(parser.builder_.GetBufferPointer(),
+                                 parser.builder_.GetSize());
+  TEST_EQ(VerifyMonsterBuffer(verifier), true);
+
   // to ensure it is correct, we now generate text back from the binary,
   // and compare the two:
   std::string jsongen;