Skip to content

fix statement nesting overflow and 0b literal folding UB - #2

Merged
IzonIcy merged 2 commits into
mainfrom
fix/phase-two-audit
Oct 19, 2026
Merged

IzonIcy merged 2 commits into
mainfrom
fix/phase-two-audit

Conversation

@IzonIcy

@IzonIcy IzonIcy commented Oct 19, 2026

Copy link
Copy Markdown
Owner

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_statement and the if/while/for/do/switch parsers recurse through each other once per nesting level with nothing bounding the depth. Measured here on an 8MB stack:

statement nesting pre-fix
10,000 parses fine
20,000 SIGSEGV

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 the cc_parse_statement entry point, so brace-less nesting is covered and not just blocks. Nested braces route through cc_parse_statement as well, which is how a single guard covers both shapes.

tests/pathological_statement_nesting.c and tests/pathological_dangling_if.c pin it, following the convention of the existing pathological_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 specifier diagnostics 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 0b literals (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:

src/ccompiler/sema_const.c:44:30: runtime error: left shift of 9223372036854775807
by 1 places cannot be represented in type 'long long'

Silent after the change, and the folded value is unchanged, so 0b followed by 64 ones still folds to -1. tests/binary_literal_overflow.c pins the fold.

Worth noting the decimal path saturates (strtoll clamps at LLONG_MAX) while the binary path wraps. That inconsistency predates this and is left alone.

Dropped from the audit

cc_parse_macro_arguments reallocs on the final argument without recording the new capacity, 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 and capacity is 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, so preprocessor.c is untouched.

Verification

  • make test: green, all suites.
  • make sanitizers (ASan + UBSan over the full suite): green.
  • make analyze: green, no warnings.
  • The crash and UBSan reproductions above were measured on this machine, each with the fix reverted.

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.
Copilot AI balanced review requested due to automatic review settings October 19, 2026 03:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Oct 19, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 9c9fd92e-96d3-45cd-99bc-e8d7e47c47d0
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@IzonIcy
IzonIcy merged commit 61fb763 into main Oct 19, 2026
14 checks passed
@IzonIcy
IzonIcy deleted the fix/phase-two-audit branch October 19, 2026 03:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants