ONE - On-device Neural Engine
Loading...
Searching...
No Matches
verifier.h
Go to the documentation of this file.
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
23namespace flatbuffers {
24
25// Helper class to verify the integrity of a FlatBuffer
26class 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>
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().
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.
325template<>
326inline 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_
#define FLATBUFFERS_ASSERT
Definition base.h:21
bool VerifyVector(const Vector< T, LenT > *const vec) const
Definition verifier.h:121
bool VerifyField(const uint8_t *const base, const voffset_t elem_off, const size_t align) const
Definition verifier.h:108
bool Verify(const size_t elem) const
Definition verifier.h:92
bool VerifyVectorOrString(const uint8_t *const vec, const size_t elem_size, size_t *const end=nullptr) const
Definition verifier.h:143
bool VerifyFromPointer(const uint8_t *const p, const size_t len)
Definition verifier.h:96
bool VerifyBufferFromStart(const char *const identifier, const size_t start)
Definition verifier.h:199
bool Verify(const size_t elem, const size_t elem_len) const
Definition verifier.h:76
bool VerifyString(const String *const str) const
Definition verifier.h:133
bool VerifyFieldStruct(const uint8_t *const base, const voffset_t elem_off, const size_t elem_len, const size_t align) const
Definition verifier.h:101
void SetFlexReuseTracker(std::vector< uint8_t > *const rt)
Definition verifier.h:308
bool VerifyAlignment(const size_t elem, const size_t align) const
Definition verifier.h:87
bool Check(const bool ok) const
Definition verifier.h:62
bool VerifySizePrefixedBuffer(const char *const identifier)
Definition verifier.h:247
size_t VerifyOffset(const uint8_t *const base, const voffset_t start) const
Definition verifier.h:270
std::vector< uint8_t > * GetFlexReuseTracker()
Definition verifier.h:306
bool VerifyVectorOfTables(const Vector< Offset< T > > *const vec)
Definition verifier.h:171
size_t VerifyOffset(const size_t start) const
Definition verifier.h:256
bool VerifyVectorOfStrings(const Vector< Offset< String > > *const vec) const
Definition verifier.h:160
bool VerifyBuffer(const char *const identifier)
Definition verifier.h:242
bool VerifyVector(const Vector< const T *, LenT > *const vec) const
Definition verifier.h:128
bool VerifyNestedFlatBuffer(const Vector< uint8_t, SizeT > *const buf, const char *const identifier)
Definition verifier.h:224
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)
Definition verifier.h:50
Verifier(const uint8_t *const buf, const size_t buf_len, const Options &opts)
Definition verifier.h:43
bool VerifyTable(const T *const table)
Definition verifier.h:115
bool Verify(const reflection::Schema &schema, const reflection::Object &root, const uint8_t *buf, size_t length, uoffset_t max_depth=64, uoffset_t max_tables=1000000)
int32_t size[5]
Definition Slice.cpp:35
Configuration p