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