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 Verifier(const uint8_t *buf, size_t buf_len, uoffset_t _max_depth = 64,
29 uoffset_t _max_tables = 1000000, bool _check_alignment = true)
30 : buf_(buf),
31 size_(buf_len),
32 depth_(0),
33 max_depth_(_max_depth),
34 num_tables_(0),
35 max_tables_(_max_tables),
36 upper_bound_(0),
37 check_alignment_(_check_alignment),
38 flex_reuse_tracker_(nullptr) {
39 FLATBUFFERS_ASSERT(size_ < FLATBUFFERS_MAX_BUFFER_SIZE);
40 }
41
42 // Central location where any verification failures register.
43 bool Check(bool ok) const {
44 // clang-format off
45 #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE
46 FLATBUFFERS_ASSERT(ok);
47 #endif
48 #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
49 if (!ok)
50 upper_bound_ = 0;
51 #endif
52 // clang-format on
53 return ok;
54 }
55
56 // Verify any range within the buffer.
57 bool Verify(size_t elem, size_t elem_len) const {
58 // clang-format off
59 #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
60 auto upper_bound = elem + elem_len;
61 if (upper_bound_ < upper_bound)
62 upper_bound_ = upper_bound;
63 #endif
64 // clang-format on
65 return Check(ok: elem_len < size_ && elem <= size_ - elem_len);
66 }
67
68 bool VerifyAlignment(size_t elem, size_t align) const {
69 return Check(ok: (elem & (align - 1)) == 0 || !check_alignment_);
70 }
71
72 // Verify a range indicated by sizeof(T).
73 template<typename T> bool Verify(size_t elem) const {
74 return VerifyAlignment(elem, align: sizeof(T)) && Verify(elem, elem_len: sizeof(T));
75 }
76
77 bool VerifyFromPointer(const uint8_t *p, size_t len) {
78 auto o = static_cast<size_t>(p - buf_);
79 return Verify(elem: o, elem_len: len);
80 }
81
82 // Verify relative to a known-good base pointer.
83 bool VerifyFieldStruct(const uint8_t *base, voffset_t elem_off,
84 size_t elem_len, size_t align) const {
85 auto f = static_cast<size_t>(base - buf_) + elem_off;
86 return VerifyAlignment(elem: f, align) && Verify(elem: f, elem_len);
87 }
88
89 template<typename T>
90 bool VerifyField(const uint8_t *base, voffset_t elem_off,
91 size_t align) const {
92 auto f = static_cast<size_t>(base - buf_) + elem_off;
93 return VerifyAlignment(elem: f, align) && Verify(elem: f, elem_len: sizeof(T));
94 }
95
96 // Verify a pointer (may be NULL) of a table type.
97 template<typename T> bool VerifyTable(const T *table) {
98 return !table || table->Verify(*this);
99 }
100
101 // Verify a pointer (may be NULL) of any vector type.
102 template<typename T> bool VerifyVector(const Vector<T> *vec) const {
103 return !vec || VerifyVectorOrString(vec: reinterpret_cast<const uint8_t *>(vec),
104 elem_size: sizeof(T));
105 }
106
107 // Verify a pointer (may be NULL) of a vector to struct.
108 template<typename T> bool VerifyVector(const Vector<const T *> *vec) const {
109 return VerifyVector(reinterpret_cast<const Vector<T> *>(vec));
110 }
111
112 // Verify a pointer (may be NULL) to string.
113 bool VerifyString(const String *str) const {
114 size_t end;
115 return !str || (VerifyVectorOrString(vec: reinterpret_cast<const uint8_t *>(str),
116 elem_size: 1, end: &end) &&
117 Verify(elem: end, elem_len: 1) && // Must have terminator
118 Check(ok: buf_[end] == '\0')); // Terminating byte must be 0.
119 }
120
121 // Common code between vectors and strings.
122 bool VerifyVectorOrString(const uint8_t *vec, size_t elem_size,
123 size_t *end = nullptr) const {
124 auto veco = static_cast<size_t>(vec - buf_);
125 // Check we can read the size field.
126 if (!Verify<uoffset_t>(elem: veco)) return false;
127 // Check the whole array. If this is a string, the byte past the array
128 // must be 0.
129 auto size = ReadScalar<uoffset_t>(p: vec);
130 auto max_elems = FLATBUFFERS_MAX_BUFFER_SIZE / elem_size;
131 if (!Check(ok: size < max_elems))
132 return false; // Protect against byte_size overflowing.
133 auto byte_size = sizeof(size) + elem_size * size;
134 if (end) *end = veco + byte_size;
135 return Verify(elem: veco, elem_len: byte_size);
136 }
137
138 // Special case for string contents, after the above has been called.
139 bool VerifyVectorOfStrings(const Vector<Offset<String>> *vec) const {
140 if (vec) {
141 for (uoffset_t i = 0; i < vec->size(); i++) {
142 if (!VerifyString(str: vec->Get(i))) return false;
143 }
144 }
145 return true;
146 }
147
148 // Special case for table contents, after the above has been called.
149 template<typename T> bool VerifyVectorOfTables(const Vector<Offset<T>> *vec) {
150 if (vec) {
151 for (uoffset_t i = 0; i < vec->size(); i++) {
152 if (!vec->Get(i)->Verify(*this)) return false;
153 }
154 }
155 return true;
156 }
157
158 __supress_ubsan__("unsigned-integer-overflow") bool VerifyTableStart(
159 const uint8_t *table) {
160 // Check the vtable offset.
161 auto tableo = static_cast<size_t>(table - buf_);
162 if (!Verify<soffset_t>(elem: tableo)) return false;
163 // This offset may be signed, but doing the subtraction unsigned always
164 // gives the result we want.
165 auto vtableo = tableo - static_cast<size_t>(ReadScalar<soffset_t>(p: table));
166 // Check the vtable size field, then check vtable fits in its entirety.
167 if (!( VerifyComplexity() && Verify<voffset_t>(elem: vtableo) &&
168 VerifyAlignment(elem: ReadScalar<voffset_t>(p: buf_ + vtableo),
169 align: sizeof(voffset_t)))) return false;
170 auto vsize = ReadScalar<voffset_t>(p: buf_ + vtableo);
171 return Check(ok: (vsize & 1) == 0) && Verify(elem: vtableo, elem_len: vsize);
172 }
173
174 template<typename T>
175 bool VerifyBufferFromStart(const char *identifier, size_t start) {
176 if (identifier && !Check(ok: (size_ >= 2 * sizeof(flatbuffers::uoffset_t) &&
177 BufferHasIdentifier(buf: buf_ + start, identifier)))) {
178 return false;
179 }
180
181 // Call T::Verify, which must be in the generated code for this type.
182 auto o = VerifyOffset(start);
183 return o && reinterpret_cast<const T *>(buf_ + start + o)->Verify(*this)
184 // clang-format off
185 #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
186 && GetComputedSize()
187 #endif
188 ;
189 // clang-format on
190 }
191
192 template<typename T>
193 bool VerifyNestedFlatBuffer(const Vector<uint8_t> *buf,
194 const char *identifier) {
195 if (!buf) return true;
196 Verifier nested_verifier(buf->data(), buf->size());
197 return nested_verifier.VerifyBuffer<T>(identifier);
198 }
199
200 // Verify this whole buffer, starting with root type T.
201 template<typename T> bool VerifyBuffer() { return VerifyBuffer<T>(nullptr); }
202
203 template<typename T> bool VerifyBuffer(const char *identifier) {
204 return VerifyBufferFromStart<T>(identifier, 0);
205 }
206
207 template<typename T> bool VerifySizePrefixedBuffer(const char *identifier) {
208 return Verify<uoffset_t>(elem: 0U) &&
209 ReadScalar<uoffset_t>(p: buf_) == size_ - sizeof(uoffset_t) &&
210 VerifyBufferFromStart<T>(identifier, sizeof(uoffset_t));
211 }
212
213 uoffset_t VerifyOffset(size_t start) const {
214 if (!Verify<uoffset_t>(elem: start)) return 0;
215 auto o = ReadScalar<uoffset_t>(p: buf_ + start);
216 // May not point to itself.
217 if (!Check(ok: o != 0)) return 0;
218 // Can't wrap around / buffers are max 2GB.
219 if (!Check(ok: static_cast<soffset_t>(o) >= 0)) return 0;
220 // Must be inside the buffer to create a pointer from it (pointer outside
221 // buffer is UB).
222 if (!Verify(elem: start + o, elem_len: 1)) return 0;
223 return o;
224 }
225
226 uoffset_t VerifyOffset(const uint8_t *base, voffset_t start) const {
227 return VerifyOffset(start: static_cast<size_t>(base - buf_) + start);
228 }
229
230 // Called at the start of a table to increase counters measuring data
231 // structure depth and amount, and possibly bails out with false if
232 // limits set by the constructor have been hit. Needs to be balanced
233 // with EndTable().
234 bool VerifyComplexity() {
235 depth_++;
236 num_tables_++;
237 return Check(ok: depth_ <= max_depth_ && num_tables_ <= max_tables_);
238 }
239
240 // Called at the end of a table to pop the depth count.
241 bool EndTable() {
242 depth_--;
243 return true;
244 }
245
246 // Returns the message size in bytes
247 size_t GetComputedSize() const {
248 // clang-format off
249 #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
250 uintptr_t size = upper_bound_;
251 // Align the size to uoffset_t
252 size = (size - 1 + sizeof(uoffset_t)) & ~(sizeof(uoffset_t) - 1);
253 return (size > size_) ? 0 : size;
254 #else
255 // Must turn on FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE for this to work.
256 (void)upper_bound_;
257 FLATBUFFERS_ASSERT(false);
258 return 0;
259 #endif
260 // clang-format on
261 }
262
263 std::vector<uint8_t> *GetFlexReuseTracker() { return flex_reuse_tracker_; }
264
265 void SetFlexReuseTracker(std::vector<uint8_t> *rt) {
266 flex_reuse_tracker_ = rt;
267 }
268
269 private:
270 const uint8_t *buf_;
271 size_t size_;
272 uoffset_t depth_;
273 uoffset_t max_depth_;
274 uoffset_t num_tables_;
275 uoffset_t max_tables_;
276 mutable size_t upper_bound_;
277 bool check_alignment_;
278 std::vector<uint8_t> *flex_reuse_tracker_;
279};
280
281} // namespace flatbuffers
282
283#endif // FLATBUFFERS_VERIFIER_H_
284

source code of flutter_engine/third_party/flatbuffers/include/flatbuffers/verifier.h