fix(encoder): T3 ADD.W raw-imm packing — ADDW (T4) for static offsets 0x100..0xFFF (#681) - #690
Merged
Merged
Conversation
….0xFFF (#681) encode_thumb32_add_imm packed the RAW immediate into the T3 ADD.W i:imm3:imm8 field, which is a ThumbExpandImm MODIFIED immediate — correct only for imm <= 0xFF. ThumbExpandImm(0x200) = 0 and ThumbExpandImm(0x400) = 0x8000_0000, so every dynamic-address load/store with a static offset in [256, 4095] silently computed a WRONG address. In --safety-bounds software the guard (correct T4 ADDW) checked the intended address while the access used the mis-encoded one — a bounds-check bypass. #253/#255 ThumbExpandImm class, reached via the #382 paths. Fix: imm <= 0xFFF delegates to encode_thumb32_add, which already picks T3 (<= 0xFF, raw == expanded, bit-identical) vs ADDW T4 plain imm12 (0x100..=0xFFF) per #253. The > 0xFFF MOVW/MOVT path is unchanged, so byte sizes are unchanged (estimator agreement #511 stays green). Class audit (no wildcard survives, #634 style): - ArmOp::Rsb (Thumb T2): field is ThumbExpandImm-coded with NO plain imm12 form — now gated on try_thumb_expand_imm, Err on non-representable. All emitters use imm 32 (byte-identical). - ArmOp::Rsb (A32): imm was silently masked & 0xFF (#378 class) — now Err for imm > 0xFF. - encode_thumb32_and_imm_raw: raw-packed ThumbExpandImm field — now gated; only caller (POPCNT, #0x3F) byte-identical. - encode_thumb32_sub/adds/subs/cmp already correct (T4 / expand-gated). Oracles: - test_encode_add_imm_thumb_expand_681: clang -target thumbv7m pinned bit-for-bit (0xFF/0x100/0x104/0x200/0x3FC/0x400/0xFFF + rd/rn perm). - test_encode_add_imm_large_350's 0x123 assertion upgraded from length-only (which let the mis-encoding pass CI) to exact bytes. - scripts/repro/addw_offset_681_differential.py: unicorn-vs-wasmtime, dynamic base + static offsets, i32/i8/i16/i64 load+store, bounds none+software incl. the bypass pin and OOB trap-to-trap. RED on pre-fix main (36 mismatches: clobber returns 4660 not 111; offset 1024 faults 2 GiB past base), GREEN post-fix (49/49). CI-wired in the trap-semantics oracle job. - Frozen anchors 10/10 byte-identical; estimator agreement green; workspace tests, fmt, clippy -D warnings clean. Closes #681 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #681.
Bug
encode_thumb32_add_immpacked a raw immediate into the T3ADD.Wi:imm3:imm8field. That field is a ThumbExpandImm modified immediate (raw == expanded only forimm <= 0xFF):ThumbExpandImm(0x200) = 0,ThumbExpandImm(0x400) = 0x8000_0000. Every dynamic-addressi32/i64/i8/i16load/store with a staticoffset ∈ [0x100, 0xFFF]silently accessed the wrong address — and in--safety-bounds softwarethe guard (correct T4ADDW) checked the intended address while the access used the mis-encoded one: a bounds-check bypass. Exact #253/#255 ThumbExpandImm class, in a helper #255's audit didn't cover, reached via the #382 paths.Fix
imm <= 0xFFFnow delegates toencode_thumb32_add, which already implements the correct split per #253:imm <= 0xFF→ T3ADD.W(raw == expanded — bit-identical to before)0x100..=0xFFF→ ADDW (T4,0xF200, plain imm12)> 0xFFF→ unchanged MOVW/MOVT + ADD.W-register path (arm backend: "ADD immediate too large for single instruction" — gale_k_stack_push_decide (struct-return) fails to compile (0.11.42 + 0.11.43) #350)Byte sizes are unchanged everywhere, so the #511 estimator↔encoder agreement oracle stays green as-is.
Class audit (no wildcard survives)
encode_thumb32_add_immencode_thumb32_addArmOp::Rsb(Thumb T2)try_thumb_expand_imm,Erron non-representable. All emitters use#32→ byte-identicalArmOp::Rsb(A32)*imm & 0xFFsilent masking (#378 class)Errforimm > 0xFFencode_thumb32_and_imm_raw#0x3F) byte-identicalencode_thumb32_sub/adds/subs/cmp/ orr / eor / cmnOracles
test_encode_add_imm_thumb_expand_681pins0xFF / 0x100 / 0x104 / 0x200 / 0x3FC / 0x400 / 0xFFF(+ a non-scratch rd/rn permutation) bit-for-bit againstclang -target thumbv7m-none-eabi.test_encode_add_imm_large_350's0x123assertion upgraded from length-only (which let the mis-encoding pass CI, as the issue notes) to exact ADDW bytes.scripts/repro/addw_offset_681_differential.py, unicorn-vs-wasmtime, symtab-based per ci(vcr-oracle): CI-gate the RV32 immediate-shift-fold execution oracle (#472, #242) #489): dynamic base + static offsets{256,512,1020,1024,4092}over i32/i8/i16/i64 load+store,--safety-bounds noneandsoftware— including the bypass pin (in-bounds base, guard passes, pre-fix access escapes 2 GiB past base) and OOB trap-to-trap.clobberreturns 4660 instead of 111; offset-1024 access faultsUC_ERR_WRITE_UNMAPPED2 GiB away)frozen_codegen_bytes10/10,.textbit-identical (fixtures never hit the dynamic-base + big-offset shape).cargo fmt --checkclean,cargo clippy --workspace --all-targets -- -D warningsclean.🤖 Generated with Claude Code