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_
|