Skip to content

[Swift] Enforce read bounds in release builds (assert -> precondition) and add missing guards - #9277

Open
fthomasella wants to merge 1 commit into
google:masterfrom
fthomasella:patch/swift-release-bounds
Open

fthomasella wants to merge 1 commit into
google:masterfrom
fthomasella:patch/swift-release-bounds

Conversation

@fthomasella

Copy link
Copy Markdown

What

The Swift readers guarded out-of-bounds reads only with assert(), which compiles away in release builds (-O). On buffers from untrusted input that skip (or predate) the verifier, those reads walk silently past the allocation. Additionally, TypedVector.subscript and FlexBuffers getRootChecked had no bounds guard at all (the latter still carries a verifier TODO).

Follows the direction of #9081 ([Swift] Fix verifier accepting truncated scalar vectors (OOB read/write, RCE)) — this closes the remaining unguarded read paths.

Changes

  • FlatBuffers/ByteBuffer.swift: read/readSlice/readString read-path asserts → precondition() (enforced in -O); builder/write-path asserts untouched. skipPrefix now guards the prefix read on undersized buffers.
  • FlexBuffers/ByteBuffer.swift: same conversion on the read paths.
  • FlexBuffers/Reader/TypedVector.swift: subscript guards index < count (was fully unchecked).
  • FlexBuffers/Reader/Reference.swift: getRootChecked fails closed on buffers too small to hold any root until a full verifier exists.
  • New in-bounds regression tests for every guarded path (ByteBufferBoundsRegressionTests).

Demonstration (Linux, swift 6.0, -O)

// master:  readSlice(0, 64) on a 4-byte buffer:
41 42 43 44 ff ff 00 00 41 00 00 00 …   <- heap-adjacent memory, exit 0
// patched: Fatal error / precondition failure (fail closed)

Full before/after transcripts and the tiny demo program: happy to attach if useful.

Testing

  • swift build + full swift test suite pass (89 existing + 4 new tests), swift 6.0 on Linux (CI matrix covers 6.0/6.1/6.2 + Windows + WASM — the guards are plain precondition calls, no platform-specific behavior).
  • Release-mode before/after verified 3x: master leaks silently; patched traps.

Notes

…) and add missing guards

The FlatBuffers/Swift readers (ByteBuffer.read/readSlice/readString,
FlexBuffers counterparts, skipPrefix) guarded out-of-bounds reads only
with assert(), which compiles away in release builds: reading attacker-
controlled FlatBuffers/FlexBuffers without a prior verifier pass then
performs silent out-of-bounds reads of heap-adjacent memory (see
precedent google#9081). TypedVector subscript and FlexBuffers getRootChecked
had no bounds guard at all (the latter still carries a TODO verifier).

- Convert the read-path asserts to precondition() so they hold in
  release (-O) as well; builder-side (write-path) asserts untouched.
- skipPrefix: guard the prefix read against undersized buffers.
- TypedVector.subscript: guard index against the vector count.
- getRootChecked: fail closed on buffers too small to hold any root.
- Add in-bounds regression tests for the guarded paths.

Demonstration (linux, swift 6, -O): readSlice(0,64) against a 4-byte
buffer returns 64 bytes on master (41 42 43 44 + heap-adjacent), traps
with this patch.
@google-cla

google-cla Bot commented Sep 25, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@github-actions github-actions Bot added the swift label Sep 25, 2026
@fthomasella

Copy link
Copy Markdown
Author

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

done

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant