Skip to content

software bounds guard wraps at the address-space top: ADD-computed end address escapes the OOB trap for multi-byte accesses (found by the #166 derived trap gate) #752

Description

@avrabe

Found RED by the VCR-VER-002 derived-ARM-trap gate (#166, v0.43 verification-closure lane): the i32 load/store OOB trap-preservation VC rejects the SHIPPED --safety-bounds software guard shape for multi-byte accesses, with counterexamples pinned at the top of the address space.

The divergence

generate_load_with_bounds_check / the store twin (crates/synth-synthesis/src/instruction_selector.rs) emit:

ADD  r12, addr, #(offset + size - 1)   ; end address — WRAPPING 32-bit add
CMP  r12, r10                          ; r10 = linear-memory size
BLO  +0                                ; in-bounds skips the trap
UDF  #0
LDR/STR ...

WASM Core's effective-address check is EXACT arithmetic: trap iff addr + offset + size > mem_size. The guard computes addr + (offset+size-1) mod 2^32: for addr >= 2^32 - (offset+size-1) the end address wraps to a small value, BLO passes, and the access goes through — reading/writing below the linear-memory base (the LDR [r11, addr, #offset] hardware address also wraps). Example (word load, offset 0): addr = 0xFFFF_FFFFr12 = 3 < r10 → no trap; WASM requires one.

Byte accesses with offset 0 are exact (end = addr + 0 cannot wrap) — the gate verifies that case green, so this is specifically the multi-byte / non-zero-offset class.

Evidence

  • crates/synth-verify/src/translation_validator.rs test word_load_software_bounds_guard_wraps_at_address_top pins the divergence as Invalid with the counterexample restricted to addr >= 0xFFFF_FFFD (flip it to Verified when this is fixed).
  • Test wraparound_safe_bounds_guard_verifies proves the gate is satisfiable by a correct guard, i.e. the red is not vacuous:
    CMP r10, #k; BHS +0; UDF (bound < k ⇒ always trap) then SUB r12, r10, #k; CMP addr, r12; BLS +0; UDF with k = offset + size — exact on both paths, same instruction count +3.

Severity / exploitability

Requires a WASM-side address in the top ~3 bytes of the i32 address space, so any module whose index arithmetic can be attacker-influenced can escape the software bounds check downward by up to offset+size-1 bytes below the heap base. MPU/masking modes are unaffected (#651's masking clamps the effective address; MPU faults on the physical access).

Related: #377 (inline UDF bounds trap), #651 (masking mode), #166 (trap-preservation VC).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions