diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/flatbuffers/flatbuffers.h | 117 |
1 files changed, 73 insertions, 44 deletions
diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index 4e2008f9..563afc1d 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -1731,17 +1731,18 @@ class Verifier FLATBUFFERS_FINAL_CLASS { Verifier(const uint8_t *buf, size_t buf_len, uoffset_t _max_depth = 64, uoffset_t _max_tables = 1000000) : buf_(buf), - end_(buf + buf_len), + size_(buf_len), depth_(0), max_depth_(_max_depth), num_tables_(0), max_tables_(_max_tables) // clang-format off #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE - , upper_bound_(buf) + , upper_bound_(0) #endif // clang-format on { + assert(size_ < FLATBUFFERS_MAX_BUFFER_SIZE); } // Central location where any verification failures register. @@ -1752,30 +1753,39 @@ class Verifier FLATBUFFERS_FINAL_CLASS { #endif #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE if (!ok) - upper_bound_ = buf_; + upper_bound_ = 0; #endif // clang-format on return ok; } // Verify any range within the buffer. - bool Verify(const void *elem, size_t elem_len) const { + bool Verify(uoffset_t elem, size_t elem_len) const { // clang-format off #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE - auto upper_bound = reinterpret_cast<const uint8_t *>(elem) + elem_len; + auto upper_bound = elem + elem_len; if (upper_bound_ < upper_bound) upper_bound_ = upper_bound; #endif // clang-format on - return Check(elem_len <= (size_t)(end_ - buf_) && elem >= buf_ && - elem <= end_ - elem_len); + return Check(elem_len < size_ && elem <= size_ - elem_len); } // Verify a range indicated by sizeof(T). - template<typename T> bool Verify(const void *elem) const { + template<typename T> bool Verify(uoffset_t elem) const { return Verify(elem, sizeof(T)); } + // Verify relative to a known-good base pointer. + bool Verify(const uint8_t *base, voffset_t elem_off, size_t elem_len) const { + return Verify(static_cast<uoffset_t>(base - buf_) + elem_off, elem_len); + } + + template<typename T> bool Verify(const uint8_t *base, voffset_t elem_off) + const { + return Verify(static_cast<uoffset_t>(base - buf_) + elem_off, sizeof(T)); + } + // Verify a pointer (may be NULL) of a table type. template<typename T> bool VerifyTable(const T *table) { return !table || table->Verify(*this); @@ -1783,9 +1793,8 @@ class Verifier FLATBUFFERS_FINAL_CLASS { // 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); + sizeof(T)); } // Verify a pointer (may be NULL) of a vector to struct. @@ -1795,18 +1804,19 @@ class Verifier FLATBUFFERS_FINAL_CLASS { // Verify a pointer (may be NULL) to string. bool Verify(const String *str) const { - const uint8_t *end; + uoffset_t end; return !str || (VerifyVector(reinterpret_cast<const uint8_t *>(str), 1, &end) && Verify(end, 1) && // Must have terminator - Check(*end == '\0')); // Terminating byte must be 0. + Check(buf_[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 { + uoffset_t *end = nullptr) const { + auto veco = static_cast<uoffset_t>(vec - buf_); // Check we can read the size field. - if (!Verify<uoffset_t>(vec)) return false; + if (!Verify<uoffset_t>(veco)) 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); @@ -1814,8 +1824,8 @@ class Verifier FLATBUFFERS_FINAL_CLASS { if (!Check(size < max_elems)) return false; // Protect against byte_size overflowing. auto byte_size = sizeof(size) + elem_size * size; - *end = vec + byte_size; - return Verify(vec, byte_size); + if (end) *end = veco + static_cast<uoffset_t>(byte_size); + return Verify(veco, byte_size); } // Special case for string contents, after the above has been called. @@ -1838,43 +1848,68 @@ class Verifier FLATBUFFERS_FINAL_CLASS { return true; } + bool VerifyTableStart(const uint8_t *table) { + // Check the vtable offset. + auto tableo = static_cast<uoffset_t>(table - buf_); + if (!Verify<soffset_t>(tableo)) return false; + auto vtableo = static_cast<uoffset_t>(static_cast<soffset_t>(tableo) - + ReadScalar<soffset_t>(table)); + // Check the vtable size field, then check vtable fits in its entirety. + return VerifyComplexity() && Verify<voffset_t>(vtableo) && + (ReadScalar<voffset_t>(buf_ + vtableo) & + (sizeof(voffset_t) - 1)) == 0 && + Verify(vtableo, ReadScalar<voffset_t>(buf_ + vtableo)); + } + template<typename T> - bool VerifyBufferFromStart(const char *identifier, const uint8_t *start) { + bool VerifyBufferFromStart(const char *identifier, uoffset_t start) { if (identifier && - (size_t(end_ - start) < 2 * sizeof(flatbuffers::uoffset_t) || - !BufferHasIdentifier(start, identifier))) { + (size_ < 2 * sizeof(flatbuffers::uoffset_t) || + !BufferHasIdentifier(buf_ + start, identifier))) { return false; } // Call T::Verify, which must be in the generated code for this type. auto o = VerifyOffset(start); - return o && reinterpret_cast<const T *>(start + o)->Verify(*this) -#ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + return o && reinterpret_cast<const T *>(buf_ + start + o)->Verify(*this) + // clang-format off + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE && GetComputedSize() -#endif + #endif ; + // clang-format on } // Verify this whole buffer, starting with root type T. template<typename T> bool VerifyBuffer() { return VerifyBuffer<T>(nullptr); } template<typename T> bool VerifyBuffer(const char *identifier) { - return VerifyBufferFromStart<T>(identifier, buf_); + return VerifyBufferFromStart<T>(identifier, 0); } template<typename T> bool VerifySizePrefixedBuffer(const char *identifier) { - return Verify<uoffset_t>(buf_) && - ReadScalar<uoffset_t>(buf_) == end_ - buf_ - sizeof(uoffset_t) && - VerifyBufferFromStart<T>(identifier, buf_ + sizeof(uoffset_t)); + return Verify<uoffset_t>(0U) && + ReadScalar<uoffset_t>(buf_) == size_ - sizeof(uoffset_t) && + VerifyBufferFromStart<T>(identifier, sizeof(uoffset_t)); } - uoffset_t VerifyOffset(const uint8_t *start) const { - if (!Verify<uoffset_t>(start)) return false; - auto o = ReadScalar<uoffset_t>(start); + uoffset_t VerifyOffset(uoffset_t start) const { + if (!Verify<uoffset_t>(start)) return 0; + auto o = ReadScalar<uoffset_t>(buf_ + start); + // May not point to itself. Check(o != 0); + // Can't wrap around / buffers are max 2GB. + if (!Check(static_cast<soffset_t>(o) >= 0)) return 0; + // Must be inside the buffer to create a pointer from it (pointer outside + // buffer is UB). + if (!Verify(start + o, 1)) return 0; return o; } + uoffset_t VerifyOffset(const uint8_t *base, voffset_t start) const { + return VerifyOffset(static_cast<uoffset_t>(base - buf_) + start); + } + // 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 @@ -1895,24 +1930,24 @@ class Verifier FLATBUFFERS_FINAL_CLASS { #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE // Returns the message size in bytes size_t GetComputedSize() const { - uintptr_t size = upper_bound_ - buf_; + uintptr_t size = upper_bound_; // Align the size to uoffset_t size = (size - 1 + sizeof(uoffset_t)) & ~(sizeof(uoffset_t) - 1); - return (buf_ + size > end_) ? 0 : size; + return (size > size_) ? 0 : size; } #endif // clang-format on private: const uint8_t *buf_; - const uint8_t *end_; + size_t size_; uoffset_t depth_; uoffset_t max_depth_; uoffset_t num_tables_; uoffset_t max_tables_; // clang-format off #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE - mutable const uint8_t *upper_bound_; + mutable size_t upper_bound_; #endif // clang-format on }; @@ -2034,13 +2069,7 @@ class Table { // Verify the vtable of this table. // Call this once per table, followed by VerifyField once per field. bool VerifyTableStart(Verifier &verifier) const { - // Check the vtable offset. - if (!verifier.Verify<soffset_t>(data_)) return false; - auto vtable = GetVTable(); - // Check the vtable size field, then check vtable fits in its entirety. - return verifier.VerifyComplexity() && verifier.Verify<voffset_t>(vtable) && - (ReadScalar<voffset_t>(vtable) & (sizeof(voffset_t) - 1)) == 0 && - verifier.Verify(vtable, ReadScalar<voffset_t>(vtable)); + return verifier.VerifyTableStart(data_); } // Verify a particular field. @@ -2050,7 +2079,7 @@ class Table { // VerifyTable(). auto field_offset = GetOptionalFieldOffset(field); // Check the actual field. - return !field_offset || verifier.Verify<T>(data_ + field_offset); + return !field_offset || verifier.Verify<T>(data_, field_offset); } // VerifyField for required fields. @@ -2058,19 +2087,19 @@ class Table { bool VerifyFieldRequired(const Verifier &verifier, voffset_t field) const { auto field_offset = GetOptionalFieldOffset(field); return verifier.Check(field_offset != 0) && - verifier.Verify<T>(data_ + field_offset); + verifier.Verify<T>(data_, field_offset); } // Versions for offsets. bool VerifyOffset(const Verifier &verifier, voffset_t field) const { auto field_offset = GetOptionalFieldOffset(field); - return !field_offset || verifier.VerifyOffset(data_ + field_offset); + return !field_offset || verifier.VerifyOffset(data_, field_offset); } bool VerifyOffsetRequired(const Verifier &verifier, voffset_t field) const { auto field_offset = GetOptionalFieldOffset(field); return verifier.Check(field_offset != 0) && - verifier.VerifyOffset(data_ + field_offset); + verifier.VerifyOffset(data_, field_offset); } private: |