Verifier.swift (7501B)
1 /* 2 * Copyright 2023 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 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 } 207 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 217 }