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 template <bool TrackVerifierBufferSize> 27 class VerifierTemplate FLATBUFFERS_FINAL_CLASS { 28 public: 29 struct Options { 30 // The maximum nesting of tables and vectors before we call it invalid. 31 uoffset_t max_depth = 64; 32 // The maximum number of tables we will verify before we call it invalid. 33 uoffset_t max_tables = 1000000; 34 // If true, verify all data is aligned. 35 bool check_alignment = true; 36 // If true, run verifier on nested flatbuffers 37 bool check_nested_flatbuffers = true; 38 // The maximum size of a buffer. 39 size_t max_size = FLATBUFFERS_MAX_BUFFER_SIZE; 40 // Use assertions to check for errors. 41 bool assert = false; 42 }; 43 VerifierTemplate(const uint8_t * const buf,const size_t buf_len,const Options & opts)44 explicit VerifierTemplate(const uint8_t *const buf, const size_t buf_len, 45 const Options &opts) 46 : buf_(buf), size_(buf_len), opts_(opts) { 47 FLATBUFFERS_ASSERT(size_ < opts.max_size); 48 } 49 50 // Deprecated API, please construct with VerifierTemplate::Options. 51 VerifierTemplate(const uint8_t *const buf, const size_t buf_len, 52 const uoffset_t max_depth = 64, 53 const uoffset_t max_tables = 1000000, 54 const bool check_alignment = true) 55 : VerifierTemplate(buf, buf_len, [&] { 56 Options opts; 57 opts.max_depth = max_depth; 58 opts.max_tables = max_tables; 59 opts.check_alignment = check_alignment; 60 return opts; 61 }()) {} 62 63 // Central location where any verification failures register. Check(const bool ok)64 bool Check(const bool ok) const { 65 // clang-format off 66 #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE 67 if (opts_.assert) { FLATBUFFERS_ASSERT(ok); } 68 #endif 69 // clang-format on 70 if (TrackVerifierBufferSize) { 71 if (!ok) { 72 upper_bound_ = 0; 73 } 74 } 75 return ok; 76 } 77 78 // Verify any range within the buffer. Verify(const size_t elem,const size_t elem_len)79 bool Verify(const size_t elem, const size_t elem_len) const { 80 if (TrackVerifierBufferSize) { 81 auto upper_bound = elem + elem_len; 82 if (upper_bound_ < upper_bound) { 83 upper_bound_ = upper_bound; 84 } 85 } 86 return Check(elem_len < size_ && elem <= size_ - elem_len); 87 } 88 VerifyAlignment(const size_t elem,const size_t align)89 bool VerifyAlignment(const size_t elem, const size_t align) const { 90 return Check((elem & (align - 1)) == 0 || !opts_.check_alignment); 91 } 92 93 // Verify a range indicated by sizeof(T). Verify(const size_t elem)94 template<typename T> bool Verify(const size_t elem) const { 95 return VerifyAlignment(elem, sizeof(T)) && Verify(elem, sizeof(T)); 96 } 97 VerifyFromPointer(const uint8_t * const p,const size_t len)98 bool VerifyFromPointer(const uint8_t *const p, const size_t len) { 99 return Verify(static_cast<size_t>(p - buf_), len); 100 } 101 102 // Verify relative to a known-good base pointer. VerifyFieldStruct(const uint8_t * const base,const voffset_t elem_off,const size_t elem_len,const size_t align)103 bool VerifyFieldStruct(const uint8_t *const base, const voffset_t elem_off, 104 const size_t elem_len, const size_t align) const { 105 const auto f = static_cast<size_t>(base - buf_) + elem_off; 106 return VerifyAlignment(f, align) && Verify(f, elem_len); 107 } 108 109 template<typename T> VerifyField(const uint8_t * const base,const voffset_t elem_off,const size_t align)110 bool VerifyField(const uint8_t *const base, const voffset_t elem_off, 111 const size_t align) const { 112 const auto f = static_cast<size_t>(base - buf_) + elem_off; 113 return VerifyAlignment(f, align) && Verify(f, sizeof(T)); 114 } 115 116 // Verify a pointer (may be NULL) of a table type. VerifyTable(const T * const table)117 template<typename T> bool VerifyTable(const T *const table) { 118 return !table || table->Verify(*this); 119 } 120 121 // Verify a pointer (may be NULL) of any vector type. 122 template<int &..., typename T, typename LenT> VerifyVector(const Vector<T,LenT> * const vec)123 bool VerifyVector(const Vector<T, LenT> *const vec) const { 124 return !vec || VerifyVectorOrString<LenT>( 125 reinterpret_cast<const uint8_t *>(vec), sizeof(T)); 126 } 127 128 // Verify a pointer (may be NULL) of a vector to struct. 129 template<int &..., typename T, typename LenT> VerifyVector(const Vector<const T *,LenT> * const vec)130 bool VerifyVector(const Vector<const T *, LenT> *const vec) const { 131 return VerifyVector(reinterpret_cast<const Vector<T, LenT> *>(vec)); 132 } 133 134 // Verify a pointer (may be NULL) to string. VerifyString(const String * const str)135 bool VerifyString(const String *const str) const { 136 size_t end; 137 return !str || (VerifyVectorOrString<uoffset_t>( 138 reinterpret_cast<const uint8_t *>(str), 1, &end) && 139 Verify(end, 1) && // Must have terminator 140 Check(buf_[end] == '\0')); // Terminating byte must be 0. 141 } 142 143 // Common code between vectors and strings. 144 template<typename LenT = uoffset_t> 145 bool VerifyVectorOrString(const uint8_t *const vec, const size_t elem_size, 146 size_t *const end = nullptr) const { 147 const auto vec_offset = static_cast<size_t>(vec - buf_); 148 // Check we can read the size field. 149 if (!Verify<LenT>(vec_offset)) return false; 150 // Check the whole array. If this is a string, the byte past the array must 151 // be 0. 152 const LenT size = ReadScalar<LenT>(vec); 153 const auto max_elems = opts_.max_size / elem_size; 154 if (!Check(size < max_elems)) 155 return false; // Protect against byte_size overflowing. 156 const auto byte_size = sizeof(LenT) + elem_size * size; 157 if (end) *end = vec_offset + byte_size; 158 return Verify(vec_offset, byte_size); 159 } 160 161 // Special case for string contents, after the above has been called. VerifyVectorOfStrings(const Vector<Offset<String>> * const vec)162 bool VerifyVectorOfStrings(const Vector<Offset<String>> *const vec) const { 163 if (vec) { 164 for (uoffset_t i = 0; i < vec->size(); i++) { 165 if (!VerifyString(vec->Get(i))) return false; 166 } 167 } 168 return true; 169 } 170 171 // Special case for table contents, after the above has been called. 172 template<typename T> VerifyVectorOfTables(const Vector<Offset<T>> * const vec)173 bool VerifyVectorOfTables(const Vector<Offset<T>> *const vec) { 174 if (vec) { 175 for (uoffset_t i = 0; i < vec->size(); i++) { 176 if (!vec->Get(i)->Verify(*this)) return false; 177 } 178 } 179 return true; 180 } 181 182 FLATBUFFERS_SUPPRESS_UBSAN("unsigned-integer-overflow") VerifyTableStart(const uint8_t * const table)183 bool VerifyTableStart(const uint8_t *const table) { 184 // Check the vtable offset. 185 const auto tableo = static_cast<size_t>(table - buf_); 186 if (!Verify<soffset_t>(tableo)) return false; 187 // This offset may be signed, but doing the subtraction unsigned always 188 // gives the result we want. 189 const auto vtableo = 190 tableo - static_cast<size_t>(ReadScalar<soffset_t>(table)); 191 // Check the vtable size field, then check vtable fits in its entirety. 192 if (!(VerifyComplexity() && Verify<voffset_t>(vtableo) && 193 VerifyAlignment(ReadScalar<voffset_t>(buf_ + vtableo), 194 sizeof(voffset_t)))) 195 return false; 196 const auto vsize = ReadScalar<voffset_t>(buf_ + vtableo); 197 return Check((vsize & 1) == 0) && Verify(vtableo, vsize); 198 } 199 200 template<typename T> VerifyBufferFromStart(const char * const identifier,const size_t start)201 bool VerifyBufferFromStart(const char *const identifier, const size_t start) { 202 // Buffers have to be of some size to be valid. The reason it is a runtime 203 // check instead of static_assert, is that nested flatbuffers go through 204 // this call and their size is determined at runtime. 205 if (!Check(size_ >= FLATBUFFERS_MIN_BUFFER_SIZE)) return false; 206 207 // If an identifier is provided, check that we have a buffer 208 if (identifier && !Check((size_ >= 2 * sizeof(flatbuffers::uoffset_t) && 209 BufferHasIdentifier(buf_ + start, identifier)))) { 210 return false; 211 } 212 213 // Call T::Verify, which must be in the generated code for this type. 214 const auto o = VerifyOffset<uoffset_t>(start); 215 if (!Check(o != 0)) return false; 216 if (!(reinterpret_cast<const T *>(buf_ + start + o)->Verify(*this))) { 217 return false; 218 } 219 if (TrackVerifierBufferSize) { 220 if (GetComputedSize() == 0) return false; 221 } 222 return true; 223 } 224 225 template<typename T, int &..., typename SizeT> VerifyNestedFlatBuffer(const Vector<uint8_t,SizeT> * const buf,const char * const identifier)226 bool VerifyNestedFlatBuffer(const Vector<uint8_t, SizeT> *const buf, 227 const char *const identifier) { 228 // Caller opted out of this. 229 if (!opts_.check_nested_flatbuffers) return true; 230 231 // An empty buffer is OK as it indicates not present. 232 if (!buf) return true; 233 234 // If there is a nested buffer, it must be greater than the min size. 235 if (!Check(buf->size() >= FLATBUFFERS_MIN_BUFFER_SIZE)) return false; 236 237 VerifierTemplate<TrackVerifierBufferSize> nested_verifier( 238 buf->data(), buf->size(), opts_); 239 return nested_verifier.VerifyBuffer<T>(identifier); 240 } 241 242 // Verify this whole buffer, starting with root type T. VerifyBuffer()243 template<typename T> bool VerifyBuffer() { return VerifyBuffer<T>(nullptr); } 244 VerifyBuffer(const char * const identifier)245 template<typename T> bool VerifyBuffer(const char *const identifier) { 246 return VerifyBufferFromStart<T>(identifier, 0); 247 } 248 249 template<typename T, typename SizeT = uoffset_t> VerifySizePrefixedBuffer(const char * const identifier)250 bool VerifySizePrefixedBuffer(const char *const identifier) { 251 return Verify<SizeT>(0U) && 252 // Ensure the prefixed size is within the bounds of the provided 253 // length. 254 Check(ReadScalar<SizeT>(buf_) + sizeof(SizeT) <= size_) && 255 VerifyBufferFromStart<T>(identifier, sizeof(SizeT)); 256 } 257 258 template<typename OffsetT = uoffset_t, typename SOffsetT = soffset_t> VerifyOffset(const size_t start)259 size_t VerifyOffset(const size_t start) const { 260 if (!Verify<OffsetT>(start)) return 0; 261 const auto o = ReadScalar<OffsetT>(buf_ + start); 262 // May not point to itself. 263 if (!Check(o != 0)) return 0; 264 // Can't wrap around larger than the max size. 265 if (!Check(static_cast<SOffsetT>(o) >= 0)) return 0; 266 // Must be inside the buffer to create a pointer from it (pointer outside 267 // buffer is UB). 268 if (!Verify(start + o, 1)) return 0; 269 return o; 270 } 271 272 template<typename OffsetT = uoffset_t> VerifyOffset(const uint8_t * const base,const voffset_t start)273 size_t VerifyOffset(const uint8_t *const base, const voffset_t start) const { 274 return VerifyOffset<OffsetT>(static_cast<size_t>(base - buf_) + start); 275 } 276 277 // Called at the start of a table to increase counters measuring data 278 // structure depth and amount, and possibly bails out with false if limits set 279 // by the constructor have been hit. Needs to be balanced with EndTable(). VerifyComplexity()280 bool VerifyComplexity() { 281 depth_++; 282 num_tables_++; 283 return Check(depth_ <= opts_.max_depth && num_tables_ <= opts_.max_tables); 284 } 285 286 // Called at the end of a table to pop the depth count. EndTable()287 bool EndTable() { 288 depth_--; 289 return true; 290 } 291 292 // Returns the message size in bytes. 293 // 294 // This should only be called after first calling VerifyBuffer or 295 // VerifySizePrefixedBuffer. 296 // 297 // This method should only be called for VerifierTemplate instances 298 // where the TrackVerifierBufferSize template parameter is true, 299 // i.e. for SizeVerifier. For instances where TrackVerifierBufferSize 300 // is false, this fails at runtime or returns zero. GetComputedSize()301 size_t GetComputedSize() const { 302 if (TrackVerifierBufferSize) { 303 uintptr_t size = upper_bound_; 304 // Align the size to uoffset_t 305 size = (size - 1 + sizeof(uoffset_t)) & ~(sizeof(uoffset_t) - 1); 306 return (size > size_) ? 0 : size; 307 } 308 // Must use SizeVerifier, or (deprecated) turn on 309 // FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE, for this to work. 310 (void)upper_bound_; 311 FLATBUFFERS_ASSERT(false); 312 return 0; 313 } 314 GetFlexReuseTracker()315 std::vector<uint8_t> *GetFlexReuseTracker() { return flex_reuse_tracker_; } 316 SetFlexReuseTracker(std::vector<uint8_t> * const rt)317 void SetFlexReuseTracker(std::vector<uint8_t> *const rt) { 318 flex_reuse_tracker_ = rt; 319 } 320 321 private: 322 const uint8_t *buf_; 323 const size_t size_; 324 const Options opts_; 325 326 mutable size_t upper_bound_ = 0; 327 328 uoffset_t depth_ = 0; 329 uoffset_t num_tables_ = 0; 330 std::vector<uint8_t> *flex_reuse_tracker_ = nullptr; 331 }; 332 333 // Specialization for 64-bit offsets. 334 template<> 335 template<> 336 inline size_t VerifierTemplate<false>::VerifyOffset<uoffset64_t>( 337 const size_t start) const { 338 return VerifyOffset<uoffset64_t, soffset64_t>(start); 339 } 340 template<> 341 template<> 342 inline size_t VerifierTemplate<true>::VerifyOffset<uoffset64_t>( 343 const size_t start) const { 344 return VerifyOffset<uoffset64_t, soffset64_t>(start); 345 } 346 347 // Instance of VerifierTemplate that supports GetComputedSize(). 348 using SizeVerifier = VerifierTemplate</*TrackVerifierBufferSize = */ true>; 349 350 // The FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE build configuration macro is 351 // deprecated, and should not be defined, since it is easy to misuse in ways 352 // that result in ODR violations. Rather than using Verifier and defining 353 // FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE, please use SizeVerifier instead. 354 #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE // Deprecated, see above. 355 using Verifier = SizeVerifier; 356 #else 357 // Instance of VerifierTemplate that is slightly faster, but does not 358 // support GetComputedSize(). 359 using Verifier = VerifierTemplate</*TrackVerifierBufferSize = */ false>; 360 #endif 361 362 } // namespace flatbuffers 363 364 #endif // FLATBUFFERS_VERIFIER_H_ 365