Summary
Every repository-facing contract declares state numeric, including the native tracePcIndir boundary, but exploreState returns unless state is a string. Thus all valid calls are discarded while invalid strings reach the numeric native API.
Impact: Calls such as the maze example's jazzer.exploreState(hash(x, y), 0) never submit state feedback to libFuzzer, so the documented feature cannot guide exploration. Untyped calls with a string do reach a native function declared to require a number.
Code path
Checked against current main at commit 4c14e1bf63d82475ea057d6208e6f2eac87fa680.
packages/fuzzer/trace.ts:198-218
packages/fuzzer/addon.ts:56-62
packages/bug-detectors/DEVELOPMENT.md:113-118
examples/maze/fuzz.js:57-60
Steps to reproduce
Validation level: current HEAD supplemental dynamic witness reproduced.
node -e 'const fs=require("fs"); const source=fs.readFileSync("packages/fuzzer/trace.ts","utf8"); const match=source.match(/export function exploreState\(state: number, id: number\) \{[\s\S]*?\n\}/); if(!match) throw new Error("function not found"); const js=match[0].replace("export ","").replace("state: number","state").replace("id: number","id"); const calls=[]; const fn=Function("tracer",js+"; return exploreState")({tracePcIndir:(...args)=>calls.push(args)}); fn(7,11); console.log(JSON.stringify({input:[7,11],tracePcIndirCalls:calls}));'
Observed output:
{"input":[7,11],"tracePcIndirCalls":[]}
Neighboring control:
node -e 'const fs=require("fs"); const source=fs.readFileSync("packages/fuzzer/trace.ts","utf8"); const match=source.match(/export function exploreState\(state: number, id: number\) \{[\s\S]*?\n\}/); if(!match) throw new Error("function not found"); const js=match[0].replace("export ","").replace("state: number","state").replace("id: number","id"); const calls=[]; const fn=Function("tracer",js+"; return exploreState")({tracePcIndir:(...args)=>calls.push(args)}); fn("7",11); console.log(JSON.stringify({input:["7",11],tracePcIndirCalls:calls}));'
Control output:
{"input":["7",11],"tracePcIndirCalls":[[11,"7"]]}
Expected behavior
The documented/source-grounded contract should hold without the drift described above.
Actual behavior
exploreState rejects every contract-conforming numeric state while allowing strings through to the numeric tracer boundary.
Existing coverage
I checked the current issue and PR lists for overlapping titles/root-cause keywords before filing this. I did not find an item covering this same root cause.
Suggested fix
Correct exploreState's runtime guard to require a numeric state and numeric identifier.
Suggested tests
- Add a regression test for the reproduction above.
- Add a neighboring control assertion so the intended non-bug path remains covered.
Submitted with Codex.
Summary
Every repository-facing contract declares
statenumeric, including the nativetracePcIndirboundary, butexploreStatereturns unlessstateis a string. Thus all valid calls are discarded while invalid strings reach the numeric native API.Impact: Calls such as the maze example's
jazzer.exploreState(hash(x, y), 0)never submit state feedback to libFuzzer, so the documented feature cannot guide exploration. Untyped calls with a string do reach a native function declared to require a number.Code path
Checked against current
mainat commit4c14e1bf63d82475ea057d6208e6f2eac87fa680.packages/fuzzer/trace.ts:198-218packages/fuzzer/addon.ts:56-62packages/bug-detectors/DEVELOPMENT.md:113-118examples/maze/fuzz.js:57-60Steps to reproduce
Validation level: current HEAD supplemental dynamic witness reproduced.
node -e 'const fs=require("fs"); const source=fs.readFileSync("packages/fuzzer/trace.ts","utf8"); const match=source.match(/export function exploreState\(state: number, id: number\) \{[\s\S]*?\n\}/); if(!match) throw new Error("function not found"); const js=match[0].replace("export ","").replace("state: number","state").replace("id: number","id"); const calls=[]; const fn=Function("tracer",js+"; return exploreState")({tracePcIndir:(...args)=>calls.push(args)}); fn(7,11); console.log(JSON.stringify({input:[7,11],tracePcIndirCalls:calls}));'Observed output:
Neighboring control:
node -e 'const fs=require("fs"); const source=fs.readFileSync("packages/fuzzer/trace.ts","utf8"); const match=source.match(/export function exploreState\(state: number, id: number\) \{[\s\S]*?\n\}/); if(!match) throw new Error("function not found"); const js=match[0].replace("export ","").replace("state: number","state").replace("id: number","id"); const calls=[]; const fn=Function("tracer",js+"; return exploreState")({tracePcIndir:(...args)=>calls.push(args)}); fn("7",11); console.log(JSON.stringify({input:["7",11],tracePcIndirCalls:calls}));'Control output:
Expected behavior
The documented/source-grounded contract should hold without the drift described above.
Actual behavior
exploreState rejects every contract-conforming numeric state while allowing strings through to the numeric tracer boundary.
Existing coverage
I checked the current issue and PR lists for overlapping titles/root-cause keywords before filing this. I did not find an item covering this same root cause.
Suggested fix
Correct exploreState's runtime guard to require a numeric state and numeric identifier.
Suggested tests
Submitted with Codex.