Skip to content

Commit 3f83193

Browse files
fix(grammar): stop the Swift scanner shifting past the width of an int
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>
1 parent 5fbab7b commit 3f83193

3 files changed

Lines changed: 21 additions & 2 deletions

File tree

internal/cbm/vendored/grammars/swift/scanner.c

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/vendored-checksums.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4 internal/cbm/v
791791
2db740ea1be6b71014d2be1385491f01bf2d15b607e61c5b30b4128535efe68a internal/cbm/vendored/grammars/sway/tree_sitter/parser.h
792792
3533cec129bb4bba015c0d61d86dd7c3b7e82110e4d2ff7837a01eff5bad5ccc internal/cbm/vendored/grammars/swift/LICENSE
793793
93e6b39fc5b16ef9d5869862ec8d56380a838355c362481942942d59ca666de2 internal/cbm/vendored/grammars/swift/parser.c
794-
b835c1ded068e902944fe82c3770b9dcf85f67dcaab8fbf772f1606007da7373 internal/cbm/vendored/grammars/swift/scanner.c
794+
f3d6271d64f58c39eed544104a70ca2cf9ecbf80c5d900620f1afd38836542cb internal/cbm/vendored/grammars/swift/scanner.c
795795
b29c1c9fb7cc82f58c84b376df1297d6e2737a1d655fd356db0859e3c29c2fea internal/cbm/vendored/grammars/swift/tree_sitter/alloc.h
796796
5bdf6ed1a78e3409fd443e085ca967a64c188a5d082aaf7f819bccd53a471c94 internal/cbm/vendored/grammars/swift/tree_sitter/array.h
797797
a1f6ef161fbaf48a0e10fca90ef5290a062462b307b3898aa562993853b9f80a internal/cbm/vendored/grammars/swift/tree_sitter/parser.h

tests/test_extraction.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,6 +1810,24 @@ TEST(swift_chained_call) {
18101810
PASS();
18111811
}
18121812

1813+
/* A Swift force-unwrap is the one thing that reaches the scanner's suppressor
1814+
* path -- the rule that stops `try!` emitting its `!` as a token of its own.
1815+
* That path shifted an int by up to TOKEN_COUNT bits, which runs past the
1816+
* width of the type once the index reaches 31.
1817+
*
1818+
* This test cannot go red here. The normal test build prints the UBSan
1819+
* message and carries on, which is why the bug survived. The Windows
1820+
* CLANGARM64 leg runs UBSan in trap mode, where the same shift is an
1821+
* illegal-instruction crash, so parsing this file at all is the check. */
1822+
TEST(swift_force_unwrap_scanner_shift) {
1823+
CBMFileResult *r =
1824+
extract("func load() { let u = cached! }\n", CBM_LANG_SWIFT, "t", "Load.swift");
1825+
ASSERT_NOT_NULL(r);
1826+
ASSERT_FALSE(r->has_error);
1827+
cbm_free_result(r);
1828+
PASS();
1829+
}
1830+
18131831
/* --- Objective-C --- */
18141832
TEST(objc_interface) {
18151833
CBMFileResult *r =
@@ -6661,6 +6679,7 @@ SUITE(extraction) {
66616679
RUN_TEST(swift_method_call);
66626680
RUN_TEST(swift_constructor_call);
66636681
RUN_TEST(swift_chained_call);
6682+
RUN_TEST(swift_force_unwrap_scanner_shift);
66646683
RUN_TEST(objc_interface);
66656684
RUN_TEST(objc_implementation);
66666685
RUN_TEST(dart_top_level_function);

0 commit comments

Comments
 (0)