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