fix(grammar): stop the Swift try-bang entry shifting past its own type - #1986
Open
CaptainMittens wants to merge 2 commits into
Open
fix(grammar): stop the Swift try-bang entry shifting past its own type#1986CaptainMittens wants to merge 2 commits into
CaptainMittens wants to merge 2 commits into
Conversation
The Swift scanner keeps a 64-bit mask of the symbols that suppress a
match -- the rule that stops `try!` emitting its `!` as a token of its
own. It tests one bit per candidate:
uint64_t suppressing_symbols = OP_SYMBOL_SUPPRESSOR[full_match];
for (uint64_t suppressor = 0; suppressor < TOKEN_COUNT; suppressor++) {
if (!(suppressing_symbols & 1 << suppressor)) {
The mask is uint64_t but the literal `1` is an int, so the shift is an
int shift. TOKEN_COUNT is larger than 32, so once suppressor reaches 31
the shift runs past the width of the type. That is undefined behavior,
and every bit above 31 is tested against a value the standard does not
define.
Nothing caught it because nothing in the tree reached the suppressor
path. Any Swift force-unwrap does: `cached!` is enough.
UBSan reports it as:
scanner.c:514:47: runtime error: left shift of 1 by 31 places
cannot be represented in type 'int'
`1ULL` makes the literal as wide as the mask it is tested against.
The new test in tests/test_extraction.c cannot go red on its own. The
normal test build prints the UBSan message and carries on, which is why
this survived. The Windows CLANGARM64 leg runs UBSan in trap mode, and
there the same shift is an illegal-instruction crash -- so the test
exists to make sure that leg keeps parsing a force-unwrap at all.
scripts/vendored-checksums.txt records the new hash for the one changed
file, as scripts/security-vendored.sh --update writes it. Layer 8 of the
security gate compares vendored content against that manifest, so the
edit and its recorded hash belong in the same commit.
Found while adding Swift URL extraction in DeusData#1892 / DeusData#1976, and split out
of that PR so the vendored change can be reviewed on its own.
Signed-off-by: Joshua Richter <jrichter5781@gmail.com>
scanner.c line 131 shifts a literal 1UL by 32 to build the entry that suppresses the bang of a try!. unsigned long is 64 bits on Linux and macOS but 32 bits on Windows, so there the shift count equals the width of the type, which is undefined. 1ULL is 64 bits on every target this project builds for. Nothing reports it today. The expression is a compile-time constant, so no sanitizer sees it run, and Makefile.cbm:719 builds vendored grammars with -w, which switches off -Wshift-count-overflow. Upstream tree-sitter-swift made the same change in 6ab8d1d74ebd, after the commit this grammar is pinned to. Refs DeusData#1892 Signed-off-by: Joshua Richter <jrichter5781@gmail.com>
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One token in the same vendored file as #1977. This is the second of the two shifts in #1978, and the one that is undefined on Windows.
Stacked on #1977, because both changes rewrite the same recorded hash in
scripts/vendored-checksums.txt. Two branches offmainwould collide there. Until #1977 merges this PR shows both commits; after it merges the diff narrows to the one token below.The bug
internal/cbm/vendored/grammars/swift/scanner.c:131builds the entry that suppresses the!of atry!:FAKE_TRY_BANGis the last member of a 33-entryTokenTypeenum, so its value is 32.unsigned longis 64 bits on Linux and macOS. On Windows it is 32 bits, so the shift count equals the width of the type, which is undefined.1ULLis 64 bits on every target this project builds for.Why nothing catches it
Three things line up, and the third is the one I would not have guessed:
Makefile.cbm:719compiles every vendored grammar with-w. That switches off-Wshift-count-overflow, the one diagnostic that names this exact defect. The comment on line 718 gives the reason: upstream code has warnings.CLANG64onwindows-latestsits inCORE_WIN(.github/workflows/_test.yml:63), so it builds on every pull request and reports nothing.What I did and did not verify
I have no Windows machine, so I did not observe a symptom in the product. What I did do is compile the same shape locally, using
unsigned intbecause it is 32 bits everywhere:cc -Wallwarnsshift count >= width of type. Adding-w, as the grammar build does, silences it.I expected the folded value to be
0and said so in #1978 before testing. It is not. The variable is simply never assigned, and three runs of one binary printed4338156640,4364321888and4377183328; at-O2it printed8447164672. The correct form,(uint64_t)1 << 32, prints4294967296every time. So the practical effect on Windows is a suppressor entry holding whatever happens to be there, not a predictable zero.No test, and why
A test here would pass before and after the change on every machine CI can run it on.
unsigned longis already 64 bits on Linux and macOS, so the expression is correct there; the defect only exists on a target the test suite does not execute. Adding a test that cannot fail would suggest a guarantee that does not exist. The existingswift_force_unwrap_scanner_shiftfrom #1977 already keeps a force-unwrap parsing at all.Upstream
Not my own reading alone.
alex-pinkus/tree-sitter-swiftmade the same one-token change in6ab8d1d74ebd(2026-08-10). Our copy is pinned at8abb3e8b3325(2026-03-20), so that fix landed after our pin. I checked our copy byte-for-byte against upstream at that pin: it is a faithful vendor, not a local edit that drifted.Re-vendoring picks up both fixes and five months of other work, but
TOKEN_COUNTmoves from 33 to 34 upstream, so the grammar itself changed.CONTRIBUTING.md:134puts a vendored dependency change behind a design discussion, so I have not proposed it here.Files
internal/cbm/vendored/grammars/swift/scanner.c1ULto1ULLon line 131scripts/vendored-checksums.txtscripts/security-vendored.sh --updatewrites itLayer 8 of the security gate compares vendored content against that manifest, so the edit and its recorded hash land together.
scripts/security-vendored.shexits 0 on this branch.One thing worth your call
-won the grammar build is what hid this. Turning on the single warning-Wshift-count-overflowfor vendored grammars — one narrow diagnostic, not-Wall -Werror— would catch this class across all 104 vendored scanners without touching the rest of the noise. I have not made that change here. Say the word and I will open it as its own issue.Fixes #1978
Refs #1892