diff --git a/src/singlepass/x64/codegen.h b/src/singlepass/x64/codegen.h index 89e415d0b..c2e4eaa6d 100644 --- a/src/singlepass/x64/codegen.h +++ b/src/singlepass/x64/codegen.h @@ -935,19 +935,15 @@ class X64OnePassCodeGenImpl X64Reg::getRegRef(BaseReg), 0, Offset, getWASMTypeSize()); -#ifdef ZEN_ENABLE_CPU_EXCEPTION - if (!Base.isImm() && (Offset >= INT32_MAX)) { - // when offset >= INT32_MAX, then will cause inst like mov edi, dword - // ptr[r13+edi-1]. + if (!Base.isImm() && (Offset >= (uint32_t)INT32_MAX)) { auto MemAddrReg = Layout.getScopedTemp(); _ mov(X64Reg::getRegRef(MemAddrReg), Offset); _ add(X64Reg::getRegRef(MemAddrReg), X64Reg::getRegRef(BaseReg)); _ add(X64Reg::getRegRef(MemAddrReg), ABI.getMemoryBaseReg()); Addr = asmjit::x86::Mem(X64Reg::getRegRef(MemAddrReg), 0, - getWASMTypeSize(SrcType)); + getWASMTypeSize()); } -#endif // ZEN_ENABLE_CPU_EXCEPTION LoadOperatorImpl::emit( ASM, X64Reg::getRegRef(ValReg), Addr); @@ -1014,8 +1010,6 @@ class X64OnePassCodeGenImpl ZEN_ABORT(); } - // Addr = memoryBase + (in64) offset, so when offset < 0, - // the result i32 Addr works like add (2**32 + offset) asmjit::x86::Mem Addr = Base.isImm() ? asmjit::x86::Mem(ABI.getMemoryBaseReg(), Offset, getWASMTypeSize()) @@ -1023,6 +1017,16 @@ class X64OnePassCodeGenImpl X64Reg::getRegRef(RegNum), 0, Offset, getWASMTypeSize()); + if (!Base.isImm() && (Offset >= (uint32_t)INT32_MAX)) { + auto MemAddrReg = Layout.getScopedTemp(); + _ mov(X64Reg::getRegRef(MemAddrReg), Offset); + _ add(X64Reg::getRegRef(MemAddrReg), + X64Reg::getRegRef(RegNum)); + _ add(X64Reg::getRegRef(MemAddrReg), ABI.getMemoryBaseReg()); + Addr = asmjit::x86::Mem(X64Reg::getRegRef(MemAddrReg), 0, + getWASMTypeSize()); + } + mov(Addr, Value); } diff --git a/tests/wast/spec_extra/store_large_offset_false_positive_oob.wast b/tests/wast/spec_extra/store_large_offset_false_positive_oob.wast new file mode 100644 index 000000000..b6398414b --- /dev/null +++ b/tests/wast/spec_extra/store_large_offset_false_positive_oob.wast @@ -0,0 +1,44 @@ +;; Test: f64.store with offset >= INT32_MAX on large memory should NOT trap. +;; +;; When offset >= 0x80000000, x86-64 disp32 sign-extends to a negative 64-bit +;; value, causing the effective address to go before MemBase. The JIT must +;; compute the full 64-bit address explicitly to avoid this. +;; +;; Effective address = memory.size(65131) + offset(4268353288) = 4268418419 +;; Memory size = 65131 * 65536 = 4268425216 +;; 4268418419 + 8 <= 4268425216 => in-bounds, should NOT trap. + +(module + (memory (;0;) 65131) + + ;; dynamic base (memory.size result) + large offset: in-bounds store + (func (export "f64_store_dynamic_base_large_offset") + memory.size + f64.const -5.44203 + f64.store offset=4268353288) + + ;; dynamic base via parameter + large offset: in-bounds store + (func (export "f64_store_param_base_large_offset") (param i32) + local.get 0 + f64.const 1.0 + f64.store offset=4268353288) + + ;; dynamic base (memory.size result) + large offset: in-bounds load + (func (export "f64_load_dynamic_base_large_offset") (result f64) + memory.size + f64.load offset=4268353288) + + ;; large offset store that IS out-of-bounds must still trap + (func (export "f64_store_large_offset_oob") (param i32) + local.get 0 + f64.const 0.0 + f64.store offset=4294967295) +) + +;; These should all succeed without trapping +(assert_return (invoke "f64_store_dynamic_base_large_offset")) +(assert_return (invoke "f64_store_param_base_large_offset" (i32.const 65131))) +(assert_return (invoke "f64_load_dynamic_base_large_offset") (f64.const 1.0)) + +;; Large offset that actually exceeds memory should still trap +(assert_trap (invoke "f64_store_large_offset_oob" (i32.const 65131)) "out of bounds memory access")