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