github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/executor/_include/flatbuffers/verifier.h (about)

     1  /*
     2   * Copyright 2021 Google Inc. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  #ifndef FLATBUFFERS_VERIFIER_H_
    18  #define FLATBUFFERS_VERIFIER_H_
    19  
    20  #include "flatbuffers/base.h"
    21  #include "flatbuffers/vector.h"
    22  
    23  namespace flatbuffers {
    24  
    25  // Helper class to verify the integrity of a FlatBuffer
    26  class Verifier FLATBUFFERS_FINAL_CLASS {
    27   public:
    28    struct Options {
    29      // The maximum nesting of tables and vectors before we call it invalid.
    30      uoffset_t max_depth = 64;
    31      // The maximum number of tables we will verify before we call it invalid.
    32      uoffset_t max_tables = 1000000;
    33      // If true, verify all data is aligned.
    34      bool check_alignment = true;
    35      // If true, run verifier on nested flatbuffers
    36      bool check_nested_flatbuffers = true;
    37      // The maximum size of a buffer.
    38      size_t max_size = FLATBUFFERS_MAX_BUFFER_SIZE;
    39      // Use assertions to check for errors.
    40      bool assert = false;
    41    };
    42  
    43    explicit Verifier(const uint8_t *const buf, const size_t buf_len,
    44                      const Options &opts)
    45        : buf_(buf), size_(buf_len), opts_(opts) {
    46      FLATBUFFERS_ASSERT(size_ < opts.max_size);
    47    }
    48  
    49    // Deprecated API, please construct with Verifier::Options.
    50    Verifier(const uint8_t *const buf, const size_t buf_len,
    51             const uoffset_t max_depth = 64, const uoffset_t max_tables = 1000000,
    52             const bool check_alignment = true)
    53        : Verifier(buf, buf_len, [&] {
    54            Options opts;
    55            opts.max_depth = max_depth;
    56            opts.max_tables = max_tables;
    57            opts.check_alignment = check_alignment;
    58            return opts;
    59          }()) {}
    60  
    61    // Central location where any verification failures register.
    62    bool Check(const bool ok) const {
    63      // clang-format off
    64      #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE
    65         if (opts_.assert) { FLATBUFFERS_ASSERT(ok); }
    66      #endif
    67      #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
    68        if (!ok)
    69          upper_bound_ = 0;
    70      #endif
    71      // clang-format on
    72      return ok;
    73    }
    74  
    75    // Verify any range within the buffer.
    76    bool Verify(const size_t elem, const size_t elem_len) const {
    77      // clang-format off
    78      #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
    79        auto upper_bound = elem + elem_len;
    80        if (upper_bound_ < upper_bound)
    81          upper_bound_ =  upper_bound;
    82      #endif
    83      // clang-format on
    84      return Check(elem_len < size_ && elem <= size_ - elem_len);
    85    }
    86  
    87    bool VerifyAlignment(const size_t elem, const size_t align) const {
    88      return Check((elem & (align - 1)) == 0 || !opts_.check_alignment);
    89    }
    90  
    91    // Verify a range indicated by sizeof(T).
    92    template<typename T> bool Verify(const size_t elem) const {
    93      return VerifyAlignment(elem, sizeof(T)) && Verify(elem, sizeof(T));
    94    }
    95  
    96    bool VerifyFromPointer(const uint8_t *const p, const size_t len) {
    97      return Verify(static_cast<size_t>(p - buf_), len);
    98    }
    99  
   100    // Verify relative to a known-good base pointer.
   101    bool VerifyFieldStruct(const uint8_t *const base, const voffset_t elem_off,
   102                           const size_t elem_len, const size_t align) const {
   103      const auto f = static_cast<size_t>(base - buf_) + elem_off;
   104      return VerifyAlignment(f, align) && Verify(f, elem_len);
   105    }
   106  
   107    template<typename T>
   108    bool VerifyField(const uint8_t *const base, const voffset_t elem_off,
   109                     const size_t align) const {
   110      const auto f = static_cast<size_t>(base - buf_) + elem_off;
   111      return VerifyAlignment(f, align) && Verify(f, sizeof(T));
   112    }
   113  
   114    // Verify a pointer (may be NULL) of a table type.
   115    template<typename T> bool VerifyTable(const T *const table) {
   116      return !table || table->Verify(*this);
   117    }
   118  
   119    // Verify a pointer (may be NULL) of any vector type.
   120    template<int &..., typename T, typename LenT>
   121    bool VerifyVector(const Vector<T, LenT> *const vec) const {
   122      return !vec || VerifyVectorOrString<LenT>(
   123                         reinterpret_cast<const uint8_t *>(vec), sizeof(T));
   124    }
   125  
   126    // Verify a pointer (may be NULL) of a vector to struct.
   127    template<int &..., typename T, typename LenT>
   128    bool VerifyVector(const Vector<const T *, LenT> *const vec) const {
   129      return VerifyVector(reinterpret_cast<const Vector<T, LenT> *>(vec));
   130    }
   131  
   132    // Verify a pointer (may be NULL) to string.
   133    bool VerifyString(const String *const str) const {
   134      size_t end;
   135      return !str || (VerifyVectorOrString<uoffset_t>(
   136                          reinterpret_cast<const uint8_t *>(str), 1, &end) &&
   137                      Verify(end, 1) &&           // Must have terminator
   138                      Check(buf_[end] == '\0'));  // Terminating byte must be 0.
   139    }
   140  
   141    // Common code between vectors and strings.
   142    template<typename LenT = uoffset_t>
   143    bool VerifyVectorOrString(const uint8_t *const vec, const size_t elem_size,
   144                              size_t *const end = nullptr) const {
   145      const auto vec_offset = static_cast<size_t>(vec - buf_);
   146      // Check we can read the size field.
   147      if (!Verify<LenT>(vec_offset)) return false;
   148      // Check the whole array. If this is a string, the byte past the array must
   149      // be 0.
   150      const LenT size = ReadScalar<LenT>(vec);
   151      const auto max_elems = opts_.max_size / elem_size;
   152      if (!Check(size < max_elems))
   153        return false;  // Protect against byte_size overflowing.
   154      const auto byte_size = sizeof(LenT) + elem_size * size;
   155      if (end) *end = vec_offset + byte_size;
   156      return Verify(vec_offset, byte_size);
   157    }
   158  
   159    // Special case for string contents, after the above has been called.
   160    bool VerifyVectorOfStrings(const Vector<Offset<String>> *const vec) const {
   161      if (vec) {
   162        for (uoffset_t i = 0; i < vec->size(); i++) {
   163          if (!VerifyString(vec->Get(i))) return false;
   164        }
   165      }
   166      return true;
   167    }
   168  
   169    // Special case for table contents, after the above has been called.
   170    template<typename T>
   171    bool VerifyVectorOfTables(const Vector<Offset<T>> *const vec) {
   172      if (vec) {
   173        for (uoffset_t i = 0; i < vec->size(); i++) {
   174          if (!vec->Get(i)->Verify(*this)) return false;
   175        }
   176      }
   177      return true;
   178    }
   179  
   180    FLATBUFFERS_SUPPRESS_UBSAN("unsigned-integer-overflow")
   181    bool VerifyTableStart(const uint8_t *const table) {
   182      // Check the vtable offset.
   183      const auto tableo = static_cast<size_t>(table - buf_);
   184      if (!Verify<soffset_t>(tableo)) return false;
   185      // This offset may be signed, but doing the subtraction unsigned always
   186      // gives the result we want.
   187      const auto vtableo =
   188          tableo - static_cast<size_t>(ReadScalar<soffset_t>(table));
   189      // Check the vtable size field, then check vtable fits in its entirety.
   190      if (!(VerifyComplexity() && Verify<voffset_t>(vtableo) &&
   191            VerifyAlignment(ReadScalar<voffset_t>(buf_ + vtableo),
   192                            sizeof(voffset_t))))
   193        return false;
   194      const auto vsize = ReadScalar<voffset_t>(buf_ + vtableo);
   195      return Check((vsize & 1) == 0) && Verify(vtableo, vsize);
   196    }
   197  
   198    template<typename T>
   199    bool VerifyBufferFromStart(const char *const identifier, const size_t start) {
   200      // Buffers have to be of some size to be valid. The reason it is a runtime
   201      // check instead of static_assert, is that nested flatbuffers go through
   202      // this call and their size is determined at runtime.
   203      if (!Check(size_ >= FLATBUFFERS_MIN_BUFFER_SIZE)) return false;
   204  
   205      // If an identifier is provided, check that we have a buffer
   206      if (identifier && !Check((size_ >= 2 * sizeof(flatbuffers::uoffset_t) &&
   207                                BufferHasIdentifier(buf_ + start, identifier)))) {
   208        return false;
   209      }
   210  
   211      // Call T::Verify, which must be in the generated code for this type.
   212      const auto o = VerifyOffset<uoffset_t>(start);
   213      return Check(o != 0) &&
   214             reinterpret_cast<const T *>(buf_ + start + o)->Verify(*this)
   215      // clang-format off
   216      #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
   217             && GetComputedSize()
   218      #endif
   219          ;
   220      // clang-format on
   221    }
   222  
   223    template<typename T, int &..., typename SizeT>
   224    bool VerifyNestedFlatBuffer(const Vector<uint8_t, SizeT> *const buf,
   225                                const char *const identifier) {
   226      // Caller opted out of this.
   227      if (!opts_.check_nested_flatbuffers) return true;
   228  
   229      // An empty buffer is OK as it indicates not present.
   230      if (!buf) return true;
   231  
   232      // If there is a nested buffer, it must be greater than the min size.
   233      if (!Check(buf->size() >= FLATBUFFERS_MIN_BUFFER_SIZE)) return false;
   234  
   235      Verifier nested_verifier(buf->data(), buf->size(), opts_);
   236      return nested_verifier.VerifyBuffer<T>(identifier);
   237    }
   238  
   239    // Verify this whole buffer, starting with root type T.
   240    template<typename T> bool VerifyBuffer() { return VerifyBuffer<T>(nullptr); }
   241  
   242    template<typename T> bool VerifyBuffer(const char *const identifier) {
   243      return VerifyBufferFromStart<T>(identifier, 0);
   244    }
   245  
   246    template<typename T, typename SizeT = uoffset_t>
   247    bool VerifySizePrefixedBuffer(const char *const identifier) {
   248      return Verify<SizeT>(0U) &&
   249             // Ensure the prefixed size is within the bounds of the provided
   250             // length.
   251             Check(ReadScalar<SizeT>(buf_) + sizeof(SizeT) <= size_) &&
   252             VerifyBufferFromStart<T>(identifier, sizeof(SizeT));
   253    }
   254  
   255    template<typename OffsetT = uoffset_t, typename SOffsetT = soffset_t>
   256    size_t VerifyOffset(const size_t start) const {
   257      if (!Verify<OffsetT>(start)) return 0;
   258      const auto o = ReadScalar<OffsetT>(buf_ + start);
   259      // May not point to itself.
   260      if (!Check(o != 0)) return 0;
   261      // Can't wrap around larger than the max size.
   262      if (!Check(static_cast<SOffsetT>(o) >= 0)) return 0;
   263      // Must be inside the buffer to create a pointer from it (pointer outside
   264      // buffer is UB).
   265      if (!Verify(start + o, 1)) return 0;
   266      return o;
   267    }
   268  
   269    template<typename OffsetT = uoffset_t>
   270    size_t VerifyOffset(const uint8_t *const base, const voffset_t start) const {
   271      return VerifyOffset<OffsetT>(static_cast<size_t>(base - buf_) + start);
   272    }
   273  
   274    // Called at the start of a table to increase counters measuring data
   275    // structure depth and amount, and possibly bails out with false if limits set
   276    // by the constructor have been hit. Needs to be balanced with EndTable().
   277    bool VerifyComplexity() {
   278      depth_++;
   279      num_tables_++;
   280      return Check(depth_ <= opts_.max_depth && num_tables_ <= opts_.max_tables);
   281    }
   282  
   283    // Called at the end of a table to pop the depth count.
   284    bool EndTable() {
   285      depth_--;
   286      return true;
   287    }
   288  
   289    // Returns the message size in bytes
   290    size_t GetComputedSize() const {
   291      // clang-format off
   292      #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
   293        uintptr_t size = upper_bound_;
   294        // Align the size to uoffset_t
   295        size = (size - 1 + sizeof(uoffset_t)) & ~(sizeof(uoffset_t) - 1);
   296        return (size > size_) ?  0 : size;
   297      #else
   298        // Must turn on FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE for this to work.
   299        (void)upper_bound_;
   300        FLATBUFFERS_ASSERT(false);
   301        return 0;
   302      #endif
   303      // clang-format on
   304    }
   305  
   306    std::vector<uint8_t> *GetFlexReuseTracker() { return flex_reuse_tracker_; }
   307  
   308    void SetFlexReuseTracker(std::vector<uint8_t> *const rt) {
   309      flex_reuse_tracker_ = rt;
   310    }
   311  
   312   private:
   313    const uint8_t *buf_;
   314    const size_t size_;
   315    const Options opts_;
   316  
   317    mutable size_t upper_bound_ = 0;
   318  
   319    uoffset_t depth_ = 0;
   320    uoffset_t num_tables_ = 0;
   321    std::vector<uint8_t> *flex_reuse_tracker_ = nullptr;
   322  };
   323  
   324  // Specialization for 64-bit offsets.
   325  template<>
   326  inline size_t Verifier::VerifyOffset<uoffset64_t>(const size_t start) const {
   327    return VerifyOffset<uoffset64_t, soffset64_t>(start);
   328  }
   329  
   330  }  // namespace flatbuffers
   331  
   332  #endif  // FLATBUFFERS_VERIFIER_H_