LCOV - code coverage report
Current view: top level - third_party/flatbuffers - verifier.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 86 91 94.5 %
Date: 2026-06-19 21:24:00 Functions: 84 94 89.4 %

          Line data    Source code
       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             :   };
      38             : 
      39        7360 :   explicit Verifier(const uint8_t *const buf, const size_t buf_len,
      40             :                     const Options &opts)
      41        7360 :       : buf_(buf), size_(buf_len), opts_(opts) {
      42        7360 :     FLATBUFFERS_ASSERT(size_ < FLATBUFFERS_MAX_BUFFER_SIZE);
      43        7360 :   }
      44             : 
      45             :   // Deprecated API, please construct with Verifier::Options.
      46        7360 :   Verifier(const uint8_t *const buf, const size_t buf_len,
      47             :            const uoffset_t max_depth = 64, const uoffset_t max_tables = 1000000,
      48             :            const bool check_alignment = true)
      49       14720 :       : Verifier(buf, buf_len, [&] {
      50        7360 :           Options opts;
      51        7360 :           opts.max_depth = max_depth;
      52        7360 :           opts.max_tables = max_tables;
      53        7360 :           opts.check_alignment = check_alignment;
      54        7360 :           return opts;
      55        7360 :         }()) {}
      56             : 
      57             :   // Central location where any verification failures register.
      58     3530750 :   bool Check(const bool ok) const {
      59             :     // clang-format off
      60             :     #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE
      61             :       FLATBUFFERS_ASSERT(ok);
      62             :     #endif
      63             :     #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
      64             :       if (!ok)
      65             :         upper_bound_ = 0;
      66             :     #endif
      67             :     // clang-format on
      68     3530750 :     return ok;
      69             :   }
      70             : 
      71             :   // Verify any range within the buffer.
      72     1443410 :   bool Verify(const size_t elem, const size_t elem_len) const {
      73             :     // clang-format off
      74             :     #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
      75             :       auto upper_bound = elem + elem_len;
      76             :       if (upper_bound_ < upper_bound)
      77             :         upper_bound_ =  upper_bound;
      78             :     #endif
      79             :     // clang-format on
      80     1443410 :     return Check(elem_len < size_ && elem <= size_ - elem_len);
      81             :   }
      82             : 
      83      977809 :   bool VerifyAlignment(const size_t elem, const size_t align) const {
      84      977809 :     return Check((elem & (align - 1)) == 0 || !opts_.check_alignment);
      85             :   }
      86             : 
      87             :   // Verify a range indicated by sizeof(T).
      88      624812 :   template<typename T> bool Verify(const size_t elem) const {
      89      624812 :     return VerifyAlignment(elem, sizeof(T)) && Verify(elem, sizeof(T));
      90             :   }
      91             : 
      92             :   bool VerifyFromPointer(const uint8_t *const p, const size_t len) {
      93             :     return Verify(static_cast<size_t>(p - buf_), len);
      94             :   }
      95             : 
      96             :   // Verify relative to a known-good base pointer.
      97             :   bool VerifyFieldStruct(const uint8_t *const base, const voffset_t elem_off,
      98             :                          const size_t elem_len, const size_t align) const {
      99             :     const auto f = static_cast<size_t>(base - buf_) + elem_off;
     100             :     return VerifyAlignment(f, align) && Verify(f, elem_len);
     101             :   }
     102             : 
     103             :   template<typename T>
     104      221685 :   bool VerifyField(const uint8_t *const base, const voffset_t elem_off,
     105             :                    const size_t align) const {
     106      221685 :     const auto f = static_cast<size_t>(base - buf_) + elem_off;
     107      221685 :     return VerifyAlignment(f, align) && Verify(f, sizeof(T));
     108             :   }
     109             : 
     110             :   // Verify a pointer (may be NULL) of a table type.
     111       13723 :   template<typename T> bool VerifyTable(const T *const table) {
     112       13723 :     return !table || table->Verify(*this);
     113             :   }
     114             : 
     115             :   // Verify a pointer (may be NULL) of any vector type.
     116      133991 :   template<typename T> bool VerifyVector(const Vector<T> *const vec) const {
     117      133991 :     return !vec || VerifyVectorOrString(reinterpret_cast<const uint8_t *>(vec),
     118      133991 :                                         sizeof(T));
     119             :   }
     120             : 
     121             :   // Verify a pointer (may be NULL) of a vector to struct.
     122             :   template<typename T>
     123        6665 :   bool VerifyVector(const Vector<const T *> *const vec) const {
     124        6665 :     return VerifyVector(reinterpret_cast<const Vector<T> *>(vec));
     125             :   }
     126             : 
     127             :   // Verify a pointer (may be NULL) to string.
     128      322474 :   bool VerifyString(const String *const str) const {
     129             :     size_t end;
     130      425890 :     return !str || (VerifyVectorOrString(reinterpret_cast<const uint8_t *>(str),
     131      103416 :                                          1, &end) &&
     132      206832 :                     Verify(end, 1) &&           // Must have terminator
     133      425890 :                     Check(buf_[end] == '\0'));  // Terminating byte must be 0.
     134             :   }
     135             : 
     136             :   // Common code between vectors and strings.
     137      170624 :   bool VerifyVectorOrString(const uint8_t *const vec, const size_t elem_size,
     138             :                             size_t *const end = nullptr) const {
     139      170624 :     const auto veco = static_cast<size_t>(vec - buf_);
     140             :     // Check we can read the size field.
     141      170624 :     if (!Verify<uoffset_t>(veco)) return false;
     142             :     // Check the whole array. If this is a string, the byte past the array must
     143             :     // be 0.
     144      170624 :     const auto size = ReadScalar<uoffset_t>(vec);
     145      170624 :     const auto max_elems = FLATBUFFERS_MAX_BUFFER_SIZE / elem_size;
     146      170624 :     if (!Check(size < max_elems))
     147           0 :       return false;  // Protect against byte_size overflowing.
     148      170624 :     const auto byte_size = sizeof(size) + elem_size * size;
     149      170624 :     if (end) *end = veco + byte_size;
     150      170624 :     return Verify(veco, byte_size);
     151             :   }
     152             : 
     153             :   // Special case for string contents, after the above has been called.
     154        2194 :   bool VerifyVectorOfStrings(const Vector<Offset<String>> *const vec) const {
     155        2194 :     if (vec) {
     156        2194 :       for (uoffset_t i = 0; i < vec->size(); i++) {
     157           0 :         if (!VerifyString(vec->Get(i))) return false;
     158             :       }
     159             :     }
     160        2194 :     return true;
     161             :   }
     162             : 
     163             :   // Special case for table contents, after the above has been called.
     164             :   template<typename T>
     165       36310 :   bool VerifyVectorOfTables(const Vector<Offset<T>> *const vec) {
     166       36310 :     if (vec) {
     167      142823 :       for (uoffset_t i = 0; i < vec->size(); i++) {
     168      110374 :         if (!vec->Get(i)->Verify(*this)) return false;
     169             :       }
     170             :     }
     171       36310 :     return true;
     172             :   }
     173             : 
     174      131313 :   __suppress_ubsan__("unsigned-integer-overflow") bool VerifyTableStart(
     175             :       const uint8_t *const table) {
     176             :     // Check the vtable offset.
     177      131313 :     const auto tableo = static_cast<size_t>(table - buf_);
     178      131313 :     if (!Verify<soffset_t>(tableo)) return false;
     179             :     // This offset may be signed, but doing the subtraction unsigned always
     180             :     // gives the result we want.
     181             :     const auto vtableo =
     182      131312 :         tableo - static_cast<size_t>(ReadScalar<soffset_t>(table));
     183             :     // Check the vtable size field, then check vtable fits in its entirety.
     184      262624 :     if (!(VerifyComplexity() && Verify<voffset_t>(vtableo) &&
     185      131312 :           VerifyAlignment(ReadScalar<voffset_t>(buf_ + vtableo),
     186             :                           sizeof(voffset_t))))
     187           0 :       return false;
     188      131312 :     const auto vsize = ReadScalar<voffset_t>(buf_ + vtableo);
     189      131312 :     return Check((vsize & 1) == 0) && Verify(vtableo, vsize);
     190             :   }
     191             : 
     192             :   template<typename T>
     193        7360 :   bool VerifyBufferFromStart(const char *const identifier, const size_t start) {
     194             :     // Buffers have to be of some size to be valid. The reason it is a runtime
     195             :     // check instead of static_assert, is that nested flatbuffers go through
     196             :     // this call and their size is determined at runtime.
     197        7360 :     if (!Check(size_ >= FLATBUFFERS_MIN_BUFFER_SIZE)) return false;
     198             : 
     199             :     // If an identifier is provided, check that we have a buffer
     200        7356 :     if (identifier && !Check((size_ >= 2 * sizeof(flatbuffers::uoffset_t) &&
     201           0 :                               BufferHasIdentifier(buf_ + start, identifier)))) {
     202           0 :       return false;
     203             :     }
     204             : 
     205             :     // Call T::Verify, which must be in the generated code for this type.
     206        7356 :     const auto o = VerifyOffset(start);
     207       14712 :     return Check(o != 0) &&
     208       14712 :            reinterpret_cast<const T *>(buf_ + start + o)->Verify(*this)
     209             :     // clang-format off
     210             :     #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
     211             :            && GetComputedSize()
     212             :     #endif
     213             :         ;
     214             :     // clang-format on
     215             :   }
     216             : 
     217             :   template<typename T>
     218             :   bool VerifyNestedFlatBuffer(const Vector<uint8_t> *const buf,
     219             :                               const char *const identifier) {
     220             :     // Caller opted out of this.
     221             :     if (!opts_.check_nested_flatbuffers) return true;
     222             : 
     223             :     // An empty buffer is OK as it indicates not present.
     224             :     if (!buf) return true;
     225             : 
     226             :     // If there is a nested buffer, it must be greater than the min size.
     227             :     if (!Check(buf->size() >= FLATBUFFERS_MIN_BUFFER_SIZE)) return false;
     228             : 
     229             :     Verifier nested_verifier(buf->data(), buf->size());
     230             :     return nested_verifier.VerifyBuffer<T>(identifier);
     231             :   }
     232             : 
     233             :   // Verify this whole buffer, starting with root type T.
     234             :   template<typename T> bool VerifyBuffer() { return VerifyBuffer<T>(nullptr); }
     235             : 
     236        7360 :   template<typename T> bool VerifyBuffer(const char *const identifier) {
     237        7360 :     return VerifyBufferFromStart<T>(identifier, 0);
     238             :   }
     239             : 
     240             :   template<typename T>
     241             :   bool VerifySizePrefixedBuffer(const char *const identifier) {
     242             :     return Verify<uoffset_t>(0U) &&
     243             :            Check(ReadScalar<uoffset_t>(buf_) == size_ - sizeof(uoffset_t)) &&
     244             :            VerifyBufferFromStart<T>(identifier, sizeof(uoffset_t));
     245             :   }
     246             : 
     247      191563 :   uoffset_t VerifyOffset(const size_t start) const {
     248      191563 :     if (!Verify<uoffset_t>(start)) return 0;
     249      191563 :     const auto o = ReadScalar<uoffset_t>(buf_ + start);
     250             :     // May not point to itself.
     251      191563 :     if (!Check(o != 0)) return 0;
     252             :     // Can't wrap around / buffers are max 2GB.
     253      191563 :     if (!Check(static_cast<soffset_t>(o) >= 0)) return 0;
     254             :     // Must be inside the buffer to create a pointer from it (pointer outside
     255             :     // buffer is UB).
     256      191563 :     if (!Verify(start + o, 1)) return 0;
     257      191563 :     return o;
     258             :   }
     259             : 
     260      184207 :   uoffset_t VerifyOffset(const uint8_t *const base,
     261             :                          const voffset_t start) const {
     262      184207 :     return VerifyOffset(static_cast<size_t>(base - buf_) + start);
     263             :   }
     264             : 
     265             :   // Called at the start of a table to increase counters measuring data
     266             :   // structure depth and amount, and possibly bails out with false if limits set
     267             :   // by the constructor have been hit. Needs to be balanced with EndTable().
     268      131312 :   bool VerifyComplexity() {
     269      131312 :     depth_++;
     270      131312 :     num_tables_++;
     271      131312 :     return Check(depth_ <= opts_.max_depth && num_tables_ <= opts_.max_tables);
     272             :   }
     273             : 
     274             :   // Called at the end of a table to pop the depth count.
     275      131312 :   bool EndTable() {
     276      131312 :     depth_--;
     277      131312 :     return true;
     278             :   }
     279             : 
     280             :   // Returns the message size in bytes
     281             :   size_t GetComputedSize() const {
     282             :     // clang-format off
     283             :     #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
     284             :       uintptr_t size = upper_bound_;
     285             :       // Align the size to uoffset_t
     286             :       size = (size - 1 + sizeof(uoffset_t)) & ~(sizeof(uoffset_t) - 1);
     287             :       return (size > size_) ?  0 : size;
     288             :     #else
     289             :       // Must turn on FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE for this to work.
     290             :       (void)upper_bound_;
     291             :       FLATBUFFERS_ASSERT(false);
     292             :       return 0;
     293             :     #endif
     294             :     // clang-format on
     295             :   }
     296             : 
     297        1057 :   std::vector<uint8_t> *GetFlexReuseTracker() { return flex_reuse_tracker_; }
     298             : 
     299             :   void SetFlexReuseTracker(std::vector<uint8_t> *const rt) {
     300             :     flex_reuse_tracker_ = rt;
     301             :   }
     302             : 
     303             :  private:
     304             :   const uint8_t *buf_;
     305             :   const size_t size_;
     306             :   const Options opts_;
     307             : 
     308             :   mutable size_t upper_bound_ = 0;
     309             : 
     310             :   uoffset_t depth_ = 0;
     311             :   uoffset_t num_tables_ = 0;
     312             :   std::vector<uint8_t> *flex_reuse_tracker_ = nullptr;
     313             : };
     314             : 
     315             : }  // namespace flatbuffers
     316             : 
     317             : #endif  // FLATBUFFERS_VERIFIER_H_

Generated by: LCOV version 1.14