Add checks to verifier (#7438)

This commit is contained in:
Derek Bailey
2022-08-13 23:44:13 -07:00
committed by GitHub
parent 8a09f3fb0b
commit 83d4e2a100

View File

@@ -25,22 +25,24 @@ namespace flatbuffers {
// Helper class to verify the integrity of a FlatBuffer // Helper class to verify the integrity of a FlatBuffer
class Verifier FLATBUFFERS_FINAL_CLASS { class Verifier FLATBUFFERS_FINAL_CLASS {
public: public:
Verifier(const uint8_t *buf, size_t buf_len, uoffset_t _max_depth = 64, Verifier(const uint8_t *const buf, const size_t buf_len,
uoffset_t _max_tables = 1000000, bool _check_alignment = true) const uoffset_t _max_depth = 64,
const uoffset_t _max_tables = 1000000,
const bool _check_alignment = true)
: buf_(buf), : buf_(buf),
size_(buf_len), size_(buf_len),
depth_(0),
max_depth_(_max_depth), max_depth_(_max_depth),
num_tables_(0),
max_tables_(_max_tables), max_tables_(_max_tables),
upper_bound_(0),
check_alignment_(_check_alignment), check_alignment_(_check_alignment),
upper_bound_(0),
depth_(0),
num_tables_(0),
flex_reuse_tracker_(nullptr) { flex_reuse_tracker_(nullptr) {
FLATBUFFERS_ASSERT(size_ < FLATBUFFERS_MAX_BUFFER_SIZE); FLATBUFFERS_ASSERT(size_ < FLATBUFFERS_MAX_BUFFER_SIZE);
} }
// Central location where any verification failures register. // Central location where any verification failures register.
bool Check(bool ok) const { bool Check(const bool ok) const {
// clang-format off // clang-format off
#ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE
FLATBUFFERS_ASSERT(ok); FLATBUFFERS_ASSERT(ok);
@@ -54,7 +56,7 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
} }
// Verify any range within the buffer. // Verify any range within the buffer.
bool Verify(size_t elem, size_t elem_len) const { bool Verify(const size_t elem, const size_t elem_len) const {
// clang-format off // clang-format off
#ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
auto upper_bound = elem + elem_len; auto upper_bound = elem + elem_len;
@@ -65,52 +67,52 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
return Check(elem_len < size_ && elem <= size_ - elem_len); return Check(elem_len < size_ && elem <= size_ - elem_len);
} }
bool VerifyAlignment(size_t elem, size_t align) const { bool VerifyAlignment(const size_t elem, const size_t align) const {
return Check((elem & (align - 1)) == 0 || !check_alignment_); return Check((elem & (align - 1)) == 0 || !check_alignment_);
} }
// Verify a range indicated by sizeof(T). // Verify a range indicated by sizeof(T).
template<typename T> bool Verify(size_t elem) const { template<typename T> bool Verify(const size_t elem) const {
return VerifyAlignment(elem, sizeof(T)) && Verify(elem, sizeof(T)); return VerifyAlignment(elem, sizeof(T)) && Verify(elem, sizeof(T));
} }
bool VerifyFromPointer(const uint8_t *p, size_t len) { bool VerifyFromPointer(const uint8_t *const p, const size_t len) {
auto o = static_cast<size_t>(p - buf_); return Verify(static_cast<size_t>(p - buf_), len);
return Verify(o, len);
} }
// Verify relative to a known-good base pointer. // Verify relative to a known-good base pointer.
bool VerifyFieldStruct(const uint8_t *base, voffset_t elem_off, bool VerifyFieldStruct(const uint8_t *const base, const voffset_t elem_off,
size_t elem_len, size_t align) const { const size_t elem_len, const size_t align) const {
auto f = static_cast<size_t>(base - buf_) + elem_off; const auto f = static_cast<size_t>(base - buf_) + elem_off;
return VerifyAlignment(f, align) && Verify(f, elem_len); return VerifyAlignment(f, align) && Verify(f, elem_len);
} }
template<typename T> template<typename T>
bool VerifyField(const uint8_t *base, voffset_t elem_off, bool VerifyField(const uint8_t *const base, const voffset_t elem_off,
size_t align) const { const size_t align) const {
auto f = static_cast<size_t>(base - buf_) + elem_off; const auto f = static_cast<size_t>(base - buf_) + elem_off;
return VerifyAlignment(f, align) && Verify(f, sizeof(T)); return VerifyAlignment(f, align) && Verify(f, sizeof(T));
} }
// Verify a pointer (may be NULL) of a table type. // Verify a pointer (may be NULL) of a table type.
template<typename T> bool VerifyTable(const T *table) { template<typename T> bool VerifyTable(const T *const table) {
return !table || table->Verify(*this); return !table || table->Verify(*this);
} }
// Verify a pointer (may be NULL) of any vector type. // Verify a pointer (may be NULL) of any vector type.
template<typename T> bool VerifyVector(const Vector<T> *vec) const { template<typename T> bool VerifyVector(const Vector<T> *const vec) const {
return !vec || VerifyVectorOrString(reinterpret_cast<const uint8_t *>(vec), return !vec || VerifyVectorOrString(reinterpret_cast<const uint8_t *>(vec),
sizeof(T)); sizeof(T));
} }
// Verify a pointer (may be NULL) of a vector to struct. // Verify a pointer (may be NULL) of a vector to struct.
template<typename T> bool VerifyVector(const Vector<const T *> *vec) const { template<typename T>
bool VerifyVector(const Vector<const T *> *const vec) const {
return VerifyVector(reinterpret_cast<const Vector<T> *>(vec)); return VerifyVector(reinterpret_cast<const Vector<T> *>(vec));
} }
// Verify a pointer (may be NULL) to string. // Verify a pointer (may be NULL) to string.
bool VerifyString(const String *str) const { bool VerifyString(const String *const str) const {
size_t end; size_t end;
return !str || (VerifyVectorOrString(reinterpret_cast<const uint8_t *>(str), return !str || (VerifyVectorOrString(reinterpret_cast<const uint8_t *>(str),
1, &end) && 1, &end) &&
@@ -119,24 +121,24 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
} }
// Common code between vectors and strings. // Common code between vectors and strings.
bool VerifyVectorOrString(const uint8_t *vec, size_t elem_size, bool VerifyVectorOrString(const uint8_t *const vec, const size_t elem_size,
size_t *end = nullptr) const { size_t *const end = nullptr) const {
auto veco = static_cast<size_t>(vec - buf_); const auto veco = static_cast<size_t>(vec - buf_);
// Check we can read the size field. // Check we can read the size field.
if (!Verify<uoffset_t>(veco)) return false; if (!Verify<uoffset_t>(veco)) return false;
// Check the whole array. If this is a string, the byte past the array // Check the whole array. If this is a string, the byte past the array
// must be 0. // must be 0.
auto size = ReadScalar<uoffset_t>(vec); const auto size = ReadScalar<uoffset_t>(vec);
auto max_elems = FLATBUFFERS_MAX_BUFFER_SIZE / elem_size; const auto max_elems = FLATBUFFERS_MAX_BUFFER_SIZE / elem_size;
if (!Check(size < max_elems)) if (!Check(size < max_elems))
return false; // Protect against byte_size overflowing. return false; // Protect against byte_size overflowing.
auto byte_size = sizeof(size) + elem_size * size; const auto byte_size = sizeof(size) + elem_size * size;
if (end) *end = veco + byte_size; if (end) *end = veco + byte_size;
return Verify(veco, byte_size); return Verify(veco, byte_size);
} }
// Special case for string contents, after the above has been called. // Special case for string contents, after the above has been called.
bool VerifyVectorOfStrings(const Vector<Offset<String>> *vec) const { bool VerifyVectorOfStrings(const Vector<Offset<String>> *const vec) const {
if (vec) { if (vec) {
for (uoffset_t i = 0; i < vec->size(); i++) { for (uoffset_t i = 0; i < vec->size(); i++) {
if (!VerifyString(vec->Get(i))) return false; if (!VerifyString(vec->Get(i))) return false;
@@ -146,7 +148,8 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
} }
// Special case for table contents, after the above has been called. // Special case for table contents, after the above has been called.
template<typename T> bool VerifyVectorOfTables(const Vector<Offset<T>> *vec) { template<typename T>
bool VerifyVectorOfTables(const Vector<Offset<T>> *const vec) {
if (vec) { if (vec) {
for (uoffset_t i = 0; i < vec->size(); i++) { for (uoffset_t i = 0; i < vec->size(); i++) {
if (!vec->Get(i)->Verify(*this)) return false; if (!vec->Get(i)->Verify(*this)) return false;
@@ -156,31 +159,34 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
} }
__supress_ubsan__("unsigned-integer-overflow") bool VerifyTableStart( __supress_ubsan__("unsigned-integer-overflow") bool VerifyTableStart(
const uint8_t *table) { const uint8_t *const table) {
// Check the vtable offset. // Check the vtable offset.
auto tableo = static_cast<size_t>(table - buf_); const auto tableo = static_cast<size_t>(table - buf_);
if (!Verify<soffset_t>(tableo)) return false; if (!Verify<soffset_t>(tableo)) return false;
// This offset may be signed, but doing the subtraction unsigned always // This offset may be signed, but doing the subtraction unsigned always
// gives the result we want. // gives the result we want.
auto vtableo = tableo - static_cast<size_t>(ReadScalar<soffset_t>(table)); const auto vtableo =
tableo - static_cast<size_t>(ReadScalar<soffset_t>(table));
// Check the vtable size field, then check vtable fits in its entirety. // Check the vtable size field, then check vtable fits in its entirety.
if (!( VerifyComplexity() && Verify<voffset_t>(vtableo) && if (!(VerifyComplexity() && Verify<voffset_t>(vtableo) &&
VerifyAlignment(ReadScalar<voffset_t>(buf_ + vtableo), VerifyAlignment(ReadScalar<voffset_t>(buf_ + vtableo),
sizeof(voffset_t)))) return false; sizeof(voffset_t))))
auto vsize = ReadScalar<voffset_t>(buf_ + vtableo); return false;
const auto vsize = ReadScalar<voffset_t>(buf_ + vtableo);
return Check((vsize & 1) == 0) && Verify(vtableo, vsize); return Check((vsize & 1) == 0) && Verify(vtableo, vsize);
} }
template<typename T> template<typename T>
bool VerifyBufferFromStart(const char *identifier, size_t start) { bool VerifyBufferFromStart(const char *const identifier, const size_t start) {
if (identifier && !Check((size_ >= 2 * sizeof(flatbuffers::uoffset_t) && if (identifier && !Check((size_ >= 2 * sizeof(flatbuffers::uoffset_t) &&
BufferHasIdentifier(buf_ + start, identifier)))) { BufferHasIdentifier(buf_ + start, identifier)))) {
return false; return false;
} }
// Call T::Verify, which must be in the generated code for this type. // Call T::Verify, which must be in the generated code for this type.
auto o = VerifyOffset(start); const auto o = VerifyOffset(start);
return o && reinterpret_cast<const T *>(buf_ + start + o)->Verify(*this) return Check(o != 0) &&
reinterpret_cast<const T *>(buf_ + start + o)->Verify(*this)
// clang-format off // clang-format off
#ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
&& GetComputedSize() && GetComputedSize()
@@ -190,8 +196,8 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
} }
template<typename T> template<typename T>
bool VerifyNestedFlatBuffer(const Vector<uint8_t> *buf, bool VerifyNestedFlatBuffer(const Vector<uint8_t> *const buf,
const char *identifier) { const char *const identifier) {
if (!buf) return true; if (!buf) return true;
Verifier nested_verifier(buf->data(), buf->size()); Verifier nested_verifier(buf->data(), buf->size());
return nested_verifier.VerifyBuffer<T>(identifier); return nested_verifier.VerifyBuffer<T>(identifier);
@@ -200,19 +206,20 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
// Verify this whole buffer, starting with root type T. // Verify this whole buffer, starting with root type T.
template<typename T> bool VerifyBuffer() { return VerifyBuffer<T>(nullptr); } template<typename T> bool VerifyBuffer() { return VerifyBuffer<T>(nullptr); }
template<typename T> bool VerifyBuffer(const char *identifier) { template<typename T> bool VerifyBuffer(const char *const identifier) {
return VerifyBufferFromStart<T>(identifier, 0); return VerifyBufferFromStart<T>(identifier, 0);
} }
template<typename T> bool VerifySizePrefixedBuffer(const char *identifier) { template<typename T>
bool VerifySizePrefixedBuffer(const char *const identifier) {
return Verify<uoffset_t>(0U) && return Verify<uoffset_t>(0U) &&
ReadScalar<uoffset_t>(buf_) == size_ - sizeof(uoffset_t) && Check(ReadScalar<uoffset_t>(buf_) == size_ - sizeof(uoffset_t)) &&
VerifyBufferFromStart<T>(identifier, sizeof(uoffset_t)); VerifyBufferFromStart<T>(identifier, sizeof(uoffset_t));
} }
uoffset_t VerifyOffset(size_t start) const { uoffset_t VerifyOffset(const size_t start) const {
if (!Verify<uoffset_t>(start)) return 0; if (!Verify<uoffset_t>(start)) return 0;
auto o = ReadScalar<uoffset_t>(buf_ + start); const auto o = ReadScalar<uoffset_t>(buf_ + start);
// May not point to itself. // May not point to itself.
if (!Check(o != 0)) return 0; if (!Check(o != 0)) return 0;
// Can't wrap around / buffers are max 2GB. // Can't wrap around / buffers are max 2GB.
@@ -223,7 +230,8 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
return o; return o;
} }
uoffset_t VerifyOffset(const uint8_t *base, voffset_t start) const { uoffset_t VerifyOffset(const uint8_t *const base,
const voffset_t start) const {
return VerifyOffset(static_cast<size_t>(base - buf_) + start); return VerifyOffset(static_cast<size_t>(base - buf_) + start);
} }
@@ -262,19 +270,21 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
std::vector<uint8_t> *GetFlexReuseTracker() { return flex_reuse_tracker_; } std::vector<uint8_t> *GetFlexReuseTracker() { return flex_reuse_tracker_; }
void SetFlexReuseTracker(std::vector<uint8_t> *rt) { void SetFlexReuseTracker(std::vector<uint8_t> *const rt) {
flex_reuse_tracker_ = rt; flex_reuse_tracker_ = rt;
} }
private: private:
const uint8_t *buf_; const uint8_t *buf_;
size_t size_; const size_t size_;
uoffset_t depth_; const uoffset_t max_depth_;
uoffset_t max_depth_; const uoffset_t max_tables_;
uoffset_t num_tables_; const bool check_alignment_;
uoffset_t max_tables_;
mutable size_t upper_bound_; mutable size_t upper_bound_;
bool check_alignment_;
uoffset_t depth_;
uoffset_t num_tables_;
std::vector<uint8_t> *flex_reuse_tracker_; std::vector<uint8_t> *flex_reuse_tracker_;
}; };