Found during the #651 fix (PR #654) (maintainer loop): the ARM direct selector's mask mode applied (addr & mask) + offset — offset escapes the bound — and used the memory SIZE (not size-1) as the mask. PR #654 fixed all 8 ARM sites via a single mask_effective_address helper: min((operand + offset) & (size-1), size - access_size) with the u33-soundness argument documented.
RISC-V's emit_bounds_check (crates/synth-backend-riscv/src/selector.rs) has the same operand-before-offset shape and needs the same treatment: fold the static offset BEFORE the bound check/mask, bound the FINAL byte (offset+size-1), and audit whether the comparison bound is size or size-1. Mirror #654's oracle (in-bound operand + escaping offset → red today) on the RV32 unicorn differential harness.
Refs #651, PR #654.
Found during the #651 fix (PR #654) (maintainer loop): the ARM direct selector's mask mode applied
(addr & mask) + offset— offset escapes the bound — and used the memory SIZE (not size-1) as the mask. PR #654 fixed all 8 ARM sites via a singlemask_effective_addresshelper:min((operand + offset) & (size-1), size - access_size)with the u33-soundness argument documented.RISC-V's
emit_bounds_check(crates/synth-backend-riscv/src/selector.rs) has the same operand-before-offset shape and needs the same treatment: fold the static offset BEFORE the bound check/mask, bound the FINAL byte (offset+size-1), and audit whether the comparison bound is size or size-1. Mirror #654's oracle (in-bound operand + escaping offset → red today) on the RV32 unicorn differential harness.Refs #651, PR #654.