From 4cc88dc4c81d206cb3e1ee9b4c517eb5b0a5da43 Mon Sep 17 00:00:00 2001 From: Ali Sherif Date: Thu, 7 May 2026 17:55:55 +0300 Subject: [PATCH] [Swift] Fix verifier accepting truncated scalar vectors (OOB read/write, RCE) getCheckedRoot accepted malformed FlatBuffers whose scalar vector element count did not match the available payload bytes. After verification succeeded, the generated accessors and mutators read and wrote past ByteBuffer.capacity, producing out-of-bounds memory disclosure, out-of-bounds memory corruption, and code execution when the corrupted adjacent control data was later invoked. Root cause: verifyRange passed the vector element count to rangeInBuffer as a byte count. For any scalar vector whose element size is greater than one byte (e.g. [long], [int], [double]), the declared length only had to be at most byteBuffer.capacity bytes beyond the vector header, instead of count * elementSize bytes, so a buffer declaring N elements but containing only N raw bytes was accepted. Fix: multiply the declared element count by MemoryLayout.size with overflow detection and pass the resulting byte size to rangeInBuffer, so truncated scalar vectors are rejected before any unsafe generated access can occur. Adds a regression test that builds a Swift_Tests_Vectors-shaped buffer declaring a length-2 ulong vector backed by only 2 bytes of payload and asserts getCheckedRoot throws. --- swift/Sources/FlatBuffers/Verifiable.swift | 9 +++++++- .../FlatbuffersVerifierTests.swift | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/swift/Sources/FlatBuffers/Verifiable.swift b/swift/Sources/FlatBuffers/Verifiable.swift index 42259e6f35..9f4bcf6d78 100644 --- a/swift/Sources/FlatBuffers/Verifiable.swift +++ b/swift/Sources/FlatBuffers/Verifiable.swift @@ -56,8 +56,15 @@ extension Verifiable { let len: UOffset = try verifier.getValue(at: position) let intLen = Int(len) let start = Int(clamping: (position &+ MemoryLayout.size).magnitude) + let byteCount = intLen.multipliedReportingOverflow( + by: MemoryLayout.size) + guard !byteCount.overflow else { + throw FlatbuffersErrors.outOfBounds( + position: UInt.max, + end: verifier.capacity) + } try verifier.isAligned(position: start, type: type.self) - try verifier.rangeInBuffer(position: start, size: intLen) + try verifier.rangeInBuffer(position: start, size: byteCount.partialValue) return (start, intLen) } } diff --git a/tests/swift/Tests/Flatbuffers/FlatbuffersVerifierTests.swift b/tests/swift/Tests/Flatbuffers/FlatbuffersVerifierTests.swift index b116f1d9cb..b4c94ca25a 100644 --- a/tests/swift/Tests/Flatbuffers/FlatbuffersVerifierTests.swift +++ b/tests/swift/Tests/Flatbuffers/FlatbuffersVerifierTests.swift @@ -411,6 +411,27 @@ final class FlatbuffersVerifierTests { } } + @Test(.bug("https://github.com/google/flatbuffers/issues/9082")) + func testRejectsTruncatedScalarVector() { + // swiftformat:disable all + var byteBuffer = ByteBuffer(bytes: [ + 16, 0, 0, 0, + 6, 0, 8, 0, + 4, 0, 0, 0, + 0, 0, 0, 0, + 12, 0, 0, 0, + 8, 0, 0, 0, + 0, 0, 0, 0, + 2, 0, 0, 0, + 65, 66, + ]) + // swiftformat:enable all + + #expect(throws: FlatbuffersErrors.self) { + try getCheckedRoot(byteBuffer: &byteBuffer) as Swift_Tests_Vectors + } + } + @Test func testValidUnionBuffer() { let string = "Awesome \\\\t\t\nstring!"