and since it may cause the buffer to be brought into cache before
reading, the actual overhead may be even lower than expected.
+In specialized cases where a denial of service attack is possible,
+the verifier has two additional constructor arguments that allow
+you to limit the nesting depth and total amount of tables the
+verifier may encounter before declaring the buffer malformed.
+
## Text & schema parsing
Using binary buffers with the generated header provides a super low
// 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)
+ Verifier(const uint8_t *buf, size_t buf_len, size_t _max_depth = 64,
+ size_t _max_tables = 1000000)
+ : buf_(buf), end_(buf + buf_len), depth_(0), max_depth_(_max_depth),
+ num_tables_(0), max_tables_(_max_tables)
{}
// Verify any range within the buffer.
}
// Verify a pointer (may be NULL) of a table type.
- template<typename T> bool VerifyTable(const T *table) const {
+ template<typename T> bool VerifyTable(const T *table) {
return !table || table->Verify(*this);
}
}
// Special case for table contents, after the above has been called.
- template<typename T> bool VerifyVectorOfTables(const Vector<Offset<T>> *vec)
- const {
+ template<typename T> bool VerifyVectorOfTables(const Vector<Offset<T>> *vec) {
if (vec) {
for (uoffset_t i = 0; i < vec->Length(); i++) {
if (!vec->Get(i)->Verify(*this)) return false;
}
// Verify this whole buffer, starting with root type T.
- template<typename T> bool VerifyBuffer() const {
+ template<typename T> bool VerifyBuffer() {
// 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);
}
+ // Called at the start of a table to increase counters measuring data
+ // structure depth and amount, and possibly bails out with false if
+ // limits set by the constructor have been hit. Needs to be balanced
+ // with EndTable().
+ bool VerifyComplexity() {
+ depth_++;
+ num_tables_++;
+ bool too_complex = depth_ > max_depth_ || num_tables_ > max_tables_;
+ #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE
+ assert(!too_complex);
+ #endif
+ return !too_complex;
+ }
+
+ // Called at the end of a table to pop the depth count.
+ bool EndTable() {
+ depth_--;
+ return true;
+ }
+
private:
const uint8_t *buf_;
const uint8_t *end_;
+ size_t depth_;
+ size_t max_depth_;
+ size_t num_tables_;
+ size_t max_tables_;
};
// "structs" are flat structures that do not have an offset table, thus
// Verify the vtable of this table.
// Call this once per table, followed by VerifyField once per field.
- bool VerifyTable(const Verifier &verifier) const {
+ bool VerifyTableStart(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) &&
+ return verifier.VerifyComplexity() &&
+ verifier.Verify<voffset_t>(vtable) &&
verifier.Verify(vtable, ReadScalar<voffset_t>(vtable));
}
// 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, " +
+ "(flatbuffers::Verifier &verifier, " +
"const void *union_obj, uint8_t type)";
code += signature + ";\n\n";
code_post += signature + " {\n switch (type) {\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)";
+ code += " bool Verify(flatbuffers::Verifier &verifier) const {\n";
+ code += " return VerifyTableStart(verifier)";
std::string prefix = " &&\n ";
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end();
}
}
}
+ code += prefix + "verifier.EndTable()";
code += ";\n }\n";
code += "};\n\n";
// The root verifier:
code += "inline bool Verify";
code += parser.root_struct_def->name;
- code += "Buffer(const flatbuffers::Verifier &verifier) { "
+ code += "Buffer(flatbuffers::Verifier &verifier) { "
"return verifier.VerifyBuffer<";
code += parser.root_struct_def->name + ">(); }\n\n";
inline const char *EnumNameAny(int e) { return EnumNamesAny()[e]; }
-bool VerifyAny(const flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type);
+bool VerifyAny(flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type);
MANUALLY_ALIGNED_STRUCT(2) Test {
private:
const flatbuffers::Vector<uint8_t> *testnestedflatbuffer() const { return GetPointer<const flatbuffers::Vector<uint8_t> *>(30); }
const Monster *testnestedflatbuffer_nested_root() { return flatbuffers::GetRoot<Monster>(testnestedflatbuffer()->Data()); }
const Monster *testempty() const { return GetPointer<const Monster *>(32); }
- bool Verify(const flatbuffers::Verifier &verifier) const {
- return VerifyTable(verifier) &&
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
VerifyField<Vec3>(verifier, 4 /* pos */) &&
VerifyField<int16_t>(verifier, 6 /* mana */) &&
VerifyField<int16_t>(verifier, 8 /* hp */) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 30 /* testnestedflatbuffer */) &&
verifier.Verify(testnestedflatbuffer()) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 32 /* testempty */) &&
- verifier.VerifyTable(testempty());
+ verifier.VerifyTable(testempty()) &&
+ verifier.EndTable();
}
};
return builder_.Finish();
}
-bool VerifyAny(const flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type) {
+bool VerifyAny(flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type) {
switch (type) {
case Any_NONE: return true;
case Any_Monster: return verifier.VerifyTable(reinterpret_cast<const Monster *>(union_obj));
inline const Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot<Monster>(buf); }
-inline bool VerifyMonsterBuffer(const flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<Monster>(); }
+inline bool VerifyMonsterBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<Monster>(); }
inline void FinishMonsterBuffer(flatbuffers::FlatBufferBuilder &fbb, flatbuffers::Offset<Monster> root) { fbb.Finish(root, "MONS"); }