Skip to content

Pragma keyword fuzz tests draw their random char via charFromMask on complement masks, so every run tests exactly NUL #567

Description

@thedavidmeister

Unit

test/src/lib/parse/LibParsePragma.keyword.t.sol, three draws:

  • line ~106: charFromMask(seed, ~CMASK_WHITESPACE) in testPragmaKeywordNoWhitespace
  • line ~124: charFromMask(seed + 1, ~CMASK_INTERSTITIAL_HEAD) in testPragmaKeywordWhitespaceNoHex
  • line ~156: charFromMask(seed, ~CMASK_HEX) in testPragmaKeywordParseSubParserBasic

Violated property

LibConformString.charFromMask probes linearly from char 0 and only consults the seed after a miss (documented behavior as of rain.string PR #58). NUL is in none of WHITESPACE / INTERSTITIAL_HEAD / HEX, so every complement mask has bit 0 set and every one of these draws returns "\x00" for every seed. Across all fuzz runs the three tests exercise exactly one character each, while reading as if they cover the whole complement set.

testPragmaKeywordWhitespaceNoHex's cursor assertion is load-bearing on the bias: it holds because NUL is not a literal head. A uniform draw over ~CMASK_INTERSTITIAL_HEAD would hit 0-9/-/"/[ (~13/128 per draw), parsePragma's literal path would consume further, and assertEq(cursorAfter, cursor + 17) would fail — i.e. the test would find real behavior it currently never reaches.

Proposed fix

Derive the char from the seed across the whole target set — e.g. draw a byte from the seed and reroll while it misses the intended set (the conform-style loop), or use charFromMask(uint256(keccak256(abi.encode(seed))), mask) only for masks without bit 0 and an explicit rejection loop otherwise. Then make the WhitespaceNoHex expectation robust to non-NUL draws (restrict the set deliberately, or handle the literal-path outcome).

Triage note

charFromMask's bias is now documented upstream and rain.string deliberately kept it (behavior change would break exactly these tests). The gap is on this side: the tests claim fuzz coverage they don't have.

Found by the 2026-08-24 rain.string CMask consumer-oracle audit.

🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

adversarialFound by adversarial testingauditAudit finding

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions