1 /* 2 * Copyright 2014 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 using System; 17 using System.Reflection;using System.Collections.Generic; 18 using System.IO; 19 20 namespace Google.FlatBuffers 21 { 22 23 /// <summary> 24 /// The Class of the Verifier Options 25 /// </summary> 26 public class Options 27 { 28 public const int DEFAULT_MAX_DEPTH = 64; 29 public const int DEFAULT_MAX_TABLES = 1000000; 30 31 private int max_depth = 0; 32 private int max_tables = 0; 33 private bool string_end_check = false; 34 private bool alignment_check = false; 35 Options()36 public Options() 37 { 38 max_depth = DEFAULT_MAX_DEPTH; 39 max_tables = DEFAULT_MAX_TABLES; 40 string_end_check = true; 41 alignment_check = true; 42 } 43 Options(int maxDepth, int maxTables, bool stringEndCheck, bool alignmentCheck)44 public Options(int maxDepth, int maxTables, bool stringEndCheck, bool alignmentCheck) 45 { 46 max_depth = maxDepth; 47 max_tables = maxTables; 48 string_end_check = stringEndCheck; 49 alignment_check = alignmentCheck; 50 } 51 /// <summary> Maximum depth of nested tables allowed in a valid flatbuffer. </summary> 52 public int maxDepth 53 { 54 get { return max_depth; } 55 set { max_depth = value; } 56 } 57 /// <summary> Maximum number of tables allowed in a valid flatbuffer. </summary> 58 public int maxTables 59 { 60 get { return max_tables; } 61 set { max_tables = value; } 62 } 63 /// <summary> Check that string contains its null terminator </summary> 64 public bool stringEndCheck 65 { 66 get { return string_end_check; } 67 set { string_end_check = value; } 68 } 69 /// <summary> Check alignment of elements </summary> 70 public bool alignmentCheck 71 { 72 get { return alignment_check; } 73 set { alignment_check = value; } 74 } 75 } 76 77 public struct checkElementStruct 78 { 79 public bool elementValid; 80 public uint elementOffset; 81 } 82 VerifyTableAction(Verifier verifier, uint tablePos)83 public delegate bool VerifyTableAction(Verifier verifier, uint tablePos); VerifyUnionAction(Verifier verifier, byte typeId, uint tablePos)84 public delegate bool VerifyUnionAction(Verifier verifier, byte typeId, uint tablePos); 85 86 /// <summary> 87 /// The Main Class of the FlatBuffer Verifier 88 /// </summary> 89 public class Verifier 90 { 91 private ByteBuffer verifier_buffer = null; 92 private Options verifier_options = null; 93 private int depth_cnt = 0; 94 private int num_tables_cnt = 0; 95 96 public const int SIZE_BYTE = 1; 97 public const int SIZE_INT = 4; 98 public const int SIZE_U_OFFSET = 4; 99 public const int SIZE_S_OFFSET = 4; 100 public const int SIZE_V_OFFSET = 2; 101 public const int SIZE_PREFIX_LENGTH = FlatBufferConstants.SizePrefixLength; // default size = 4 102 public const int FLATBUFFERS_MAX_BUFFER_SIZE = System.Int32.MaxValue; // default size = 2147483647 103 public const int FILE_IDENTIFIER_LENGTH = FlatBufferConstants.FileIdentifierLength; // default size = 4 104 105 /// <summary> The Base Constructor of the Verifier object </summary> Verifier()106 public Verifier() 107 { 108 // Verifier buffer 109 verifier_buffer = null; 110 // Verifier settings 111 verifier_options = null; 112 // Depth counter 113 depth_cnt = 0; 114 // Tables counter 115 num_tables_cnt = 0; 116 } 117 118 /// <summary> The Constructor of the Verifier object with input parameters: ByteBuffer and/or Options </summary> 119 /// <param name="buf"> Input flat byte buffer defined as ByteBuffer type</param> 120 /// <param name="options"> Options object with settings for the coniguration the Verifier </param> Verifier(ByteBuffer buf, Options options = null)121 public Verifier(ByteBuffer buf, Options options = null) 122 { 123 verifier_buffer = buf; 124 verifier_options = options ?? new Options(); 125 depth_cnt = 0; 126 num_tables_cnt = 0; 127 } 128 129 /// <summary> Bytes Buffer for Verify</summary> 130 public ByteBuffer Buf 131 { 132 get { return verifier_buffer; } 133 set { verifier_buffer = value; } 134 } 135 /// <summary> Options of the Verifier </summary> 136 public Options options 137 { 138 get { return verifier_options; } 139 set { verifier_options = value; } 140 } 141 /// <summary> Counter of tables depth in a tested flatbuffer </summary> 142 public int depth 143 { 144 get { return depth_cnt; } 145 set { depth_cnt = value; } 146 } 147 /// <summary> Counter of tables in a tested flatbuffer </summary> 148 public int numTables 149 { 150 get { return num_tables_cnt; } 151 set { num_tables_cnt = value; } 152 } 153 154 155 /// <summary> Method set maximum tables depth of valid structure</summary> 156 /// <param name="value"> Specify Value of the maximum depth of the structure</param> SetMaxDepth(int value)157 public Verifier SetMaxDepth(int value) 158 { 159 verifier_options.maxDepth = value; 160 return this; 161 } 162 /// <summary> Specify maximum number of tables in structure </summary> 163 /// <param name="value"> Specify Value of the maximum number of the tables in the structure</param> SetMaxTables(int value)164 public Verifier SetMaxTables(int value) 165 { 166 verifier_options.maxTables = value; 167 return this; 168 } 169 /// <summary> Enable/disable buffer content alignment check </summary> 170 /// <param name="value"> Value of the State for buffer content alignment check (Enable = true) </param> SetAlignmentCheck(bool value)171 public Verifier SetAlignmentCheck(bool value) 172 { 173 verifier_options.alignmentCheck = value; 174 return this; 175 } 176 /// <summary> Enable/disable checking of string termination '0' character </summary> 177 /// <param name="value"> Value of the option for string termination '0' character check (Enable = true)</param> SetStringCheck(bool value)178 public Verifier SetStringCheck(bool value) 179 { 180 verifier_options.stringEndCheck = value; 181 return this; 182 } 183 184 /// <summary> Check if there is identifier in buffer </summary> 185 /// <param name="buf"> Input flat byte buffer defined as ByteBuffer type </param> 186 /// <param name="startPos">Start position of data in the Byte Buffer</param> 187 /// <param name="identifier"> Identifier for the Byte Buffer</param> 188 /// <returns> Return True when the Byte Buffer Identifier is present</returns> BufferHasIdentifier(ByteBuffer buf, uint startPos, string identifier)189 private bool BufferHasIdentifier(ByteBuffer buf, uint startPos, string identifier) 190 { 191 if (identifier.Length != FILE_IDENTIFIER_LENGTH) 192 { 193 throw new ArgumentException("FlatBuffers: file identifier must be length" + Convert.ToString(FILE_IDENTIFIER_LENGTH)); 194 } 195 for (int i = 0; i < FILE_IDENTIFIER_LENGTH; i++) 196 { 197 if ((sbyte)identifier[i] != verifier_buffer.GetSbyte(Convert.ToInt32(SIZE_S_OFFSET + i + startPos))) 198 { 199 return false; 200 } 201 } 202 203 return true; 204 } 205 206 /// <summary> Get UOffsetT from buffer at given position - it must be verified before read </summary> 207 /// <param name="buf"> Input flat byte buffer defined as ByteBuffer type </param> 208 /// <param name="pos"> Position of data in the Byte Buffer</param> 209 /// <returns> Return the UOffset Value (Unsigned Integer type - 4 bytes) in pos </returns> ReadUOffsetT(ByteBuffer buf, uint pos)210 private uint ReadUOffsetT(ByteBuffer buf, uint pos) 211 { 212 return buf.GetUint(Convert.ToInt32(pos)); 213 } 214 /// <summary> Get SOffsetT from buffer at given position - it must be verified before read </summary> 215 /// <param name="buf"> Input flat byte buffer defined as ByteBuffer type </param> 216 /// <param name="pos"> Position of data in the Byte Buffer</param> 217 /// <returns> Return the SOffset Value (Signed Integer type - 4 bytes) in pos </returns> ReadSOffsetT(ByteBuffer buf, int pos)218 private int ReadSOffsetT(ByteBuffer buf, int pos) 219 { 220 return buf.GetInt(pos); 221 } 222 /// <summary> Get VOffsetT from buffer at given position - it must be verified before read </summary> 223 /// <param name="buf"> Input flat byte buffer defined as ByteBuffer type </param> 224 /// <param name="pos"> Position of data in the Byte Buffer</param> 225 /// <returns> Return the VOffset Value (Short type - 2 bytes) in pos </returns> ReadVOffsetT(ByteBuffer buf, int pos)226 private short ReadVOffsetT(ByteBuffer buf, int pos) 227 { 228 return buf.GetShort(pos); 229 } 230 231 /// <summary> Get table data area relative offset from vtable. Result is relative to table start 232 /// Fields which are deprecated are ignored by checking against the vtable's length. </summary> 233 /// <param name="pos"> Position of data in the Byte Buffer </param> 234 /// <param name="vtableOffset"> offset of value in the Table</param> 235 /// <returns> Return the relative VOffset Value (Short type - 2 bytes) in calculated offset </returns> GetVRelOffset(int pos, short vtableOffset)236 private short GetVRelOffset(int pos, short vtableOffset) 237 { 238 short VOffset = 0; 239 // Used try/catch because pos typa as int 32bit 240 try 241 { 242 // First, get vtable offset 243 short vtable = Convert.ToInt16(pos - ReadSOffsetT(verifier_buffer, pos)); 244 // Check that offset points to vtable area (is smaller than vtable size) 245 if (vtableOffset < ReadVOffsetT(verifier_buffer, vtable)) 246 { 247 // Now, we can read offset value - TODO check this value against size of table data 248 VOffset = ReadVOffsetT(verifier_buffer, vtable + vtableOffset); 249 } 250 else 251 { 252 VOffset = 0; 253 } 254 } 255 catch (Exception e) 256 { 257 Console.WriteLine("Exception: {0}", e); 258 return VOffset; 259 } 260 return VOffset; 261 262 } 263 /// <summary> Get table data area absolute offset from vtable. Result is the absolute buffer offset. 264 /// The result value offset cannot be '0' (pointing to itself) so after validation this method returnes '0' 265 /// value as a marker for missing optional entry </summary> 266 /// <param name="tablePos"> Table Position value in the Byte Buffer </param> 267 /// <param name="vtableOffset"> offset value in the Table</param> 268 /// <returns> Return the absolute UOffset Value </returns> GetVOffset(uint tablePos, short vtableOffset)269 private uint GetVOffset(uint tablePos, short vtableOffset) 270 { 271 uint UOffset = 0; 272 // First, get vtable relative offset 273 short relPos = GetVRelOffset(Convert.ToInt32(tablePos), vtableOffset); 274 if (relPos != 0) 275 { 276 // Calculate offset based on table postion 277 UOffset = Convert.ToUInt32(tablePos + relPos); 278 } 279 else 280 { 281 UOffset = 0; 282 } 283 return UOffset; 284 } 285 286 /// <summary> Check flatbuffer complexity (tables depth, elements counter and so on) </summary> 287 /// <returns> If complexity is too high function returns false as verification error </returns> CheckComplexity()288 private bool CheckComplexity() 289 { 290 return ((depth <= options.maxDepth) && (numTables <= options.maxTables)); 291 } 292 293 /// <summary> Check alignment of element. </summary> 294 /// <returns> Return True when alignment of the element is correct</returns> CheckAlignment(uint element, ulong align)295 private bool CheckAlignment(uint element, ulong align) 296 { 297 return (((element & (align - 1)) == 0) || (!options.alignmentCheck)); 298 } 299 300 /// <summary> Check if element is valid in buffer area. </summary> 301 /// <param name="pos"> Value defines the offset/position to element</param> 302 /// <param name="elementSize"> Size of element</param> 303 /// <returns> Return True when Element is correct </returns> CheckElement(uint pos, ulong elementSize)304 private bool CheckElement(uint pos, ulong elementSize) 305 { 306 return ((elementSize < Convert.ToUInt64(verifier_buffer.Length)) && (pos <= (Convert.ToUInt32(verifier_buffer.Length) - elementSize))); 307 } 308 /// <summary> Check if element is a valid scalar. </summary> 309 /// <param name="pos"> Value defines the offset to scalar</param> 310 /// <param name="elementSize"> Size of element</param> 311 /// <returns> Return True when Scalar Element is correct </returns> CheckScalar(uint pos, ulong elementSize)312 private bool CheckScalar(uint pos, ulong elementSize) 313 { 314 return ((CheckAlignment(pos, elementSize)) && (CheckElement(pos, elementSize))); 315 } 316 /// <summary> Check offset. It is a scalar with size of UOffsetT. </summary> CheckOffset(uint offset)317 private bool CheckOffset(uint offset) 318 { 319 return (CheckScalar(offset, SIZE_U_OFFSET)); 320 } 321 CheckVectorOrString(uint pos, ulong elementSize)322 private checkElementStruct CheckVectorOrString(uint pos, ulong elementSize) 323 { 324 var result = new checkElementStruct 325 { 326 elementValid = false, 327 elementOffset = 0 328 }; 329 330 uint vectorPos = pos; 331 // Check we can read the vector/string size field (it is of uoffset size) 332 if (!CheckScalar(vectorPos, SIZE_U_OFFSET)) 333 { 334 // result.elementValid = false; result.elementOffset = 0; 335 return result; 336 } 337 // Check the whole array. If this is a string, the byte past the array 338 // must be 0. 339 uint size = ReadUOffsetT(verifier_buffer, vectorPos); 340 ulong max_elements = (FLATBUFFERS_MAX_BUFFER_SIZE / elementSize); 341 if (size >= max_elements) 342 { 343 // Protect against byte_size overflowing. 344 // result.elementValid = false; result.elementOffset = 0; 345 return result; 346 } 347 348 uint bytes_size = SIZE_U_OFFSET + (Convert.ToUInt32(elementSize) * size); 349 uint buffer_end_pos = vectorPos + bytes_size; 350 result.elementValid = CheckElement(vectorPos, bytes_size); 351 result.elementOffset = buffer_end_pos; 352 return (result); 353 } 354 355 /// <summary>Verify a string at given position.</summary> CheckString(uint pos)356 private bool CheckString(uint pos) 357 { 358 var result = CheckVectorOrString(pos, SIZE_BYTE); 359 if (options.stringEndCheck) 360 { 361 result.elementValid = result.elementValid && CheckScalar(result.elementOffset, 1); // Must have terminator 362 result.elementValid = result.elementValid && (verifier_buffer.GetSbyte(Convert.ToInt32(result.elementOffset)) == 0); // Terminating byte must be 0. 363 } 364 return result.elementValid; 365 } 366 367 /// <summary> Verify the vector of elements of given size </summary> CheckVector(uint pos, ulong elementSize)368 private bool CheckVector(uint pos, ulong elementSize) 369 { 370 var result = CheckVectorOrString(pos, elementSize); 371 return result.elementValid; 372 } 373 /// <summary> Verify table content using structure dependent generated function </summary> CheckTable(uint tablePos, VerifyTableAction verifyAction)374 private bool CheckTable(uint tablePos, VerifyTableAction verifyAction) 375 { 376 return verifyAction(this, tablePos); 377 } 378 379 /// <summary> String check wrapper function to be used in vector of strings check </summary> CheckStringFunc(Verifier verifier, uint pos)380 private bool CheckStringFunc(Verifier verifier, uint pos) 381 { 382 return verifier.CheckString(pos); 383 } 384 385 /// <summary> Check vector of objects. Use generated object verification function </summary> CheckVectorOfObjects(uint pos, VerifyTableAction verifyAction)386 private bool CheckVectorOfObjects(uint pos, VerifyTableAction verifyAction) 387 { 388 if (!CheckVector(pos, SIZE_U_OFFSET)) 389 { 390 return false; 391 } 392 uint size = ReadUOffsetT(verifier_buffer, pos); 393 // Vector data starts just after vector size/length 394 uint vecStart = pos + SIZE_U_OFFSET; 395 uint vecOff = 0; 396 // Iterate offsets and verify referenced objects 397 for (uint i = 0; i < size; i++) 398 { 399 vecOff = vecStart + (i * SIZE_U_OFFSET); 400 if (!CheckIndirectOffset(vecOff)) 401 { 402 return false; 403 } 404 uint objOffset = GetIndirectOffset(vecOff); 405 if (!verifyAction(this, objOffset)) 406 { 407 return false; 408 } 409 } 410 return true; 411 } 412 413 /// <summary> Check if the offset referenced by offsetPos is the valid offset pointing to buffer</summary> 414 // offsetPos - offset to offset data CheckIndirectOffset(uint pos)415 private bool CheckIndirectOffset(uint pos) 416 { 417 // Check the input offset is valid 418 if(!CheckScalar(pos, SIZE_U_OFFSET)) 419 { 420 return false; 421 } 422 // Get indirect offset 423 uint offset = ReadUOffsetT(verifier_buffer, pos); 424 // May not point to itself neither wrap around (buffers are max 2GB) 425 if ((offset == 0) || (offset >= FLATBUFFERS_MAX_BUFFER_SIZE)) 426 { 427 return false; 428 } 429 // Must be inside the buffer 430 return CheckElement(pos + offset, 1); 431 } 432 433 /// <summary> Check flatbuffer content using generated object verification function </summary> CheckBufferFromStart(string identifier, uint startPos, VerifyTableAction verifyAction)434 private bool CheckBufferFromStart(string identifier, uint startPos, VerifyTableAction verifyAction) 435 { 436 if ((identifier != null) && 437 (identifier.Length == 0) && 438 ((verifier_buffer.Length < (SIZE_U_OFFSET + FILE_IDENTIFIER_LENGTH)) || (!BufferHasIdentifier(verifier_buffer, startPos, identifier)))) 439 { 440 return false; 441 } 442 if(!CheckIndirectOffset(startPos)) 443 { 444 return false; 445 } 446 uint offset = GetIndirectOffset(startPos); 447 return CheckTable(offset, verifyAction); // && GetComputedSize() 448 } 449 450 /// <summary> Get indirect offset. It is an offset referenced by offset Pos </summary> GetIndirectOffset(uint pos)451 private uint GetIndirectOffset(uint pos) 452 { 453 // Get indirect offset referenced by offsetPos 454 uint offset = pos + ReadUOffsetT(verifier_buffer, pos); 455 return offset; 456 } 457 458 /// <summary> Verify beginning of table </summary> 459 /// <param name="tablePos"> Position in the Table </param> 460 /// <returns> Return True when the verification of the beginning of the table is passed</returns> 461 // (this method is used internally by generated verification functions) VerifyTableStart(uint tablePos)462 public bool VerifyTableStart(uint tablePos) 463 { 464 // Starting new table verification increases complexity of structure 465 depth_cnt++; 466 num_tables_cnt++; 467 468 if (!CheckScalar(tablePos, SIZE_S_OFFSET)) 469 { 470 return false; 471 } 472 uint vtable = (uint)(tablePos - ReadSOffsetT(verifier_buffer, Convert.ToInt32(tablePos))); 473 return ((CheckComplexity()) && (CheckScalar(vtable, SIZE_V_OFFSET)) && (CheckAlignment(Convert.ToUInt32(ReadVOffsetT(verifier_buffer, Convert.ToInt32(vtable))), SIZE_V_OFFSET)) && (CheckElement(vtable, Convert.ToUInt64(ReadVOffsetT(verifier_buffer, Convert.ToInt32(vtable)))))); 474 } 475 476 /// <summary> Verify end of table. In practice, this function does not check buffer but handles 477 /// verification statistics update </summary> 478 // (this method is used internally by generated verification functions) VerifyTableEnd(uint tablePos)479 public bool VerifyTableEnd(uint tablePos) 480 { 481 depth--; 482 return true; 483 } 484 485 /// <summary> Verifiy static/inlined data area field </summary> 486 /// <param name="tablePos"> Position in the Table</param> 487 /// <param name="offsetId"> Offset to the static/inlined data element </param> 488 /// <param name="elementSize"> Size of the element </param> 489 /// <param name="align"> Alignment bool value </param> 490 /// <param name="required"> Required Value when the offset == 0 </param> 491 /// <returns>Return True when the verification of the static/inlined data element is passed</returns> 492 // (this method is used internally by generated verification functions) VerifyField(uint tablePos, short offsetId, ulong elementSize, ulong align, bool required)493 public bool VerifyField(uint tablePos, short offsetId, ulong elementSize, ulong align, bool required) 494 { 495 uint offset = GetVOffset(tablePos, offsetId); 496 if (offset != 0) 497 { 498 return ((CheckAlignment(offset, align)) && (CheckElement(offset, elementSize))); 499 } 500 return !required; // it is OK if field is not required 501 } 502 503 /// <summary> Verify string </summary> 504 /// <param name="tablePos"> Position in the Table</param> 505 /// <param name="vOffset"> Offset to the String element </param> 506 /// <param name="required"> Required Value when the offset == 0 </param> 507 /// <returns>Return True when the verification of the String is passed</returns> 508 // (this method is used internally by generated verification functions) VerifyString(uint tablePos, short vOffset, bool required)509 public bool VerifyString(uint tablePos, short vOffset, bool required) 510 { 511 var offset = GetVOffset(tablePos, vOffset); 512 if (offset == 0) 513 { 514 return !required; 515 } 516 if (!CheckIndirectOffset(offset)) 517 { 518 return false; 519 } 520 var strOffset = GetIndirectOffset(offset); 521 return CheckString(strOffset); 522 } 523 524 /// <summary> Verify vector of fixed size structures and scalars </summary> 525 /// <param name="tablePos"> Position in the Table</param> 526 /// <param name="vOffset"> Offset to the Vector of Data </param> 527 /// <param name="elementSize"> Size of the element</param> 528 /// <param name="required"> Required Value when the offset == 0 </param> 529 /// <returns>Return True when the verification of the Vector of Data passed</returns> 530 // (this method is used internally by generated verification functions) VerifyVectorOfData(uint tablePos, short vOffset, ulong elementSize, bool required)531 public bool VerifyVectorOfData(uint tablePos, short vOffset, ulong elementSize, bool required) 532 { 533 var offset = GetVOffset(tablePos, vOffset); 534 if (offset == 0) 535 { 536 return !required; 537 } 538 if (!CheckIndirectOffset(offset)) 539 { 540 return false; 541 } 542 var vecOffset = GetIndirectOffset(offset); 543 return CheckVector(vecOffset, elementSize); 544 } 545 546 /// <summary> Verify array of strings </summary> 547 /// <param name="tablePos"> Position in the Table</param> 548 /// <param name="offsetId"> Offset to the Vector of String </param> 549 /// <param name="required"> Required Value when the offset == 0 </param> 550 /// <returns>Return True when the verification of the Vector of String passed</returns> 551 // (this method is used internally by generated verification functions) VerifyVectorOfStrings(uint tablePos, short offsetId, bool required)552 public bool VerifyVectorOfStrings(uint tablePos, short offsetId, bool required) 553 { 554 var offset = GetVOffset(tablePos, offsetId); 555 if (offset == 0) 556 { 557 return !required; 558 } 559 if (!CheckIndirectOffset(offset)) 560 { 561 return false; 562 } 563 var vecOffset = GetIndirectOffset(offset); 564 return CheckVectorOfObjects(vecOffset, CheckStringFunc); 565 } 566 567 /// <summary> Verify vector of tables (objects). Tables are verified using generated verifyObjFunc </summary> 568 /// <param name="tablePos"> Position in the Table</param> 569 /// <param name="offsetId"> Offset to the Vector of Table </param> 570 /// <param name="verifyAction"> Method used to the verification Table </param> 571 /// <param name="required"> Required Value when the offset == 0 </param> 572 /// <returns>Return True when the verification of the Vector of Table passed</returns> 573 // (this method is used internally by generated verification functions) VerifyVectorOfTables(uint tablePos, short offsetId, VerifyTableAction verifyAction, bool required)574 public bool VerifyVectorOfTables(uint tablePos, short offsetId, VerifyTableAction verifyAction, bool required) 575 { 576 var offset = GetVOffset(tablePos, offsetId); 577 if (offset == 0) 578 { 579 return !required; 580 } 581 if (!CheckIndirectOffset(offset)) 582 { 583 return false; 584 } 585 var vecOffset = GetIndirectOffset(offset); 586 return CheckVectorOfObjects(vecOffset, verifyAction); 587 } 588 589 /// <summary> Verify table object using generated verification function. </summary> 590 /// <param name="tablePos"> Position in the Table</param> 591 /// <param name="offsetId"> Offset to the Table </param> 592 /// <param name="verifyAction"> Method used to the verification Table </param> 593 /// <param name="required"> Required Value when the offset == 0 </param> 594 /// <returns>Return True when the verification of the Table passed</returns> 595 // (this method is used internally by generated verification functions) VerifyTable(uint tablePos, short offsetId, VerifyTableAction verifyAction, bool required)596 public bool VerifyTable(uint tablePos, short offsetId, VerifyTableAction verifyAction, bool required) 597 { 598 var offset = GetVOffset(tablePos, offsetId); 599 if (offset == 0) 600 { 601 return !required; 602 } 603 if (!CheckIndirectOffset(offset)) 604 { 605 return false; 606 } 607 var tabOffset = GetIndirectOffset(offset); 608 return CheckTable(tabOffset, verifyAction); 609 } 610 611 /// <summary> Verify nested buffer object. When verifyObjFunc is provided, it is used to verify object structure. </summary> 612 /// <param name="tablePos"> Position in the Table </param> 613 /// <param name="offsetId"> Offset to the Table </param> 614 /// <param name="verifyAction"> Method used to the verification Table </param> 615 /// <param name="required"> Required Value when the offset == 0 </param> 616 // (this method is used internally by generated verification functions) VerifyNestedBuffer(uint tablePos, short offsetId, VerifyTableAction verifyAction, bool required)617 public bool VerifyNestedBuffer(uint tablePos, short offsetId, VerifyTableAction verifyAction, bool required) 618 { 619 var offset = GetVOffset(tablePos, offsetId); 620 if (offset == 0) 621 { 622 return !required; 623 } 624 uint vecOffset = GetIndirectOffset(offset); 625 if (!CheckVector(vecOffset, SIZE_BYTE)) 626 { 627 return false; 628 } 629 if (verifyAction != null) 630 { 631 var vecLength = ReadUOffsetT(verifier_buffer, vecOffset); 632 // Buffer begins after vector length 633 var vecStart = vecOffset + SIZE_U_OFFSET; 634 // Create and Copy nested buffer bytes from part of Verify Buffer 635 var nestedByteBuffer = new ByteBuffer(verifier_buffer.ToArray(Convert.ToInt32(vecStart), Convert.ToInt32(vecLength))); 636 var nestedVerifyier = new Verifier(nestedByteBuffer, options); 637 // There is no internal identifier - use empty one 638 if (!nestedVerifyier.CheckBufferFromStart("", 0, verifyAction)) 639 { 640 return false; 641 } 642 } 643 return true; 644 } 645 646 /// <summary> Verifiy static/inlined data area at absolute offset </summary> 647 /// <param name="pos"> Position of static/inlined data area in the Byte Buffer</param> 648 /// <param name="elementSize"> Size of the union data</param> 649 /// <param name="align"> Alignment bool value </param> 650 /// <returns>Return True when the verification of the Union Data is passed</returns> 651 // (this method is used internally by generated verification functions) VerifyUnionData(uint pos, ulong elementSize, ulong align)652 public bool VerifyUnionData(uint pos, ulong elementSize, ulong align) 653 { 654 bool result = ((CheckAlignment(pos, align)) && (CheckElement(pos, elementSize))); 655 return result; 656 } 657 658 /// <summary> Verify string referenced by absolute offset value </summary> 659 /// <param name="pos"> Position of Union String in the Byte Buffer</param> 660 /// <returns>Return True when the verification of the Union String is passed</returns> 661 // (this method is used internally by generated verification functions) VerifyUnionString(uint pos)662 public bool VerifyUnionString(uint pos) 663 { 664 bool result = CheckString(pos); 665 return result; 666 } 667 668 /// <summary> Method verifies union object using generated verification function </summary> 669 /// <param name="tablePos"> Position in the Table</param> 670 /// <param name="typeIdVOffset"> Offset in the Table</param> 671 /// <param name="valueVOffset"> Offset to Element</param> 672 /// <param name="verifyAction"> Verification Method used for Union</param> 673 /// <param name="required"> Required Value when the offset == 0 </param> 674 // (this method is used internally by generated verification functions) VerifyUnion(uint tablePos, short typeIdVOffset, short valueVOffset, VerifyUnionAction verifyAction, bool required)675 public bool VerifyUnion(uint tablePos, short typeIdVOffset, short valueVOffset, VerifyUnionAction verifyAction, bool required) 676 { 677 // Check the union type index 678 var offset = GetVOffset(tablePos, typeIdVOffset); 679 if (offset == 0) 680 { 681 return !required; 682 } 683 if (!((CheckAlignment(offset, SIZE_BYTE)) && (CheckElement(offset, SIZE_BYTE)))) 684 { 685 return false; 686 } 687 // Check union data 688 offset = GetVOffset(tablePos, valueVOffset); 689 // Take type id 690 var typeId = verifier_buffer.Get(Convert.ToInt32(offset)); 691 if (offset == 0) 692 { 693 // When value data is not present, allow union verification function to deal with illegal offset 694 return verifyAction(this, typeId, Convert.ToUInt32(verifier_buffer.Length)); 695 } 696 if (!CheckIndirectOffset(offset)) 697 { 698 return false; 699 } 700 // Take value offset and validate union structure 701 uint unionOffset = GetIndirectOffset(offset); 702 return verifyAction(this, typeId, unionOffset); 703 } 704 705 /// <summary> Verify vector of unions (objects). Unions are verified using generated verifyObjFunc </summary> 706 /// <param name="tablePos"> Position of the Table</param> 707 /// <param name="typeOffsetId"> Offset in the Table (Union type id)</param> 708 /// <param name="offsetId"> Offset to vector of Data Stucture offset</param> 709 /// <param name="verifyAction"> Verification Method used for Union</param> 710 /// <param name="required"> Required Value when the offset == 0 </param> 711 /// <returns>Return True when the verification of the Vector of Unions passed</returns> 712 // (this method is used internally by generated verification functions) VerifyVectorOfUnion(uint tablePos, short typeOffsetId, short offsetId, VerifyUnionAction verifyAction, bool required)713 public bool VerifyVectorOfUnion(uint tablePos, short typeOffsetId, short offsetId, VerifyUnionAction verifyAction, bool required) 714 { 715 // type id offset must be valid 716 var offset = GetVOffset(tablePos, typeOffsetId); 717 if (offset == 0) 718 { 719 return !required; 720 } 721 if (!CheckIndirectOffset(offset)) 722 { 723 return false; 724 } 725 // Get type id table absolute offset 726 var typeIdVectorOffset = GetIndirectOffset(offset); 727 // values offset must be valid 728 offset = GetVOffset(tablePos, offsetId); 729 if (!CheckIndirectOffset(offset)) 730 { 731 return false; 732 } 733 var valueVectorOffset = GetIndirectOffset(offset); 734 // validate referenced vectors 735 if(!CheckVector(typeIdVectorOffset, SIZE_BYTE) || 736 !CheckVector(valueVectorOffset, SIZE_U_OFFSET)) 737 { 738 return false; 739 } 740 // Both vectors should have the same length 741 var typeIdVectorLength = ReadUOffsetT(verifier_buffer, typeIdVectorOffset); 742 var valueVectorLength = ReadUOffsetT(verifier_buffer, valueVectorOffset); 743 if (typeIdVectorLength != valueVectorLength) 744 { 745 return false; 746 } 747 // Verify each union from vectors 748 var typeIdStart = typeIdVectorOffset + SIZE_U_OFFSET; 749 var valueStart = valueVectorOffset + SIZE_U_OFFSET; 750 for (uint i = 0; i < typeIdVectorLength; i++) 751 { 752 // Get type id 753 byte typeId = verifier_buffer.Get(Convert.ToInt32(typeIdStart + i * SIZE_U_OFFSET)); 754 // get offset to vector item 755 uint off = valueStart + i * SIZE_U_OFFSET; 756 // Check the vector item has a proper offset 757 if (!CheckIndirectOffset(off)) 758 { 759 return false; 760 } 761 uint valueOffset = GetIndirectOffset(off); 762 // Verify object 763 if (!verifyAction(this, typeId, valueOffset)) 764 { 765 return false; 766 } 767 } 768 return true; 769 } 770 771 // Method verifies flatbuffer data using generated Table verification function. 772 // The data buffer is already provided when creating [Verifier] object (see [NewVerifier]) 773 // 774 // - identifier - the expected identifier of buffer data. 775 // When empty identifier is provided the identifier validation is skipped. 776 // - sizePrefixed - this flag should be true when buffer is prefixed with content size 777 // - verifyObjFunc - function to be used for verification. This function is generated by compiler and included in each table definition file with name "<Tablename>Verify" 778 // 779 // Example: 780 // 781 // /* Verify Monster table. Ignore buffer name and assume buffer does not contain data length prefix */ 782 // isValid = verifier.verifyBuffer(bb, false, MonsterVerify) 783 // 784 // /* Verify Monster table. Buffer name is 'MONS' and contains data length prefix */ 785 // isValid = verifier.verifyBuffer("MONS", true, MonsterVerify) 786 /// <summary> Method verifies flatbuffer data using generated Table verification function </summary> 787 /// 788 /// <param name="identifier"> The expected identifier of buffer data</param> 789 /// <param name="sizePrefixed"> Flag should be true when buffer is prefixed with content size</param> 790 /// <param name="verifyAction"> Function to be used for verification. This function is generated by compiler and included in each table definition file</param> 791 /// <returns> Return True when verification of FlatBuffer passed</returns> 792 /// <example> 793 /// Example 1. Verify Monster table. Ignore buffer name and assume buffer does not contain data length prefix 794 /// <code> isValid = verifier.VerifyBuffer(bb, false, MonsterVerify)</code> 795 /// Example 2. Verify Monster table. Buffer name is 'MONS' and contains data length prefix 796 /// <code> isValid = verifier.VerifyBuffer("MONS", true, MonsterVerify)</code> 797 /// </example> VerifyBuffer(string identifier, bool sizePrefixed, VerifyTableAction verifyAction)798 public bool VerifyBuffer(string identifier, bool sizePrefixed, VerifyTableAction verifyAction) 799 { 800 // Reset counters - starting verification from beginning 801 depth = 0; 802 numTables = 0; 803 804 var start = (uint)(verifier_buffer.Position); 805 if (sizePrefixed) 806 { 807 start = (uint)(verifier_buffer.Position) + SIZE_PREFIX_LENGTH; 808 if(!CheckScalar((uint)(verifier_buffer.Position), SIZE_PREFIX_LENGTH)) 809 { 810 return false; 811 } 812 uint size = ReadUOffsetT(verifier_buffer, (uint)(verifier_buffer.Position)); 813 if (size != ((uint)(verifier_buffer.Length) - start)) 814 { 815 return false; 816 } 817 } 818 return CheckBufferFromStart(identifier, start, verifyAction); 819 } 820 } 821 822 } 823