Summary
The Swift Verifiable.verifyRange helper used by getCheckedRoot accepts malformed FlatBuffers whose scalar-vector element count does not match the available payload bytes. After verification reports success, the generated accessors and mutators read and write past ByteBuffer.capacity.
Originally reported privately via Google IssueTracker (issue 510740173, non-public). Filing here for a public reference and to link the fix.
Affected
- Language: Swift
- File:
swift/Sources/FlatBuffers/Verifiable.swift
- Entry point:
getCheckedRoot(byteBuffer:) for any table containing a scalar vector with element size > 1 byte (e.g. [long], [int], [double], [ulong]).
Root cause
verifyRange passes the vector's declared element count directly to rangeInBuffer as if it were a byte count. For any scalar vector whose element size is greater than one byte, the declared length only has to fit byteBuffer.capacity bytes after the vector header, instead of count * elementSize bytes. A buffer declaring N elements while containing only N raw bytes of payload therefore passes verification.
Once verification succeeds, the generated typed accessors index the vector at start + i * MemoryLayout<T>.size, which can land arbitrarily far past the end of the buffer. The corresponding mutators perform out-of-bounds writes.
Impact
- Out-of-bounds memory disclosure via generated read accessors.
- Out-of-bounds memory corruption via generated mutators.
- Reachable from any code path that calls
getCheckedRoot on attacker-controlled bytes, i.e. exactly the API intended to make untrusted input safe.
Fix
Multiply the declared element count by MemoryLayout<T>.size with overflow detection, and pass the resulting byte size to rangeInBuffer, so truncated scalar vectors are rejected at verification time before any unsafe generated access can occur.
Pull request: #9081
Summary
The Swift
Verifiable.verifyRangehelper used bygetCheckedRootaccepts malformed FlatBuffers whose scalar-vector element count does not match the available payload bytes. After verification reports success, the generated accessors and mutators read and write pastByteBuffer.capacity.Originally reported privately via Google IssueTracker (issue 510740173, non-public). Filing here for a public reference and to link the fix.
Affected
swift/Sources/FlatBuffers/Verifiable.swiftgetCheckedRoot(byteBuffer:)for any table containing a scalar vector with element size > 1 byte (e.g.[long],[int],[double],[ulong]).Root cause
verifyRangepasses the vector's declared element count directly torangeInBufferas if it were a byte count. For any scalar vector whose element size is greater than one byte, the declared length only has to fitbyteBuffer.capacitybytes after the vector header, instead ofcount * elementSizebytes. A buffer declaring N elements while containing only N raw bytes of payload therefore passes verification.Once verification succeeds, the generated typed accessors index the vector at
start + i * MemoryLayout<T>.size, which can land arbitrarily far past the end of the buffer. The corresponding mutators perform out-of-bounds writes.Impact
getCheckedRooton attacker-controlled bytes, i.e. exactly the API intended to make untrusted input safe.Fix
Multiply the declared element count by
MemoryLayout<T>.sizewith overflow detection, and pass the resulting byte size torangeInBuffer, so truncated scalar vectors are rejected at verification time before any unsafe generated access can occur.Pull request: #9081