From 9d9141efac942fc87ed529a9f5c68ef96eb6d707 Mon Sep 17 00:00:00 2001 From: Taras Madan Date: Wed, 8 May 2024 12:18:24 +0200 Subject: executor: move flatbuffers from vendor to executor --- executor/_include/flatbuffers/verifier.h | 317 +++++++++++++++++++++++++++++++ 1 file changed, 317 insertions(+) create mode 100644 executor/_include/flatbuffers/verifier.h (limited to 'executor/_include/flatbuffers/verifier.h') diff --git a/executor/_include/flatbuffers/verifier.h b/executor/_include/flatbuffers/verifier.h new file mode 100644 index 000000000..87d3f54a5 --- /dev/null +++ b/executor/_include/flatbuffers/verifier.h @@ -0,0 +1,317 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_VERIFIER_H_ +#define FLATBUFFERS_VERIFIER_H_ + +#include "flatbuffers/base.h" +#include "flatbuffers/vector.h" + +namespace flatbuffers { + +// Helper class to verify the integrity of a FlatBuffer +class Verifier FLATBUFFERS_FINAL_CLASS { + public: + struct Options { + // The maximum nesting of tables and vectors before we call it invalid. + uoffset_t max_depth = 64; + // The maximum number of tables we will verify before we call it invalid. + uoffset_t max_tables = 1000000; + // If true, verify all data is aligned. + bool check_alignment = true; + // If true, run verifier on nested flatbuffers + bool check_nested_flatbuffers = true; + }; + + explicit Verifier(const uint8_t *const buf, const size_t buf_len, + const Options &opts) + : buf_(buf), size_(buf_len), opts_(opts) { + FLATBUFFERS_ASSERT(size_ < FLATBUFFERS_MAX_BUFFER_SIZE); + } + + // Deprecated API, please construct with Verifier::Options. + Verifier(const uint8_t *const buf, const size_t buf_len, + const uoffset_t max_depth = 64, const uoffset_t max_tables = 1000000, + const bool check_alignment = true) + : Verifier(buf, buf_len, [&] { + Options opts; + opts.max_depth = max_depth; + opts.max_tables = max_tables; + opts.check_alignment = check_alignment; + return opts; + }()) {} + + // Central location where any verification failures register. + bool Check(const bool ok) const { + // clang-format off + #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE + FLATBUFFERS_ASSERT(ok); + #endif + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + if (!ok) + upper_bound_ = 0; + #endif + // clang-format on + return ok; + } + + // Verify any range within the buffer. + bool Verify(const size_t elem, const size_t elem_len) const { + // clang-format off + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + auto upper_bound = elem + elem_len; + if (upper_bound_ < upper_bound) + upper_bound_ = upper_bound; + #endif + // clang-format on + return Check(elem_len < size_ && elem <= size_ - elem_len); + } + + bool VerifyAlignment(const size_t elem, const size_t align) const { + return Check((elem & (align - 1)) == 0 || !opts_.check_alignment); + } + + // Verify a range indicated by sizeof(T). + template bool Verify(const size_t elem) const { + return VerifyAlignment(elem, sizeof(T)) && Verify(elem, sizeof(T)); + } + + bool VerifyFromPointer(const uint8_t *const p, const size_t len) { + return Verify(static_cast(p - buf_), len); + } + + // Verify relative to a known-good base pointer. + bool VerifyFieldStruct(const uint8_t *const base, const voffset_t elem_off, + const size_t elem_len, const size_t align) const { + const auto f = static_cast(base - buf_) + elem_off; + return VerifyAlignment(f, align) && Verify(f, elem_len); + } + + template + bool VerifyField(const uint8_t *const base, const voffset_t elem_off, + const size_t align) const { + const auto f = static_cast(base - buf_) + elem_off; + return VerifyAlignment(f, align) && Verify(f, sizeof(T)); + } + + // Verify a pointer (may be NULL) of a table type. + template bool VerifyTable(const T *const table) { + return !table || table->Verify(*this); + } + + // Verify a pointer (may be NULL) of any vector type. + template bool VerifyVector(const Vector *const vec) const { + return !vec || VerifyVectorOrString(reinterpret_cast(vec), + sizeof(T)); + } + + // Verify a pointer (may be NULL) of a vector to struct. + template + bool VerifyVector(const Vector *const vec) const { + return VerifyVector(reinterpret_cast *>(vec)); + } + + // Verify a pointer (may be NULL) to string. + bool VerifyString(const String *const str) const { + size_t end; + return !str || (VerifyVectorOrString(reinterpret_cast(str), + 1, &end) && + Verify(end, 1) && // Must have terminator + Check(buf_[end] == '\0')); // Terminating byte must be 0. + } + + // Common code between vectors and strings. + bool VerifyVectorOrString(const uint8_t *const vec, const size_t elem_size, + size_t *const end = nullptr) const { + const auto veco = static_cast(vec - buf_); + // Check we can read the size field. + if (!Verify(veco)) return false; + // Check the whole array. If this is a string, the byte past the array must + // be 0. + const auto size = ReadScalar(vec); + const auto max_elems = FLATBUFFERS_MAX_BUFFER_SIZE / elem_size; + if (!Check(size < max_elems)) + return false; // Protect against byte_size overflowing. + const auto byte_size = sizeof(size) + elem_size * size; + if (end) *end = veco + byte_size; + return Verify(veco, byte_size); + } + + // Special case for string contents, after the above has been called. + bool VerifyVectorOfStrings(const Vector> *const vec) const { + if (vec) { + for (uoffset_t i = 0; i < vec->size(); i++) { + if (!VerifyString(vec->Get(i))) return false; + } + } + return true; + } + + // Special case for table contents, after the above has been called. + template + bool VerifyVectorOfTables(const Vector> *const vec) { + if (vec) { + for (uoffset_t i = 0; i < vec->size(); i++) { + if (!vec->Get(i)->Verify(*this)) return false; + } + } + return true; + } + + __suppress_ubsan__("unsigned-integer-overflow") bool VerifyTableStart( + const uint8_t *const table) { + // Check the vtable offset. + const auto tableo = static_cast(table - buf_); + if (!Verify(tableo)) return false; + // This offset may be signed, but doing the subtraction unsigned always + // gives the result we want. + const auto vtableo = + tableo - static_cast(ReadScalar(table)); + // Check the vtable size field, then check vtable fits in its entirety. + if (!(VerifyComplexity() && Verify(vtableo) && + VerifyAlignment(ReadScalar(buf_ + vtableo), + sizeof(voffset_t)))) + return false; + const auto vsize = ReadScalar(buf_ + vtableo); + return Check((vsize & 1) == 0) && Verify(vtableo, vsize); + } + + template + bool VerifyBufferFromStart(const char *const identifier, const size_t start) { + // Buffers have to be of some size to be valid. The reason it is a runtime + // check instead of static_assert, is that nested flatbuffers go through + // this call and their size is determined at runtime. + if (!Check(size_ >= FLATBUFFERS_MIN_BUFFER_SIZE)) return false; + + // If an identifier is provided, check that we have a buffer + if (identifier && !Check((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. + const auto o = VerifyOffset(start); + return Check(o != 0) && + reinterpret_cast(buf_ + start + o)->Verify(*this) + // clang-format off + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + && GetComputedSize() + #endif + ; + // clang-format on + } + + template + bool VerifyNestedFlatBuffer(const Vector *const buf, + const char *const identifier) { + // Caller opted out of this. + if (!opts_.check_nested_flatbuffers) return true; + + // An empty buffer is OK as it indicates not present. + if (!buf) return true; + + // If there is a nested buffer, it must be greater than the min size. + if (!Check(buf->size() >= FLATBUFFERS_MIN_BUFFER_SIZE)) return false; + + Verifier nested_verifier(buf->data(), buf->size()); + return nested_verifier.VerifyBuffer(identifier); + } + + // Verify this whole buffer, starting with root type T. + template bool VerifyBuffer() { return VerifyBuffer(nullptr); } + + template bool VerifyBuffer(const char *const identifier) { + return VerifyBufferFromStart(identifier, 0); + } + + template + bool VerifySizePrefixedBuffer(const char *const identifier) { + return Verify(0U) && + Check(ReadScalar(buf_) == size_ - sizeof(uoffset_t)) && + VerifyBufferFromStart(identifier, sizeof(uoffset_t)); + } + + uoffset_t VerifyOffset(const size_t start) const { + if (!Verify(start)) return 0; + const auto o = ReadScalar(buf_ + start); + // May not point to itself. + if (!Check(o != 0)) return 0; + // Can't wrap around / buffers are max 2GB. + if (!Check(static_cast(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 *const base, + const voffset_t start) const { + return VerifyOffset(static_cast(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 with EndTable(). + bool VerifyComplexity() { + depth_++; + num_tables_++; + return Check(depth_ <= opts_.max_depth && num_tables_ <= opts_.max_tables); + } + + // Called at the end of a table to pop the depth count. + bool EndTable() { + depth_--; + return true; + } + + // Returns the message size in bytes + size_t GetComputedSize() const { + // clang-format off + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + uintptr_t size = upper_bound_; + // Align the size to uoffset_t + size = (size - 1 + sizeof(uoffset_t)) & ~(sizeof(uoffset_t) - 1); + return (size > size_) ? 0 : size; + #else + // Must turn on FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE for this to work. + (void)upper_bound_; + FLATBUFFERS_ASSERT(false); + return 0; + #endif + // clang-format on + } + + std::vector *GetFlexReuseTracker() { return flex_reuse_tracker_; } + + void SetFlexReuseTracker(std::vector *const rt) { + flex_reuse_tracker_ = rt; + } + + private: + const uint8_t *buf_; + const size_t size_; + const Options opts_; + + mutable size_t upper_bound_ = 0; + + uoffset_t depth_ = 0; + uoffset_t num_tables_ = 0; + std::vector *flex_reuse_tracker_ = nullptr; +}; + +} // namespace flatbuffers + +#endif // FLATBUFFERS_VERIFIER_H_ -- cgit mrf-deployment