diff --git a/src/idl_gen_swift.cpp b/src/idl_gen_swift.cpp index 0fbbc7c29..2ecd54e69 100644 --- a/src/idl_gen_swift.cpp +++ b/src/idl_gen_swift.cpp @@ -506,12 +506,13 @@ class SwiftGenerator : public BaseGenerator { if (!struct_def.fixed) { if (parser_.file_identifier_.length()) { code_.SetValue("FILENAME", parser_.file_identifier_); + code_ += "{{ACCESS_TYPE}} static var id: String { \"{{FILENAME}}\" } "; code_ += "{{ACCESS_TYPE}} static func finish(_ fbb: inout " "FlatBufferBuilder, end: " "Offset, prefix: Bool = false) { fbb.finish(offset: end, " "fileId: " - "\"{{FILENAME}}\", addPrefix: prefix) }"; + "{{STRUCTNAME}}.id, addPrefix: prefix) }"; } code_ += "{{ACCESS_TYPE}} static func getRootAs{{SHORT_STRUCTNAME}}(bb: " diff --git a/swift/Sources/FlatBuffers/ByteBuffer.swift b/swift/Sources/FlatBuffers/ByteBuffer.swift index a07f66ae3..f7d77112a 100644 --- a/swift/Sources/FlatBuffers/ByteBuffer.swift +++ b/swift/Sources/FlatBuffers/ByteBuffer.swift @@ -400,10 +400,13 @@ public struct ByteBuffer { /// SkipPrefix Skips the first 4 bytes in case one of the following /// functions are called `getPrefixedSizeCheckedRoot` & `getPrefixedSizeRoot` /// which allows us to skip the first 4 bytes instead of recreating the buffer + @discardableResult @usableFromInline - mutating func skipPrefix() { + mutating func skipPrefix() -> Int32 { _writerSize = _writerSize &- MemoryLayout.size + return read(def: Int32.self, position: 0) } + } extension ByteBuffer: CustomDebugStringConvertible { diff --git a/swift/Sources/FlatBuffers/FlatbuffersErrors.swift b/swift/Sources/FlatBuffers/FlatbuffersErrors.swift index 74c06b9ae..ca1e92fda 100644 --- a/swift/Sources/FlatBuffers/FlatbuffersErrors.swift +++ b/swift/Sources/FlatBuffers/FlatbuffersErrors.swift @@ -19,6 +19,10 @@ import Foundation /// Collection of thrown from the Flatbuffer verifier public enum FlatbuffersErrors: Error, Equatable { + /// Thrown when verifying a file id that doesnt match buffer id + case bufferIdDidntMatchPassedId + /// Prefixed size doesnt match the current (readable) buffer size + case prefixedSizeNotEqualToBufferSize /// Thrown when buffer is bigger than the allowed 2GiB case exceedsMaxSizeAllowed /// Thrown when there is an missaligned pointer at position diff --git a/swift/Sources/FlatBuffers/Root.swift b/swift/Sources/FlatBuffers/Root.swift index 4d883b7b8..30dd5836b 100644 --- a/swift/Sources/FlatBuffers/Root.swift +++ b/swift/Sources/FlatBuffers/Root.swift @@ -28,10 +28,39 @@ import Foundation /// the ``ByteBuffer`` and verifies the buffer by calling ``getCheckedRoot(byteBuffer:options:)`` public func getPrefixedSizeCheckedRoot( byteBuffer: inout ByteBuffer, + fileId: String? = nil, options: VerifierOptions = .init()) throws -> T { byteBuffer.skipPrefix() - return try getCheckedRoot(byteBuffer: &byteBuffer, options: options) + return try getCheckedRoot( + byteBuffer: &byteBuffer, + fileId: fileId, + options: options) +} + +/// Takes in a prefixed sized buffer, where we check if the sized buffer is equal to prefix size. +/// And would verify that the buffer passed is a valid `Flatbuffers` Object. +/// - Parameters: +/// - byteBuffer: Buffer that needs to be checked and read +/// - options: Verifier options +/// - Throws: FlatbuffersErrors +/// - Returns: Returns a valid, checked Flatbuffers object +/// +/// ``getPrefixedSizeCheckedRoot(byteBuffer:options:)`` would skip the first Bytes in +/// the ``ByteBuffer`` and verifies the buffer by calling ``getCheckedRoot(byteBuffer:options:)`` +public func getCheckedPrefixedSizeRoot( + byteBuffer: inout ByteBuffer, + fileId: String? = nil, + options: VerifierOptions = .init()) throws -> T +{ + let prefix = byteBuffer.skipPrefix() + if prefix != byteBuffer.size { + throw FlatbuffersErrors.prefixedSizeNotEqualToBufferSize + } + return try getCheckedRoot( + byteBuffer: &byteBuffer, + fileId: fileId, + options: options) } /// Takes in a prefixed sized buffer, where the prefixed size would be skipped. @@ -61,9 +90,13 @@ public func getPrefixedSizeRoot(byteBuffer: inout ByteBuffe /// and within the ``ByteBuffer`` range. public func getCheckedRoot( byteBuffer: inout ByteBuffer, + fileId: String? = nil, options: VerifierOptions = .init()) throws -> T { var verifier = try Verifier(buffer: &byteBuffer, options: options) + if let fileId = fileId { + try verifier.verify(id: fileId) + } try ForwardOffset.verify(&verifier, at: 0, of: T.self) return T.init( byteBuffer, diff --git a/swift/Sources/FlatBuffers/Verifier.swift b/swift/Sources/FlatBuffers/Verifier.swift index 6f65ce702..63cfa3a15 100644 --- a/swift/Sources/FlatBuffers/Verifier.swift +++ b/swift/Sources/FlatBuffers/Verifier.swift @@ -200,4 +200,14 @@ public struct Verifier { internal mutating func finish() { _depth -= 1 } + + mutating func verify(id: String) throws { + let size = MemoryLayout.size + let str = _buffer.readString(at: size, count: size) + if id == str { + return + } + throw FlatbuffersErrors.bufferIdDidntMatchPassedId + } + } diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatbuffersVerifierTests.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatbuffersVerifierTests.swift index 750f97b97..cb26c27d4 100644 --- a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatbuffersVerifierTests.swift +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatbuffersVerifierTests.swift @@ -201,12 +201,34 @@ final class FlatbuffersVerifierTests: XCTestCase { XCTAssertNoThrow(try getCheckedRoot(byteBuffer: &byteBuffer) as Movie) } + func testErrorWrongFileId() { + // swiftformat:disable all + var byteBuffer = ByteBuffer(bytes: [20, 0, 0, 0, 77, 79, 86, 73, 12, 0, 12, 0, 0, 0, 0, 0, 8, 0, 4, 0, 12, 0, 0, 0, 8, 0, 0, 0, 20, 0, 0, 0, 3, 0, 0, 0, 24, 0, 0, 0, 32, 0, 0, 0, 12, 0, 0, 0, 3, 0, 0, 0, 3, 1, 4, 0, 2, 0, 0, 0, 7, 0, 0, 0, 0, 0, 6, 0, 8, 0, 4, 0, 6, 0, 0, 0, 8, 0, 0, 0]) + // swiftformat:enable all + XCTAssertThrowsError(try getCheckedRoot(byteBuffer: &byteBuffer, fileId: "FLEX") as Movie) + } + + func testVerifyPrefixedBuffer() { + // swiftformat:disable all + var byteBuffer = ByteBuffer(bytes: [0, 0, 0, 1, 20, 0, 0, 0, 77, 79, 86, 73, 12, 0, 12, 0, 0, 0, 0, 0, 8, 0, 4, 0, 12, 0, 0, 0, 8, 0, 0, 0, 20, 0, 0, 0, 3, 0, 0, 0, 24, 0, 0, 0, 32, 0, 0, 0, 12, 0, 0, 0, 3, 0, 0, 0, 3, 1, 4, 0, 2, 0, 0, 0, 7, 0, 0, 0, 0, 0, 6, 0, 8, 0, 4, 0, 6, 0, 0, 0, 8, 0, 0, 0]) + // swiftformat:enable all + XCTAssertThrowsError( + try getCheckedPrefixedSizeRoot(byteBuffer: &byteBuffer) as Movie) + } + func testFullVerifier() { XCTAssertNoThrow( try getCheckedRoot( byteBuffer: &validFlatbuffersObject) as MyGame_Example_Monster) } + func testFullVerifierWithFileId() { + XCTAssertNoThrow( + try getCheckedRoot( + byteBuffer: &validFlatbuffersObject, + fileId: MyGame_Example_Monster.id) as MyGame_Example_Monster) + } + func testInvalidBuffer() { XCTAssertThrowsError( try getCheckedRoot( diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test_generated.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test_generated.swift index e7c40e3e9..fd7e755c5 100644 --- a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test_generated.swift +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test_generated.swift @@ -683,7 +683,8 @@ public struct MyGame_InParentNamespace: FlatBufferObject, Verifiable, ObjectAPIP public var __buffer: ByteBuffer! { return _accessor.bb } private var _accessor: Table - public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + public static var id: String { "MONS" } + public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: MyGame_InParentNamespace.id, addPrefix: prefix) } public static func getRootAsInParentNamespace(bb: ByteBuffer) -> MyGame_InParentNamespace { return MyGame_InParentNamespace(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } private init(_ t: Table) { _accessor = t } @@ -736,7 +737,8 @@ public struct MyGame_Example2_Monster: FlatBufferObject, Verifiable, ObjectAPIPa public var __buffer: ByteBuffer! { return _accessor.bb } private var _accessor: Table - public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + public static var id: String { "MONS" } + public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: MyGame_Example2_Monster.id, addPrefix: prefix) } public static func getRootAsMonster(bb: ByteBuffer) -> MyGame_Example2_Monster { return MyGame_Example2_Monster(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } private init(_ t: Table) { _accessor = t } @@ -789,7 +791,8 @@ internal struct MyGame_Example_TestSimpleTableWithEnum: FlatBufferObject, Verifi internal var __buffer: ByteBuffer! { return _accessor.bb } private var _accessor: Table - internal static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + internal static var id: String { "MONS" } + internal static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: MyGame_Example_TestSimpleTableWithEnum.id, addPrefix: prefix) } internal static func getRootAsTestSimpleTableWithEnum(bb: ByteBuffer) -> MyGame_Example_TestSimpleTableWithEnum { return MyGame_Example_TestSimpleTableWithEnum(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } private init(_ t: Table) { _accessor = t } @@ -871,7 +874,8 @@ public struct MyGame_Example_Stat: FlatBufferObject, Verifiable, ObjectAPIPacker public var __buffer: ByteBuffer! { return _accessor.bb } private var _accessor: Table - public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + public static var id: String { "MONS" } + public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: MyGame_Example_Stat.id, addPrefix: prefix) } public static func getRootAsStat(bb: ByteBuffer) -> MyGame_Example_Stat { return MyGame_Example_Stat(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } private init(_ t: Table) { _accessor = t } @@ -1011,7 +1015,8 @@ public struct MyGame_Example_Referrable: FlatBufferObject, Verifiable, ObjectAPI public var __buffer: ByteBuffer! { return _accessor.bb } private var _accessor: Table - public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + public static var id: String { "MONS" } + public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: MyGame_Example_Referrable.id, addPrefix: prefix) } public static func getRootAsReferrable(bb: ByteBuffer) -> MyGame_Example_Referrable { return MyGame_Example_Referrable(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } private init(_ t: Table) { _accessor = t } @@ -1118,7 +1123,8 @@ public struct MyGame_Example_Monster: FlatBufferObject, Verifiable, ObjectAPIPac public var __buffer: ByteBuffer! { return _accessor.bb } private var _accessor: Table - public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + public static var id: String { "MONS" } + public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: MyGame_Example_Monster.id, addPrefix: prefix) } public static func getRootAsMonster(bb: ByteBuffer) -> MyGame_Example_Monster { return MyGame_Example_Monster(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } private init(_ t: Table) { _accessor = t } @@ -2275,7 +2281,8 @@ public struct MyGame_Example_TypeAliases: FlatBufferObject, Verifiable, ObjectAP public var __buffer: ByteBuffer! { return _accessor.bb } private var _accessor: Table - public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + public static var id: String { "MONS" } + public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: MyGame_Example_TypeAliases.id, addPrefix: prefix) } public static func getRootAsTypeAliases(bb: ByteBuffer) -> MyGame_Example_TypeAliases { return MyGame_Example_TypeAliases(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } private init(_ t: Table) { _accessor = t } diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/optional_scalars_generated.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/optional_scalars_generated.swift index 0f427c8a8..0e43838ef 100644 --- a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/optional_scalars_generated.swift +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/optional_scalars_generated.swift @@ -33,7 +33,8 @@ public struct optional_scalars_ScalarStuff: FlatBufferObject, Verifiable { public var __buffer: ByteBuffer! { return _accessor.bb } private var _accessor: Table - public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "NULL", addPrefix: prefix) } + public static var id: String { "NULL" } + public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: optional_scalars_ScalarStuff.id, addPrefix: prefix) } public static func getRootAsScalarStuff(bb: ByteBuffer) -> optional_scalars_ScalarStuff { return optional_scalars_ScalarStuff(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } private init(_ t: Table) { _accessor = t } diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/union_vector_generated.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/union_vector_generated.swift index 59ed00530..25a23f89c 100644 --- a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/union_vector_generated.swift +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/union_vector_generated.swift @@ -328,7 +328,8 @@ public struct Attacker: FlatBufferObject, Verifiable, ObjectAPIPacker { public var __buffer: ByteBuffer! { return _accessor.bb } private var _accessor: Table - public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MOVI", addPrefix: prefix) } + public static var id: String { "MOVI" } + public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: Attacker.id, addPrefix: prefix) } public static func getRootAsAttacker(bb: ByteBuffer) -> Attacker { return Attacker(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } private init(_ t: Table) { _accessor = t } @@ -410,7 +411,8 @@ public struct HandFan: FlatBufferObject, Verifiable, ObjectAPIPacker { public var __buffer: ByteBuffer! { return _accessor.bb } private var _accessor: Table - public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MOVI", addPrefix: prefix) } + public static var id: String { "MOVI" } + public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: HandFan.id, addPrefix: prefix) } public static func getRootAsHandFan(bb: ByteBuffer) -> HandFan { return HandFan(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } private init(_ t: Table) { _accessor = t } @@ -492,7 +494,8 @@ public struct Movie: FlatBufferObject, Verifiable, ObjectAPIPacker { public var __buffer: ByteBuffer! { return _accessor.bb } private var _accessor: Table - public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MOVI", addPrefix: prefix) } + public static var id: String { "MOVI" } + public static func finish(_ fbb: inout FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: Movie.id, addPrefix: prefix) } public static func getRootAsMovie(bb: ByteBuffer) -> Movie { return Movie(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } private init(_ t: Table) { _accessor = t }