blob: 9ac3974302c8d7ccd0463389a0149a312d4b2c5e [file] [log] [blame]
James Kuszmaul8e62b022022-03-22 09:33:25 -07001/*
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
Austin Schuh2dd86a92022-09-14 21:19:23 -070017#if !os(WASI)
James Kuszmaul8e62b022022-03-22 09:33:25 -070018import Foundation
Austin Schuh2dd86a92022-09-14 21:19:23 -070019#else
20import SwiftOverlayShims
21#endif
James Kuszmaul8e62b022022-03-22 09:33:25 -070022
23/// Verifier that check if the buffer passed into it is a valid,
24/// safe, aligned Flatbuffers object since swift read from `unsafeMemory`
25public 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
67 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
77 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`
101 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
118 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
128 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
161 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)
172 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
204 internal mutating func finish() {
205 _depth -= 1
206 }
Austin Schuh2dd86a92022-09-14 21:19:23 -0700207
208 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
James Kuszmaul8e62b022022-03-22 09:33:25 -0700217}