Summary
The encoder_no_panic fuzz target found a pre-existing panic (present on main, unrelated to #180): feeding a PC (R15) register as a data operand to a Thumb-2 op that calls the verify_reg_bits contract (SDIV/UDIV/MLS/… — 11 call sites) aborts under -Cdebug-assertions:
```
CONTRACT VIOLATION [encoding::reg]: register 15 exceeds valid range (0-14)
at crates/synth-synthesis/src/contracts.rs:176 (verify_reg_bits debug_assert)
```
Crash input (Base64): `sp6FdW2FKvw=`. The fuzz target has an empty seed corpus, so each 60s run explores different random inputs — which is why this surfaced intermittently rather than on every PR.
Why it matters
The harness contract is "encode() returns Ok or Err on every input, never panics." Synth's own codegen never emits PC for these ops, so this is not a production miscompilation (release builds no-op the debug_assert); it's a totality gap in the encoder surfaced by the fuzzer.
Fix (landed in the #180 PR #183)
Converted the 11 verify_reg_bits debug-assert calls in arm_encoder.rs to a fallible reg_bits_checked(bits) -> Result<()> that returns a typed Error::synthesis for PC/R15, matching the established "return Err, not panic" pattern (#101/#120/#132). Seeded the crash input into fuzz/corpus/encoder_no_panic/ as a regression.
Follow-up
- The fuzz target should ship a committed seed corpus so the gate is deterministic rather than luck-of-the-draw (separate infra issue).
Summary
The
encoder_no_panicfuzz target found a pre-existing panic (present onmain, unrelated to #180): feeding a PC (R15) register as a data operand to a Thumb-2 op that calls theverify_reg_bitscontract (SDIV/UDIV/MLS/… — 11 call sites) aborts under-Cdebug-assertions:```
CONTRACT VIOLATION [encoding::reg]: register 15 exceeds valid range (0-14)
at crates/synth-synthesis/src/contracts.rs:176 (verify_reg_bits debug_assert)
```
Crash input (Base64): `sp6FdW2FKvw=`. The fuzz target has an empty seed corpus, so each 60s run explores different random inputs — which is why this surfaced intermittently rather than on every PR.
Why it matters
The harness contract is "
encode()returns Ok or Err on every input, never panics." Synth's own codegen never emits PC for these ops, so this is not a production miscompilation (release builds no-op thedebug_assert); it's a totality gap in the encoder surfaced by the fuzzer.Fix (landed in the #180 PR #183)
Converted the 11
verify_reg_bitsdebug-assert calls inarm_encoder.rsto a falliblereg_bits_checked(bits) -> Result<()>that returns a typedError::synthesisfor PC/R15, matching the established "return Err, not panic" pattern (#101/#120/#132). Seeded the crash input intofuzz/corpus/encoder_no_panic/as a regression.Follow-up