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 */
16using System;
17using System.Reflection;using System.Collections.Generic;
18using System.IO;
19
20namespace 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
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
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
83 public delegate bool VerifyTableAction(Verifier verifier, 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
317 private bool CheckOffset(uint offset)
318 {
319 return (CheckScalar(offset, SIZE_U_OFFSET));
320 }
321
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>
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>
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>
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>
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>
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
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>
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>
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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>
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}
View as plain text