blob: 87d3f54a5db2f43c794014ffbc3d6c6c7c10ab76 [file] [log] [blame]
James Kuszmaul8e62b022022-03-22 09:33:25 -07001/*
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"
James Kuszmaul8e62b022022-03-22 09:33:25 -070021#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:
Austin Schuh2dd86a92022-09-14 21:19:23 -070028 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 explicit Verifier(const uint8_t *const buf, const size_t buf_len,
40 const Options &opts)
41 : buf_(buf), size_(buf_len), opts_(opts) {
James Kuszmaul8e62b022022-03-22 09:33:25 -070042 FLATBUFFERS_ASSERT(size_ < FLATBUFFERS_MAX_BUFFER_SIZE);
43 }
44
Austin Schuh2dd86a92022-09-14 21:19:23 -070045 // Deprecated API, please construct with Verifier::Options.
46 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 : Verifier(buf, buf_len, [&] {
50 Options opts;
51 opts.max_depth = max_depth;
52 opts.max_tables = max_tables;
53 opts.check_alignment = check_alignment;
54 return opts;
55 }()) {}
56
James Kuszmaul8e62b022022-03-22 09:33:25 -070057 // Central location where any verification failures register.
Austin Schuh2dd86a92022-09-14 21:19:23 -070058 bool Check(const bool ok) const {
James Kuszmaul8e62b022022-03-22 09:33:25 -070059 // 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 return ok;
69 }
70
71 // Verify any range within the buffer.
Austin Schuh2dd86a92022-09-14 21:19:23 -070072 bool Verify(const size_t elem, const size_t elem_len) const {
James Kuszmaul8e62b022022-03-22 09:33:25 -070073 // 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 return Check(elem_len < size_ && elem <= size_ - elem_len);
81 }
82
Austin Schuh2dd86a92022-09-14 21:19:23 -070083 bool VerifyAlignment(const size_t elem, const size_t align) const {
84 return Check((elem & (align - 1)) == 0 || !opts_.check_alignment);
James Kuszmaul8e62b022022-03-22 09:33:25 -070085 }
86
87 // Verify a range indicated by sizeof(T).
Austin Schuh2dd86a92022-09-14 21:19:23 -070088 template<typename T> bool Verify(const size_t elem) const {
James Kuszmaul8e62b022022-03-22 09:33:25 -070089 return VerifyAlignment(elem, sizeof(T)) && Verify(elem, sizeof(T));
90 }
91
Austin Schuh2dd86a92022-09-14 21:19:23 -070092 bool VerifyFromPointer(const uint8_t *const p, const size_t len) {
93 return Verify(static_cast<size_t>(p - buf_), len);
James Kuszmaul8e62b022022-03-22 09:33:25 -070094 }
95
96 // Verify relative to a known-good base pointer.
Austin Schuh2dd86a92022-09-14 21:19:23 -070097 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;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700100 return VerifyAlignment(f, align) && Verify(f, elem_len);
101 }
102
103 template<typename T>
Austin Schuh2dd86a92022-09-14 21:19:23 -0700104 bool VerifyField(const uint8_t *const base, const voffset_t elem_off,
105 const size_t align) const {
106 const auto f = static_cast<size_t>(base - buf_) + elem_off;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700107 return VerifyAlignment(f, align) && Verify(f, sizeof(T));
108 }
109
110 // Verify a pointer (may be NULL) of a table type.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700111 template<typename T> bool VerifyTable(const T *const table) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700112 return !table || table->Verify(*this);
113 }
114
115 // Verify a pointer (may be NULL) of any vector type.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700116 template<typename T> bool VerifyVector(const Vector<T> *const vec) const {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700117 return !vec || VerifyVectorOrString(reinterpret_cast<const uint8_t *>(vec),
118 sizeof(T));
119 }
120
121 // Verify a pointer (may be NULL) of a vector to struct.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700122 template<typename T>
123 bool VerifyVector(const Vector<const T *> *const vec) const {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700124 return VerifyVector(reinterpret_cast<const Vector<T> *>(vec));
125 }
126
127 // Verify a pointer (may be NULL) to string.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700128 bool VerifyString(const String *const str) const {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700129 size_t end;
130 return !str || (VerifyVectorOrString(reinterpret_cast<const uint8_t *>(str),
131 1, &end) &&
132 Verify(end, 1) && // Must have terminator
133 Check(buf_[end] == '\0')); // Terminating byte must be 0.
134 }
135
136 // Common code between vectors and strings.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700137 bool VerifyVectorOrString(const uint8_t *const vec, const size_t elem_size,
138 size_t *const end = nullptr) const {
139 const auto veco = static_cast<size_t>(vec - buf_);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700140 // Check we can read the size field.
141 if (!Verify<uoffset_t>(veco)) return false;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700142 // Check the whole array. If this is a string, the byte past the array must
143 // be 0.
144 const auto size = ReadScalar<uoffset_t>(vec);
145 const auto max_elems = FLATBUFFERS_MAX_BUFFER_SIZE / elem_size;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700146 if (!Check(size < max_elems))
147 return false; // Protect against byte_size overflowing.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700148 const auto byte_size = sizeof(size) + elem_size * size;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700149 if (end) *end = veco + byte_size;
150 return Verify(veco, byte_size);
151 }
152
153 // Special case for string contents, after the above has been called.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700154 bool VerifyVectorOfStrings(const Vector<Offset<String>> *const vec) const {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700155 if (vec) {
156 for (uoffset_t i = 0; i < vec->size(); i++) {
157 if (!VerifyString(vec->Get(i))) return false;
158 }
159 }
160 return true;
161 }
162
163 // Special case for table contents, after the above has been called.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700164 template<typename T>
165 bool VerifyVectorOfTables(const Vector<Offset<T>> *const vec) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700166 if (vec) {
167 for (uoffset_t i = 0; i < vec->size(); i++) {
168 if (!vec->Get(i)->Verify(*this)) return false;
169 }
170 }
171 return true;
172 }
173
Austin Schuh2dd86a92022-09-14 21:19:23 -0700174 __suppress_ubsan__("unsigned-integer-overflow") bool VerifyTableStart(
175 const uint8_t *const table) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700176 // Check the vtable offset.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700177 const auto tableo = static_cast<size_t>(table - buf_);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700178 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.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700181 const auto vtableo =
182 tableo - static_cast<size_t>(ReadScalar<soffset_t>(table));
James Kuszmaul8e62b022022-03-22 09:33:25 -0700183 // Check the vtable size field, then check vtable fits in its entirety.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700184 if (!(VerifyComplexity() && Verify<voffset_t>(vtableo) &&
185 VerifyAlignment(ReadScalar<voffset_t>(buf_ + vtableo),
186 sizeof(voffset_t))))
187 return false;
188 const auto vsize = ReadScalar<voffset_t>(buf_ + vtableo);
189 return Check((vsize & 1) == 0) && Verify(vtableo, vsize);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700190 }
191
192 template<typename T>
Austin Schuh2dd86a92022-09-14 21:19:23 -0700193 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 if (!Check(size_ >= FLATBUFFERS_MIN_BUFFER_SIZE)) return false;
198
199 // If an identifier is provided, check that we have a buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700200 if (identifier && !Check((size_ >= 2 * sizeof(flatbuffers::uoffset_t) &&
201 BufferHasIdentifier(buf_ + start, identifier)))) {
202 return false;
203 }
204
205 // Call T::Verify, which must be in the generated code for this type.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700206 const auto o = VerifyOffset(start);
207 return Check(o != 0) &&
208 reinterpret_cast<const T *>(buf_ + start + o)->Verify(*this)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700209 // 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>
Austin Schuh2dd86a92022-09-14 21:19:23 -0700218 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.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700224 if (!buf) return true;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700225
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
James Kuszmaul8e62b022022-03-22 09:33:25 -0700229 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
Austin Schuh2dd86a92022-09-14 21:19:23 -0700236 template<typename T> bool VerifyBuffer(const char *const identifier) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700237 return VerifyBufferFromStart<T>(identifier, 0);
238 }
239
Austin Schuh2dd86a92022-09-14 21:19:23 -0700240 template<typename T>
241 bool VerifySizePrefixedBuffer(const char *const identifier) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700242 return Verify<uoffset_t>(0U) &&
Austin Schuh2dd86a92022-09-14 21:19:23 -0700243 Check(ReadScalar<uoffset_t>(buf_) == size_ - sizeof(uoffset_t)) &&
James Kuszmaul8e62b022022-03-22 09:33:25 -0700244 VerifyBufferFromStart<T>(identifier, sizeof(uoffset_t));
245 }
246
Austin Schuh2dd86a92022-09-14 21:19:23 -0700247 uoffset_t VerifyOffset(const size_t start) const {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700248 if (!Verify<uoffset_t>(start)) return 0;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700249 const auto o = ReadScalar<uoffset_t>(buf_ + start);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700250 // May not point to itself.
251 if (!Check(o != 0)) return 0;
252 // Can't wrap around / buffers are max 2GB.
253 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 if (!Verify(start + o, 1)) return 0;
257 return o;
258 }
259
Austin Schuh2dd86a92022-09-14 21:19:23 -0700260 uoffset_t VerifyOffset(const uint8_t *const base,
261 const voffset_t start) const {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700262 return VerifyOffset(static_cast<size_t>(base - buf_) + start);
263 }
264
265 // Called at the start of a table to increase counters measuring data
Austin Schuh2dd86a92022-09-14 21:19:23 -0700266 // 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().
James Kuszmaul8e62b022022-03-22 09:33:25 -0700268 bool VerifyComplexity() {
269 depth_++;
270 num_tables_++;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700271 return Check(depth_ <= opts_.max_depth && num_tables_ <= opts_.max_tables);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700272 }
273
274 // Called at the end of a table to pop the depth count.
275 bool EndTable() {
276 depth_--;
277 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 std::vector<uint8_t> *GetFlexReuseTracker() { return flex_reuse_tracker_; }
298
Austin Schuh2dd86a92022-09-14 21:19:23 -0700299 void SetFlexReuseTracker(std::vector<uint8_t> *const rt) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700300 flex_reuse_tracker_ = rt;
301 }
302
303 private:
304 const uint8_t *buf_;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700305 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;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700313};
314
315} // namespace flatbuffers
316
317#endif // FLATBUFFERS_VERIFIER_H_