xref: /aosp_15_r20/external/flatbuffers/swift/Sources/FlatBuffers/Verifier.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 /// Verifier that check if the buffer passed into it is a valid,
24 /// safe, aligned Flatbuffers object since swift read from `unsafeMemory`
25 public struct Verifier {
26 
27   /// Flag to check for alignment if true
28   fileprivate let _checkAlignment: Bool
29   /// Capacity of the current buffer
30   fileprivate var _capacity: Int
31   /// Current ApparentSize
32   fileprivate var _apparentSize: UOffset = 0
33   /// Amount of tables present within a buffer
34   fileprivate var _tableCount = 0
35 
36   /// Capacity of the buffer
37   internal var capacity: Int { _capacity }
38   /// Current reached depth within the buffer
39   internal var _depth = 0
40   /// Current verifiable ByteBuffer
41   internal var _buffer: ByteBuffer
42   /// Options for verification
43   internal let _options: VerifierOptions
44 
45   /// Initializer for the verifier
46   /// - Parameters:
47   ///   - buffer: Bytebuffer that is required to be verified
48   ///   - options: `VerifierOptions` that set the rule for some of the verification done
49   ///   - checkAlignment: If alignment check is required to be preformed
50   /// - Throws: `exceedsMaxSizeAllowed` if capacity of the buffer is more than 2GiB
51   public init(
52     buffer: inout ByteBuffer,
53     options: VerifierOptions = .init(),
54     checkAlignment: Bool = true) throws
55   {
56     guard buffer.capacity < FlatBufferMaxSize else {
57       throw FlatbuffersErrors.exceedsMaxSizeAllowed
58     }
59 
60     _buffer = buffer
61     _capacity = buffer.capacity
62     _checkAlignment = checkAlignment
63     _options = options
64   }
65 
66   /// Resets the verifier to initial state
resetnull67   public mutating func reset() {
68     _depth = 0
69     _tableCount = 0
70   }
71 
72   /// Checks if the value of type `T` is aligned properly in the buffer
73   /// - Parameters:
74   ///   - position: Current position
75   ///   - type: Type of value to check
76   /// - Throws: `missAlignedPointer` if the pointer is not aligned properly
isAligned<T>null77   public mutating func isAligned<T>(position: Int, type: T.Type) throws {
78 
79     /// If check alignment is false this mutating function doesnt continue
80     if !_checkAlignment { return }
81 
82     /// advance pointer to position X
83     let ptr = _buffer._storage.memory.advanced(by: position)
84     /// Check if the pointer is aligned
85     if Int(bitPattern: ptr) & (MemoryLayout<T>.alignment &- 1) == 0 {
86       return
87     }
88 
89     throw FlatbuffersErrors.missAlignedPointer(
90       position: position,
91       type: String(describing: T.self))
92   }
93 
94   /// Checks if the value of Size "X" is within the range of the buffer
95   /// - Parameters:
96   ///   - position: Current postion to be read
97   ///   - size: `Byte` Size of readable object within the buffer
98   /// - Throws: `outOfBounds` if the value is out of the bounds of the buffer
99   /// and `apparentSizeTooLarge` if the apparent size is bigger than the one specified
100   /// in `VerifierOptions`
rangeInBuffernull101   public mutating func rangeInBuffer(position: Int, size: Int) throws {
102     let end = UInt(clamping: (position &+ size).magnitude)
103     if end > _buffer.capacity {
104       throw FlatbuffersErrors.outOfBounds(position: end, end: capacity)
105     }
106     _apparentSize = _apparentSize &+ UInt32(size)
107     if _apparentSize > _options._maxApparentSize {
108       throw FlatbuffersErrors.apparentSizeTooLarge
109     }
110   }
111 
112   /// Validates if a value of type `T` is aligned and within the bounds of
113   /// the buffer
114   /// - Parameters:
115   ///   - position: Current readable position
116   ///   - type: Type of value to check
117   /// - Throws: FlatbuffersErrors
inBuffer<T>null118   public mutating func inBuffer<T>(position: Int, of type: T.Type) throws {
119     try isAligned(position: position, type: type)
120     try rangeInBuffer(position: position, size: MemoryLayout<T>.size)
121   }
122 
123   /// Visits a table at the current position and validates if the table meets
124   /// the rules specified in the `VerifierOptions`
125   /// - Parameter position: Current position to be read
126   /// - Throws: FlatbuffersErrors
127   /// - Returns: A `TableVerifier` at the current readable table
visitTablenull128   public mutating func visitTable(at position: Int) throws -> TableVerifier {
129     let vtablePosition = try derefOffset(position: position)
130     let vtableLength: VOffset = try getValue(at: vtablePosition)
131 
132     let length = Int(vtableLength)
133     try isAligned(
134       position: Int(clamping: (vtablePosition + length).magnitude),
135       type: VOffset.self)
136     try rangeInBuffer(position: vtablePosition, size: length)
137 
138     _tableCount += 1
139 
140     if _tableCount > _options._maxTableCount {
141       throw FlatbuffersErrors.maximumTables
142     }
143 
144     _depth += 1
145 
146     if _depth > _options._maxDepth {
147       throw FlatbuffersErrors.maximumDepth
148     }
149 
150     return TableVerifier(
151       position: position,
152       vtable: vtablePosition,
153       vtableLength: length,
154       verifier: &self)
155   }
156 
157   /// Validates if a value of type `T` is within the buffer and returns it
158   /// - Parameter position: Current position to be read
159   /// - Throws: `inBuffer` errors
160   /// - Returns: a value of type `T` usually a `VTable` or a table offset
getValue<T>null161   internal mutating func getValue<T>(at position: Int) throws -> T {
162     try inBuffer(position: position, of: T.self)
163     return _buffer.read(def: T.self, position: position)
164   }
165 
166   /// derefrences an offset within a vtable to get the position of the field
167   /// in the bytebuffer
168   /// - Parameter position: Current readable position
169   /// - Throws: `inBuffer` errors & `signedOffsetOutOfBounds`
170   /// - Returns: Current readable position for a field
171   @inline(__always)
derefOffsetnull172   internal mutating func derefOffset(position: Int) throws -> Int {
173     try inBuffer(position: position, of: Int32.self)
174 
175     let offset = _buffer.read(def: Int32.self, position: position)
176     // switching to int32 since swift's default Int is int64
177     // this should be safe since we already checked if its within
178     // the buffer
179     let _int32Position = UInt32(position)
180 
181     let reportedOverflow: (partialValue: UInt32, overflow: Bool)
182     if offset > 0 {
183       reportedOverflow = _int32Position
184         .subtractingReportingOverflow(offset.magnitude)
185     } else {
186       reportedOverflow = _int32Position
187         .addingReportingOverflow(offset.magnitude)
188     }
189 
190     /// since `subtractingReportingOverflow` & `addingReportingOverflow` returns true,
191     /// if there is overflow we return failure
192     if reportedOverflow.overflow || reportedOverflow.partialValue > _buffer
193       .capacity
194     {
195       throw FlatbuffersErrors.signedOffsetOutOfBounds(
196         offset: Int(offset),
197         position: position)
198     }
199 
200     return Int(reportedOverflow.partialValue)
201   }
202 
203   /// finishes the current iteration of verification on an object
finishnull204   internal mutating func finish() {
205     _depth -= 1
206   }
207 
verifynull208   mutating func verify(id: String) throws {
209     let size = MemoryLayout<Int32>.size
210     let str = _buffer.readString(at: size, count: size)
211     if id == str {
212       return
213     }
214     throw FlatbuffersErrors.bufferIdDidntMatchPassedId
215   }
216 
217 }
218