xref: /aosp_15_r20/external/flatbuffers/swift/Sources/FlatBuffers/TableVerifier.swift (revision 890232f25432b36107d06881e0a25aaa6b473652)
1 /*
2  * Copyright 2021 Google Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #if !os(WASI)
18 import Foundation
19 #else
20 import SwiftOverlayShims
21 #endif
22 
23 /// `TableVerifier` verifies a table object is within a provided memory.
24 /// It checks if all the objects for a specific generated table, are within
25 /// the bounds of the buffer, aligned.
26 public struct TableVerifier {
27 
28   /// position of current table in `ByteBuffer`
29   fileprivate var _position: Int
30 
31   /// Current VTable position
32   fileprivate var _vtable: Int
33 
34   /// Length of current VTable
35   fileprivate var _vtableLength: Int
36 
37   /// `Verifier` object created in the base verifable call.
38   fileprivate var _verifier: Verifier
39 
40   /// Creates a `TableVerifier` verifier that allows the Flatbuffer object
41   /// to verify the buffer before accessing any of the data.
42   ///
43   /// - Parameters:
44   ///   - position: Current table Position
45   ///   - vtable: Current `VTable` position
46   ///   - vtableLength: Current `VTable` length
47   ///   - verifier: `Verifier` Object  that caches the data of the verifiable object
48   internal init(
49     position: Int,
50     vtable: Int,
51     vtableLength: Int,
52     verifier: inout Verifier)
53   {
54     _position = position
55     _vtable = vtable
56     _vtableLength = vtableLength
57     _verifier = verifier
58   }
59 
60   /// Dereference the current object position from the `VTable`
61   /// - Parameter field: Current VTable refrence to  position.
62   /// - Throws: A `FlatbuffersErrors` incase the voffset is not aligned/outOfBounds/apparentSizeTooLarge
63   /// - Returns: An optional position for current field
dereferencenull64   internal mutating func dereference(_ field: VOffset) throws -> Int? {
65     if field >= _vtableLength {
66       return nil
67     }
68 
69     /// Reading the offset for the field needs to be read.
70     let offset: VOffset = try _verifier.getValue(
71       at: Int(clamping: _vtable &+ Int(field)))
72 
73     if offset > 0 {
74       return Int(clamping: _position &+ Int(offset))
75     }
76     return nil
77   }
78 
79   /// Visits all the fields within the table to validate the integrity
80   /// of the data
81   /// - Parameters:
82   ///   - field: voffset of the current field to be read
83   ///   - fieldName: fieldname to report data Errors.
84   ///   - required: If the field has to be available in the buffer
85   ///   - type: Type of field to be read
86   /// - Throws: A `FlatbuffersErrors` where the field is corrupt
87   public mutating func visit<T>(
88     field: VOffset,
89     fieldName: String,
90     required: Bool,
91     type: T.Type) throws where T: Verifiable
92   {
93     let derefValue = try dereference(field)
94 
95     if let value = derefValue {
96       try T.verify(&_verifier, at: value, of: T.self)
97       return
98     }
99     if required {
100       throw FlatbuffersErrors.requiredFieldDoesntExist(
101         position: field,
102         name: fieldName)
103     }
104   }
105 
106   /// Visits all the fields for a union object within the table to
107   /// validate the integrity of the data
108   /// - Parameters:
109   ///   - key: Current Key Voffset
110   ///   - field: Current field Voffset
111   ///   - unionKeyName: Union key name
112   ///   - fieldName: Field key name
113   ///   - required: indicates if an object is required to be present
114   ///   - completion: Completion is a handler that WILL be called in the generated
115   /// - Throws: A `FlatbuffersErrors` where the field is corrupt
116   public mutating func visit<T>(
117     unionKey key: VOffset,
118     unionField field: VOffset,
119     unionKeyName: String,
120     fieldName: String,
121     required: Bool,
122     completion: @escaping (inout Verifier, T, Int) throws -> Void) throws
123     where T: UnionEnum
124   {
125     let keyPos = try dereference(key)
126     let valPos = try dereference(field)
127 
128     if keyPos == nil && valPos == nil {
129       if required {
130         throw FlatbuffersErrors.requiredFieldDoesntExist(
131           position: key,
132           name: unionKeyName)
133       }
134       return
135     }
136 
137     if let _key = keyPos,
138        let _val = valPos
139     {
140       /// verifiying that the key is within the buffer
141       try T.T.verify(&_verifier, at: _key, of: T.T.self)
142       guard let _enum = try T.init(value: _verifier._buffer.read(
143         def: T.T.self,
144         position: _key)) else
145       {
146         throw FlatbuffersErrors.unknownUnionCase
147       }
148       /// we are assuming that Unions will always be of type Uint8
149       try completion(
150         &_verifier,
151         _enum,
152         _val)
153       return
154     }
155     throw FlatbuffersErrors.valueNotFound(
156       key: keyPos,
157       keyName: unionKeyName,
158       field: valPos,
159       fieldName: fieldName)
160   }
161 
162   /// Visits and validates all the objects within a union vector
163   /// - Parameters:
164   ///   - key: Current Key Voffset
165   ///   - field: Current field Voffset
166   ///   - unionKeyName: Union key name
167   ///   - fieldName: Field key name
168   ///   - required: indicates if an object is required to be present
169   ///   - completion: Completion is a handler that WILL be called in the generated
170   /// - Throws: A `FlatbuffersErrors` where the field is corrupt
171   public mutating func visitUnionVector<T>(
172     unionKey key: VOffset,
173     unionField field: VOffset,
174     unionKeyName: String,
175     fieldName: String,
176     required: Bool,
177     completion: @escaping (inout Verifier, T, Int) throws -> Void) throws
178     where T: UnionEnum
179   {
180     let keyVectorPosition = try dereference(key)
181     let offsetVectorPosition = try dereference(field)
182 
183     if let keyPos = keyVectorPosition,
184        let valPos = offsetVectorPosition
185     {
186       try UnionVector<T>.verify(
187         &_verifier,
188         keyPosition: keyPos,
189         fieldPosition: valPos,
190         unionKeyName: unionKeyName,
191         fieldName: fieldName,
192         completion: completion)
193       return
194     }
195     if required {
196       throw FlatbuffersErrors.requiredFieldDoesntExist(
197         position: field,
198         name: fieldName)
199     }
200   }
201 
202   /// Finishs the current Table verifier, and subtracts the current
203   /// table from the incremented depth.
finishnull204   public mutating func finish() {
205     _verifier.finish()
206   }
207 }
208