Skip to content

AlignmentLowering: an out-of-bounds unaligned store becomes a partial write #9186

Description

@tamaroning

Summary

An unaligned i32.store align=1 is split into four i32.store8s. If the address is out of bounds, the first bytes are written and the later ones trap, so memory differs after the trap. In the spec, a store whose bytes do not all fit in the memory reduces to trap without changing the store (Step/store-num-oob), so a trapping store never writes anything.

Root cause

The byte stores are emitted in increasing address order. The last byte is the one that traps, and the earlier stores have already happened.

Affected passes

--alignment-lowering and --i64-to-i32-lowering. The latter writes the low word of an i64.store before the high word traps.

Reproducer

(module
 (memory 1 1)
 (func (export "store")
  (i32.store align=1 (i32.const 65534) (i32.const 0x01020304)))
 (func (export "peek") (result i32)
  (i32.load (i32.const 65532))))
$ wasm-opt in.wat --alignment-lowering --print
 (func $0
  (local $0 i32)
  (local $1 i32)
  (local.set $0 (i32.const 65534))
  (local.set $1 (i32.const 16909060))
  (i32.store8 (local.get $0) (local.get $1))
  (i32.store8 offset=1 (local.get $0) (i32.shr_u (local.get $1) (i32.const 8)))
  (i32.store8 offset=2 (local.get $0) (i32.shr_u (local.get $1) (i32.const 16)))
  (i32.store8 offset=3 (local.get $0) (i32.shr_u (local.get $1) (i32.const 24)))
 )

The 4-byte store at 65534 in a 1-page memory is out of bounds, so it must trap without writing. peek reads the last word afterwards:

$ wasm-opt in.wat --alignment-lowering --fuzz-exec -o /dev/null
[fuzz-exec] export store
[trap highest > memory: 65534 > 65532]
[fuzz-exec] export peek
[fuzz-exec] note result: peek => 0
[fuzz-exec] export store
[trap highest > memory: 65536 > 65535]
[fuzz-exec] export peek
[fuzz-exec] note result: peek => 50593792
[fuzz-exec] comparing peek
values not identical! 50593792 != 0
[fuzz-exec] optimization passes changed results

Expected vs actual

The original traps with memory unchanged. After --alignment-lowering the first two byte stores succeed and the third traps; the last word becomes 50593792 (0x03040000).

Version

Reproduced on upstream main at 4d8ac549e2ab9b283246ea95e79ebe139ca579ac (wasm-opt version 133).

AI was used as part of the process of finding this issue. I have manually checked and reproduced it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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