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 852 : 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 852 : : 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 852 : flex_reuse_tracker_(nullptr) {
39 852 : FLATBUFFERS_ASSERT(size_ < FLATBUFFERS_MAX_BUFFER_SIZE);
40 852 : }
41 :
42 : // Central location where any verification failures register.
43 1617430 : 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 1617430 : return ok;
54 : }
55 :
56 : // Verify any range within the buffer.
57 673876 : 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 673876 : return Check(elem_len < size_ && elem <= size_ - elem_len);
66 : }
67 :
68 470981 : bool VerifyAlignment(size_t elem, size_t align) const {
69 470981 : return Check((elem & (align - 1)) == 0 || !check_alignment_);
70 : }
71 :
72 : // Verify a range indicated by sizeof(T).
73 271752 : template<typename T> bool Verify(size_t elem) const {
74 271752 : return VerifyAlignment(elem, sizeof(T)) && Verify(elem, 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(o, 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(f, align) && Verify(f, elem_len);
87 : }
88 :
89 : template<typename T>
90 131873 : bool VerifyField(const uint8_t *base, voffset_t elem_off,
91 : size_t align) const {
92 131873 : auto f = static_cast<size_t>(base - buf_) + elem_off;
93 131873 : return VerifyAlignment(f, align) && Verify(f, sizeof(T));
94 : }
95 :
96 : // Verify a pointer (may be NULL) of a table type.
97 852 : template<typename T> bool VerifyTable(const T *table) {
98 852 : return !table || table->Verify(*this);
99 : }
100 :
101 : // Verify a pointer (may be NULL) of any vector type.
102 7479 : template<typename T> bool VerifyVector(const Vector<T> *vec) const {
103 7479 : return !vec || VerifyVectorOrString(reinterpret_cast<const uint8_t *>(vec),
104 7479 : 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 263315 : bool VerifyString(const String *str) const {
114 : size_t end;
115 329170 : return !str || (VerifyVectorOrString(reinterpret_cast<const uint8_t *>(str),
116 65855 : 1, &end) &&
117 131710 : Verify(end, 1) && // Must have terminator
118 329170 : Check(buf_[end] == '\0')); // Terminating byte must be 0.
119 : }
120 :
121 : // Common code between vectors and strings.
122 67734 : bool VerifyVectorOrString(const uint8_t *vec, size_t elem_size,
123 : size_t *end = nullptr) const {
124 67734 : auto veco = static_cast<size_t>(vec - buf_);
125 : // Check we can read the size field.
126 67734 : if (!Verify<uoffset_t>(veco)) return false;
127 : // Check the whole array. If this is a string, the byte past the array
128 : // must be 0.
129 67734 : auto size = ReadScalar<uoffset_t>(vec);
130 67734 : auto max_elems = FLATBUFFERS_MAX_BUFFER_SIZE / elem_size;
131 67734 : if (!Check(size < max_elems))
132 0 : return false; // Protect against byte_size overflowing.
133 67734 : auto byte_size = sizeof(size) + elem_size * size;
134 67734 : if (end) *end = veco + byte_size;
135 67734 : return Verify(veco, 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(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 1677 : template<typename T> bool VerifyVectorOfTables(const Vector<Offset<T>> *vec) {
150 1677 : if (vec) {
151 65991 : for (uoffset_t i = 0; i < vec->size(); i++) {
152 65784 : if (!vec->Get(i)->Verify(*this)) return false;
153 : }
154 : }
155 1677 : return true;
156 : }
157 :
158 67356 : __supress_ubsan__("unsigned-integer-overflow") bool VerifyTableStart(
159 : const uint8_t *table) {
160 : // Check the vtable offset.
161 67356 : auto tableo = static_cast<size_t>(table - buf_);
162 67356 : if (!Verify<soffset_t>(tableo)) return false;
163 : // This offset may be signed, but doing the subtraction unsigned always
164 : // gives the result we want.
165 67356 : auto vtableo = tableo - static_cast<size_t>(ReadScalar<soffset_t>(table));
166 : // Check the vtable size field, then check vtable fits in its entirety.
167 134712 : if (!( VerifyComplexity() && Verify<voffset_t>(vtableo) &&
168 67356 : VerifyAlignment(ReadScalar<voffset_t>(buf_ + vtableo),
169 0 : sizeof(voffset_t)))) return false;
170 67356 : auto vsize = ReadScalar<voffset_t>(buf_ + vtableo);
171 67356 : return Check((vsize & 1) == 0) && Verify(vtableo, vsize);
172 : }
173 :
174 : template<typename T>
175 852 : bool VerifyBufferFromStart(const char *identifier, size_t start) {
176 852 : if (identifier && !Check((size_ >= 2 * sizeof(flatbuffers::uoffset_t) &&
177 0 : BufferHasIdentifier(buf_ + start, identifier)))) {
178 0 : return false;
179 : }
180 :
181 : // Call T::Verify, which must be in the generated code for this type.
182 852 : auto o = VerifyOffset(start);
183 852 : 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 852 : template<typename T> bool VerifyBuffer(const char *identifier) {
204 852 : return VerifyBufferFromStart<T>(identifier, 0);
205 : }
206 :
207 : template<typename T> bool VerifySizePrefixedBuffer(const char *identifier) {
208 : return Verify<uoffset_t>(0U) &&
209 : ReadScalar<uoffset_t>(buf_) == size_ - sizeof(uoffset_t) &&
210 : VerifyBufferFromStart<T>(identifier, sizeof(uoffset_t));
211 : }
212 :
213 69306 : uoffset_t VerifyOffset(size_t start) const {
214 69306 : if (!Verify<uoffset_t>(start)) return 0;
215 69306 : auto o = ReadScalar<uoffset_t>(buf_ + start);
216 : // May not point to itself.
217 69306 : if (!Check(o != 0)) return 0;
218 : // Can't wrap around / buffers are max 2GB.
219 69306 : if (!Check(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 69306 : if (!Verify(start + o, 1)) return 0;
223 69306 : return o;
224 : }
225 :
226 68454 : uoffset_t VerifyOffset(const uint8_t *base, voffset_t start) const {
227 68454 : return VerifyOffset(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 67356 : bool VerifyComplexity() {
235 67356 : depth_++;
236 67356 : num_tables_++;
237 67356 : return Check(depth_ <= max_depth_ && num_tables_ <= max_tables_);
238 : }
239 :
240 : // Called at the end of a table to pop the depth count.
241 67356 : bool EndTable() {
242 67356 : depth_--;
243 67356 : 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_
|