Repository navigation
fix statement nesting overflow and 0b literal folding UB - #2
Merged
Merged
Conversation
Statement parsing recurses once per nesting level, through compound blocks and the bodies of if/while/for/do/switch, with nothing bounding it. Deep input overflows the stack: 10k levels survive an 8MB stack here, 20k segfault, and dangling ifs do it as readily as nested braces. The 200 level cap matches the one expression nesting already has, and it sits at the statement entry point so brace-less nesting is covered too. Input deeper than that is now a hard error rather than a crash.
The accumulator was a signed long long, so a literal whose top bit lands in the sign position shifted into it. That is undefined rather than a wrapped value, and UBSan flagged the 64 ones case at the shift. Accumulating unsigned and converting once on the way out removes the UB and leaves the folded value alone, so a 64 bit binary literal still folds to -1.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Two Phase 2 audit findings. Both were reproduced against a build before anything changed.
Statement nesting was unbounded (
parser.c)cc_parse_compound_statement,cc_parse_statementand theif/while/for/do/switchparsers recurse through each other once per nesting level with nothing bounding the depth. Measured here on an 8MB stack:Nested braces and dangling
ifs both get there. The cap is 200, matching the expression-nesting cap the parser already has, and it sits at thecc_parse_statemententry point, so brace-less nesting is covered and not just blocks. Nested braces route throughcc_parse_statementas well, which is how a single guard covers both shapes.tests/pathological_statement_nesting.candtests/pathological_dangling_if.cpin it, following the convention of the existingpathological_nesting.c: a fixture that trips the cap, rather than one that has to actually overflow the stack to mean anything.This is a deliberate behaviour change. Input nesting deeper than 200 statements is now an error rather than a successful compile. Recovery after the cap trips also emits a run of extra
expected declaration specifierdiagnostics on the braces fixture (101 at depth 300). It terminates, the count stays bounded, and it only happens on input that is now rejected anyway, but it is worth knowing before merging.Signed left shift folding
0bliterals (sema_const.c)The fold accumulated into a signed
long long, so the 64th bit shifted into the sign position. UBSan, built with-fno-sanitize-recover=undefined:Silent after the change, and the folded value is unchanged, so
0bfollowed by 64 ones still folds to-1.tests/binary_literal_overflow.cpins the fold.Worth noting the decimal path saturates (
strtollclamps atLLONG_MAX) while the binary path wraps. That inconsistency predates this and is left alone.Dropped from the audit
cc_parse_macro_argumentsreallocs on the final argument without recording the newcapacity, which the audit listed as informational. I tried adding the assignment for consistency and clang's dead-store checker flagged it immediately, because that branch returns straight afterwards andcapacityis never read again. The audit was right that it is dead. Adding a store to make two branches look alike, at the cost of a new static-analysis warning, is not a trade worth making, sopreprocessor.cis untouched.Verification
make test: green, all suites.make sanitizers(ASan + UBSan over the full suite): green.make analyze: green, no warnings.