• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Verifier(const uint8_t * const buf,const size_t buf_len,const Options & opts)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.
Check(const bool ok)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.
Verify(const size_t elem,const size_t elem_len)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 
VerifyAlignment(const size_t elem,const size_t align)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).
Verify(const size_t elem)92   template<typename T> bool Verify(const size_t elem) const {
93     return VerifyAlignment(elem, sizeof(T)) && Verify(elem, sizeof(T));
94   }
95 
VerifyFromPointer(const uint8_t * const p,const size_t len)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.
VerifyFieldStruct(const uint8_t * const base,const voffset_t elem_off,const size_t elem_len,const size_t align)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>
VerifyField(const uint8_t * const base,const voffset_t elem_off,const size_t align)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.
VerifyTable(const T * const table)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>
VerifyVector(const Vector<T,LenT> * const vec)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>
VerifyVector(const Vector<const T *,LenT> * const vec)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.
VerifyString(const String * const str)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.
VerifyVectorOfStrings(const Vector<Offset<String>> * const vec)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>
VerifyVectorOfTables(const Vector<Offset<T>> * const vec)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")
VerifyTableStart(const uint8_t * const table)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>
VerifyBufferFromStart(const char * const identifier,const size_t start)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>
VerifyNestedFlatBuffer(const Vector<uint8_t,SizeT> * const buf,const char * const identifier)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.
VerifyBuffer()240   template<typename T> bool VerifyBuffer() { return VerifyBuffer<T>(nullptr); }
241 
VerifyBuffer(const char * const identifier)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>
VerifySizePrefixedBuffer(const char * const identifier)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>
VerifyOffset(const size_t start)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>
VerifyOffset(const uint8_t * const base,const voffset_t start)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().
VerifyComplexity()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.
EndTable()284   bool EndTable() {
285     depth_--;
286     return true;
287   }
288 
289   // Returns the message size in bytes
GetComputedSize()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 
GetFlexReuseTracker()306   std::vector<uint8_t> *GetFlexReuseTracker() { return flex_reuse_tracker_; }
307 
SetFlexReuseTracker(std::vector<uint8_t> * const rt)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_
333