• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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