Guard generated C against non-arithmetic signed right shift (scrutineer #2526) - #2403
Draft
JasonGross wants to merge 1 commit into
Draft
Guard generated C against non-arithmetic signed right shift (scrutineer #2526)#2403JasonGross wants to merge 1 commit into
JasonGross wants to merge 1 commit into
Conversation
The C emitter prepends exactly one target guard to every generated file:
#if (-1 & 3) != 3
#error "This code only works on a two's complement system"
#endif
That guard tests the representation of negative integers. It does not
test what `>>` does to a negative signed value, which C99/C11/C17
6.5.7p5 leave implementation-defined (an implementation may shift in
zeros). The generated `subborrowx_u<w>` functions for non-power-of-two
limb widths compute the borrow as `(int1)(x1 >> w)` on a negative
`int<bits>_t` and rely on the shift being arithmetic; with a logical
shift, e.g. `fiat_25519_subborrowx_u26` returns `out2 = 193` for most
in-bounds inputs, outside its printed output bound `[0x0 ~> 0x1]`
(scrutineer finding #2526). Every toolchain in current use performs an
arithmetic shift here, and C23 mandates it, so this is a completeness
gap in the stated compile-time refusal rather than a live bug.
Emit a second guard immediately after the first:
#if ((-1) >> 1) != -1
#error "This code only works on a system with arithmetic right shift of negative signed values"
#endif
`#if` arithmetic is performed in intmax_t, so the expression evaluates
the same property the generated code relies on. gcc 12 and clang 14
accept it with -Wall -Wextra -Wpedantic -Werror under -std=c99, c11,
c17 and c2x.
The fiat-c/src/*.c outputs were updated by script (the guard is fixed
text, inserted directly after the existing #endif); CI's regeneration
check should confirm them. Other backends need no change: Rust, Go,
Java and Zig define `>>` on signed integers as arithmetic, JSON carries
no semantics, and the bedrock2 C output emits no signed right shifts
(its prelude comes from bedrock2's own ToCString).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Rbn2gww3MGhvrh52fNjpD
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.
Fixes scrutineer finding #2526 (CWE-758, severity Low).
Bug
The C emitter (
src/Stringification/C.v) prepends one target guard to every generated file,#if (-1 & 3) != 3/#error "This code only works on a two's complement system". That guard tests the representation of negative integers only. The generatedsubborrowx_u<w>functions for non-power-of-two limb widths (curve25519, p448_solinas, p521, poly1305) compute the borrow as(int1)(x1 >> w)on a negativeint<bits>_t, which relies on>>being an arithmetic (sign-extending) shift. C99/C11/C17 6.5.7p5 leave that implementation-defined; only C23 mandates it. Under the other conforming choice (shifting in zeros),fiat_25519_subborrowx_u26returnsout2 = 193for 5 of 6 in-bounds test inputs, outside its printed output bound[0x0 ~> 0x1](reproduced locally with the finding's harness). No toolchain in current use does this, so this is a completeness gap in the compile-time refusal the project advertises, not a live bug.Fix
Emit a second guard immediately after the existing one:
#ifarithmetic is done inintmax_t, so this tests exactly the property the generated code depends on, and a logical-shift implementation would refuse to compile the file instead of silently returning out-of-bound borrows.Alternatives considered:
Other backends need no change: Rust, Go, Java and Zig define
>>on signed integers as arithmetic in their language specs; JSON carries no semantics; the bedrock2 C output contains no signed right shifts at all, and its prelude is produced by bedrock2's ownToCString, not this emitter.Generated files
All 32
fiat-c/src/*.cfiles were updated by script (the guard is fixed text inserted directly after the existing#endif), not by regenerating with a rebuilt binary. The regeneration check in CI must confirm they match the emitter. No other tracked file carries the two's-complement guard (git grepover the tree finds onlyfiat-c/src/*.candC.v).Verification
coqc -q -R src Crypto ... src/Stringification/C.vagainst the installed library: compiles (only the pre-existingmissing-proof-commandwarning on line 398).-Wall -Wextra -Wpedantic -Werrorunder gcc 12.2 and clang 14.0 for-std=c99,c11,c17,c2x(8/8 ok). A negated control (#if ((-1) >> 1) != 0) correctly triggers the#error, so the guard is live rather than vacuous.make -f Makefile.examples only-test-c-files CC=gccandCC=clang(i.e.-Wall -Wno-unused-function -Wpedantic -Werror -cover all 32fiat-c/src/*.cplusinversion/c/*_test.c): both exit 0, 36 object files each.fiat_25519_subborrowx_u26beside a copy with a logical shift givesout2 = 193for 5 of 6 in-bounds inputs, confirming the claim in the finding.Not verified: no synthesis binary was rebuilt, so the regenerated-output equality relies on CI.
🤖 Generated with Claude Code
https://claude.ai/code/session_016Rbn2gww3MGhvrh52fNjpD