Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,87 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --expose-memory-corruption-api
const kHeapObjectTag = 0x1;
assertSame(typeof Sandbox.base, 'number');
assertSame(typeof Sandbox.byteLength, 'number');
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let obj = {a: 42};
//
// Sandbox.isWritable
//
assertTrue(Sandbox.isWritable(obj));
// Here we assume that certain builtin objects (e.g. the 'undefined' value) are
// located in read-only space.
assertFalse(Sandbox.isWritable(undefined));
//
// Sandbox.getAddressOf and Sandbox.getObjectAt
//
let addr = Sandbox.getAddressOf(obj);
assertSame(typeof addr, 'number');
let sameObj = Sandbox.getObjectAt(addr);
assertSame(obj, sameObj);
//
// Sandbox reading + writing
//
// Here we assume that .a will be an inline property at offset 12. We could
// also use other types of objects (e.g. inline TypedArrays) or so if that
// every changes.
const kOffsetOfPropertyA = 12;
let currentValue = memory.getUint32(addr + kOffsetOfPropertyA, true);
assertEquals(currentValue, 42 << 1);
let newValue = 43 << 1;
memory.setUint32(addr + kOffsetOfPropertyA, newValue, true);
assertEquals(obj.a, 43);
//
// Sandbox.getSizeOf, Sandbox.getInstanceTypeOf, and Sandbox.getInstanceTypeIdOf
//
let size = Sandbox.getSizeOf(obj);
// We don't want to rely on the specific size here, but it should be reasonable.
assertTrue(size > 4 && size < 64);
let instanceType = Sandbox.getInstanceTypeOf(obj);
assertSame(typeof instanceType, 'string');
assertEquals(instanceType, 'JS_OBJECT_TYPE');
let instanceTypeId = Sandbox.getInstanceTypeIdOf(obj);
assertSame(typeof instanceTypeId, 'number');
assertEquals(instanceTypeId, Sandbox.getInstanceTypeIdFor('JS_OBJECT_TYPE'));
//
// Sandbox.isValidObjectAt
//
addr = Sandbox.getAddressOf(obj);
assertTrue(Sandbox.isValidObjectAt(addr));
assertFalse(Sandbox.isValidObjectAt(0x41414141));
// Check that internal objects are recognized by isValidObjectAt.
// Here we assume that the first three fields are pointer fields.
assertTrue(Sandbox.getSizeOf(obj) >= 12);
for (let i = 0; i < 3; i++) {
let internalObjectAddr = memory.getUint32(addr + i*4, true);
assertEquals(internalObjectAddr & kHeapObjectTag, kHeapObjectTag)
assertTrue(Sandbox.isValidObjectAt(internalObjectAddr));
}
// Check that various objects are recognized by isValidObjectAt.
for (let prop of Object.getOwnPropertyNames(this)) {
let addr = Sandbox.getAddressOf(this[prop]);
assertTrue(Sandbox.isValidObjectAt(addr));
}
// Check that random addresses don't cause crashes in isValidObjectAt.
for (let addr = 0; addr <= 0x1000; addr++) {
Sandbox.isValidObjectAt(addr);
}

View File

@ -0,0 +1,35 @@
// Copyright 2025 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing
let sbx_memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let len = 0x20000;
let ar = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * len));
function corruptInBackground(address, valueA, valueB) {
function workerTemplate(address, valueA, valueB) {
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
while (true) {
memory.setUint8(address, valueA, true);
memory.setUint8(address, valueB, true);
}
}
const workerCode = new Function(
`(${workerTemplate})(${address}, ${valueA}, ${valueB})`);
return new Worker(workerCode, { type: 'function' });
}
let ar_addr = Sandbox.getAddressOf(ar);
let ar_map = sbx_memory.getUint32(ar_addr, true);
let bit_field2_addr = ar_map + 10;
let bit_field2_val = sbx_memory.getUint8(bit_field2_addr);
let worker = corruptInBackground(
bit_field2_addr, bit_field2_val, bit_field2_val ^ 0xff);
for (let i = 0; i < 10000; ++i) {
ar.sort();
}

View File

@ -0,0 +1,96 @@
// Copyright 2025 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing
// Prepare corruption utilities.
const kHeapObjectTag = 1;
const kJSArrayBufferBackingStoreOffset = 0x24;
const kSandboxSizeLog2 = 40;
const kSandboxedPointerShift = 64 - kSandboxSizeLog2;
const kHeapNumberValueOffset = 4;
const kJSArrayLengthOffset = 0xc;
const kMapOffset = 0;
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function getPtr(obj) {
return Sandbox.getAddressOf(obj) + kHeapObjectTag;
}
function getObj(ptr) {
return Sandbox.getObjectAt(ptr);
}
function getField(obj, offset) {
return memory.getUint32(obj + offset - kHeapObjectTag, true);
}
function getField64(obj, offset) {
return memory.getBigUint64(obj + offset - kHeapObjectTag, true);
}
function setField(obj, offset, value) {
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
}
function abptr(ab) {
let ab_ofs = getField64(getPtr(ab),
kJSArrayBufferBackingStoreOffset) >> BigInt(kSandboxedPointerShift);
let base = BigInt(Sandbox.base) + ab_ofs;
return base;
}
let MAX_TRIES = 0x5000;
let TARGET = 0x424242424240n;
let VALUE = 0x434343434343n;
let buffer = new WebAssembly.Memory(
{initial: 0x20, maximum: 0x20, shared: true}).buffer;
let u64s = new BigUint64Array(buffer);
u64s[0] = VALUE;
let u64a = new BigUint64Array(0x100000);
let arr = [];
let kBigUint64ArrayMap = getField(getPtr(u64s), kMapOffset);
let kJSArrayMap = getField(getPtr(arr), kMapOffset);
// Make u64s a "polymorphic" object of BigUint64Array & JSArray.
let ofs_idx_bi = (TARGET - abptr(u64a.buffer)) / 8n;
let ofs_idx = Number(ofs_idx_bi);
if (BigInt(ofs_idx) != ofs_idx_bi) {
console.log(`[!] precision loss`);
}
// Set fake length field.
let fake_len = Number((-ofs_idx_bi & ((1n<<64n) - 1n)) + 0x10000n);
setField(getPtr(u64s), kJSArrayLengthOffset, getPtr(fake_len));
// Set fake JSArray map.
setField(getPtr(u64s), kMapOffset, kJSArrayMap);
let workerScript = `
// Prepare corruption utilities.
const kHeapObjectTag = 1;
const kMapOffset = 0;
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function setField(obj, offset, value) {
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
}
let u64s_ptr = ${getPtr(u64s)};
let kBigUint64ArrayMap = ${kBigUint64ArrayMap};
let kJSArrayMap = ${kJSArrayMap};
while (true) {
for (let i = 0; i < 0x10; i++) {
setField(u64s_ptr, 0, kBigUint64ArrayMap);
}
setField(u64s_ptr, 0, kBigUint64ArrayMap);
// Only make it a JSArray for TypedArrayPrototypeSetArray.
setField(u64s_ptr, 0, kJSArrayMap);
setField(u64s_ptr, 0, kBigUint64ArrayMap);
for (let i = 0; i < 0x10; i++) {
setField(u64s_ptr, 0, kBigUint64ArrayMap);
}
}
`;
let worker = new Worker(workerScript, {type: 'string'});
for (let i = 0; i < 1000000; i++);
for (let i = 0; i < MAX_TRIES; i++) {
try {
u64a.set(u64s, ofs_idx);
} catch { }
}

View File

@ -0,0 +1,28 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --expose-gc --sandbox-testing
const kJSArrayType = Sandbox.getInstanceTypeIdFor("JS_ARRAY_TYPE");
const kJSArrayLengthOffset = Sandbox.getFieldOffset(kJSArrayType, "length");
const kMaxRegularHeapObjectSize = 131072;
const memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
// Allocate an array and promote it to the old generation.
const array = Array();
gc();
// Allocate a value in the young generation.
const value = new Number();
// Corrupt the length to still look like a regular heap object.
memory.setUint32(
Sandbox.getAddressOf(array) + kJSArrayLengthOffset,
kMaxRegularHeapObjectSize,
true);
// OOB write to the JS array also triggering the write barrier for an old->new
// write with an offset that's too large for the remembered set. This should not
// crash with OOB in malloc'ed memory.
array[array.length - 1] = value;

View File

@ -0,0 +1,71 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --wasm-staging --sandbox-testing
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
const builder = new WasmModuleBuilder();
builder.exportMemoryAs("mem0", 0);
let $mem0 = builder.addMemory(1, 1);
let $box = builder.addStruct([makeField(kWasmFuncRef, true)]);
let $sig_i_l = builder.addType(kSig_i_l);
builder.addFunction("func0", kSig_i_i).exportFunc().addBody([
kExprLocalGet, 0,
...wasmI32Const(0x41414141),
kExprI32StoreMem, 0, 0,
kExprI32Const, 0,
]);
builder.addFunction("func1", $sig_i_l).exportFunc().addBody([
kExprLocalGet, 0,
kExprI32ConvertI64,
]);
builder.addFunction("get_func0", kSig_r_v).exportFunc().addBody([
kExprRefFunc, 0,
kGCPrefix, kExprStructNew, $box,
kGCPrefix, kExprExternConvertAny,
]);
builder.addFunction("get_func1", kSig_r_v).exportFunc().addBody([
kExprRefFunc, 1,
kGCPrefix, kExprStructNew, $box,
kGCPrefix, kExprExternConvertAny,
]);
builder.addFunction("boom", kSig_i_l).exportFunc().addBody([
kExprLocalGet, 0,
kExprRefFunc, 1,
kExprCallRef, $sig_i_l,
])
let instance = builder.instantiate();
instance.exports.func0(0);
const kHeapObjectTag = 1;
const kStructField0Offset = 8; // 0:map, 4:hash
const kWasmFuncRefType = Sandbox.getInstanceTypeIdFor('WASM_FUNC_REF_TYPE');
const kWasmFuncRefInternalOffset = Sandbox.getFieldOffset(kWasmFuncRefType, 'trusted_internal');
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function getPtr(obj) {
return Sandbox.getAddressOf(obj) + kHeapObjectTag;
}
function getField(obj, offset) {
return memory.getUint32(obj + offset - kHeapObjectTag, true);
}
function setField(obj, offset, value) {
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
}
let f0_box = getPtr(instance.exports.get_func0());
let f0 = getField(f0_box, kStructField0Offset);
let f0_int = getField(f0, kWasmFuncRefInternalOffset);
let f1_box = getPtr(instance.exports.get_func1());
let f1 = getField(f1_box, kStructField0Offset);
setField(f1, kWasmFuncRefInternalOffset, f0_int);
assertTraps(kTrapMemOutOfBounds, () => instance.exports.boom(0x414141414141n));

View File

@ -0,0 +1,33 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing
const kJSFunctionType = Sandbox.getInstanceTypeIdFor('JS_FUNCTION_TYPE');
const kJSFunctionDispatchHandleOffset = Sandbox.getFieldOffset(kJSFunctionType, 'dispatch_handle');
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let params = [];
for (let i = 1; i <= 10000; i++) {
params.push(`a${i}`);
}
let body = 'return 42;';
let f1 = new Function(...params, body);
f1();
let f2 = new Function('a', 'b', 'c', 'return 43;');
f2();
// Transplant the dispatch handle from one function to another.
let f1_addr = Sandbox.getAddressOf(f1);
let f2_addr = Sandbox.getAddressOf(f2);
let dispatch_handle1 = memory.getUint32(f1_addr + kJSFunctionDispatchHandleOffset, true);
let dispatch_handle2 = memory.getUint32(f2_addr + kJSFunctionDispatchHandleOffset, true);
memory.setUint32(f2_addr + kJSFunctionDispatchHandleOffset, dispatch_handle1, true);
memory.setUint32(f1_addr + kJSFunctionDispatchHandleOffset, dispatch_handle2, true);
f1();
f2();
// Should either crash safely or succeed.

View File

@ -0,0 +1,40 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --sandbox-testing
const kJSFunctionType = Sandbox.getInstanceTypeIdFor('JS_FUNCTION_TYPE');
const kJSFunctionDispatchHandleOffset = Sandbox.getFieldOffset(kJSFunctionType, 'dispatch_handle');
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let params = [];
for (let i = 1; i <= 10000; i++) {
params.push(`a${i}`);
}
let body = 'return 42;';
let f1 = new Function(...params, body);
%PrepareFunctionForOptimization(f1);
f1();
%OptimizeFunctionOnNextCall(f1);
f1();
let f2 = new Function('a', 'b', 'c', 'return 43;');
%PrepareFunctionForOptimization(f2);
f2();
%OptimizeFunctionOnNextCall(f2);
f2();
// Transplant the dispatch handle from one function to another.
// This should simply result in the execution of different (but valid) code.
assertEquals(f1(), 42);
assertEquals(f2(), 43);
let f1_addr = Sandbox.getAddressOf(f1);
let f2_addr = Sandbox.getAddressOf(f2);
let dispatch_handle1 = memory.getUint32(f1_addr + kJSFunctionDispatchHandleOffset, true);
let dispatch_handle2 = memory.getUint32(f2_addr + kJSFunctionDispatchHandleOffset, true);
memory.setUint32(f2_addr + kJSFunctionDispatchHandleOffset, dispatch_handle1, true);
memory.setUint32(f1_addr + kJSFunctionDispatchHandleOffset, dispatch_handle2, true);
assertEquals(f1(), 43);
assertEquals(f2(), 42);

View File

@ -0,0 +1,56 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --sandbox-testing --no-maglev-inlining --no-turbo-inlining
const kJSFunctionType = Sandbox.getInstanceTypeIdFor('JS_FUNCTION_TYPE');
const kJSFunctionDispatchHandleOffset = Sandbox.getFieldOffset(kJSFunctionType, 'dispatch_handle');
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let params = [];
for (let i = 1; i <= 10000; i++) {
params.push(`a${i}`);
}
let body = 'return 42;';
let f1 = new Function(...params, body);
%PrepareFunctionForOptimization(f1);
f1();
%OptimizeFunctionOnNextCall(f1);
f1();
let f2 = new Function('a', 'b', 'c', 'return 43;');
%PrepareFunctionForOptimization(f2);
f2();
%OptimizeFunctionOnNextCall(f2);
f2();
// Inlining needs to be disabled here so the JIT compiler emits calls to the
// (known) functions instead of inlining them.
function caller1() {
f1();
}
function caller2() {
f2();
}
%PrepareFunctionForOptimization(caller1);
%PrepareFunctionForOptimization(caller2);
caller1();
caller2();
%OptimizeFunctionOnNextCall(caller1);
%OptimizeFunctionOnNextCall(caller2);
caller1();
caller2();
// Swap the dispatch handles of the two functions.
let f1_addr = Sandbox.getAddressOf(f1);
let f2_addr = Sandbox.getAddressOf(f2);
let dispatch_handle1 = memory.getUint32(f1_addr + kJSFunctionDispatchHandleOffset, true);
let dispatch_handle2 = memory.getUint32(f2_addr + kJSFunctionDispatchHandleOffset, true);
memory.setUint32(f2_addr + kJSFunctionDispatchHandleOffset, dispatch_handle1, true);
memory.setUint32(f1_addr + kJSFunctionDispatchHandleOffset, dispatch_handle2, true);
caller1();
caller2();
// Should either crash safely or succeed.

View File

@ -0,0 +1,36 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing
const kJSFunctionType = Sandbox.getInstanceTypeIdFor('JS_FUNCTION_TYPE');
const kSharedFunctionInfoType = Sandbox.getInstanceTypeIdFor('SHARED_FUNCTION_INFO_TYPE');
const kJSFunctionSFIOffset = Sandbox.getFieldOffset(kJSFunctionType, 'shared_function_info');
const kSharedFunctionInfoLengthOffset = Sandbox.getFieldOffset(kSharedFunctionInfoType, 'length');
const kSharedFunctionInfoFormalParameterCountOffset = Sandbox.getFieldOffset(kSharedFunctionInfoType, 'formal_parameter_count');
const kHeapObjectTag = 1;
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
const builder = new WasmModuleBuilder();
builder.exportMemoryAs("mem0", 0);
let $mem0 = builder.addMemory(1, 1);
let $sig_i_l = builder.addType(kSig_v_i);
builder.addFunction("func", builder.addType(kSig_l_l)).exportFunc().addBody([
kExprLocalGet, 0,
]);
let instance = builder.instantiate();
instance.exports.func(0n);
let func_addr = Sandbox.getAddressOf(instance.exports.func);
let sfi_addr = memory.getUint32(func_addr + kJSFunctionSFIOffset, true) - kHeapObjectTag;
memory.setUint16(sfi_addr + kSharedFunctionInfoLengthOffset, 0xffff, true);
memory.setUint16(sfi_addr + kSharedFunctionInfoFormalParameterCountOffset, 0xffff, true);
instance.exports.func(0x4141n);

View File

@ -0,0 +1,71 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
let builder = new WasmModuleBuilder();
let $struct = builder.addStruct([makeField(kWasmI64, true)]);
let $sig_v_ls = builder.addType(makeSig([kWasmI64, wasmRefType($struct)], []));
let $sig_v_ll = builder.addType(makeSig([kWasmI64, kWasmI64], []));
let $writer = builder.addFunction("writer", $sig_v_ls)
.exportFunc()
.addBody([
kExprLocalGet, 1,
kExprLocalGet, 0,
kGCPrefix, kExprStructSet, $struct, 0,
]);
let $boom = builder.addFunction("boom", $sig_v_ll)
.exportFunc()
.addBody([
kExprLocalGet, 1,
kExprLocalGet, 0,
kExprI32Const, 0,
kExprCallIndirect, $sig_v_ll, 0,
])
let $dummy = builder.addFunction("dummy", $sig_v_ll).exportFunc().addBody([]);
// Target table.
let $t0 = builder
.addTable(wasmRefType($sig_v_ll), 1, 1, [kExprRefFunc, $dummy.index])
.exportAs('table_v_ll');
// Padding tables for alignment.
let $td0 = builder.addTable(
wasmRefType($sig_v_ll), 1, 1, [kExprRefFunc, $dummy.index]);
let $td1 = builder.addTable(
wasmRefType($sig_v_ll), 1, 1, [kExprRefFunc, $dummy.index]);
let $td2 = builder.addTable(
wasmRefType($sig_v_ll), 1, 1, [kExprRefFunc, $dummy.index]);
// OOB table.
let $t1 = builder
.addTable(wasmRefType($sig_v_ls), 1, 1, [kExprRefFunc, $writer.index])
.exportAs("table_v_ls");
let instance = builder.instantiate();
let { writer, dummy, boom, table_v_ls, table_v_ll } = instance.exports;
// Prepare corruption utilities.
const kHeapObjectTag = 1;
const kWasmTableType = Sandbox.getInstanceTypeIdFor('WASM_TABLE_OBJECT_TYPE');
const kWasmTableObjectCurrentLengthOffset = Sandbox.getFieldOffset(kWasmTableType, 'current_length');
const kWasmTableObjectMaximumLengthOffset = Sandbox.getFieldOffset(kWasmTableType, 'maximum_length');
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function getPtr(obj) {
return Sandbox.getAddressOf(obj) + kHeapObjectTag;
}
function getField(obj, offset) {
return memory.getUint32(obj + offset - kHeapObjectTag, true);
}
function setField(obj, offset, value) {
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
}
const kSmiMinusOne = 0xfffffffe;
setField(getPtr(table_v_ls), kWasmTableObjectCurrentLengthOffset, kSmiMinusOne);
setField(getPtr(table_v_ls), kWasmTableObjectMaximumLengthOffset, kSmiMinusOne);
// Check bypassed, write @ index -7 -> writes into table_v_ll dispatch table!
table_v_ls.set(0xfffffff9, writer);
boom(0x414141414141n, 42n);

View File

@ -0,0 +1,75 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --sandbox-testing
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
// Prepare corruption utilities.
const kHeapObjectTag = 1;
const kSmiTagSize = 1;
const kKindBits = 5;
const kI64 = 2;
const kRef = 0xa;
const kWasmTagObjectSerializedSignatureOffset = 0xc;
const cage_base = BigInt(Sandbox.base);
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function getPtr(obj) {
return Sandbox.getAddressOf(obj) + kHeapObjectTag;
}
function getField(obj, offset) {
return memory.getUint32(obj + offset - kHeapObjectTag, true);
}
function setField(obj, offset, value) {
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
}
// find ByteArrayMap dynamically
let dummy_tag = new WebAssembly.Tag({parameters: ['i64'], returns: []});
let dummy_tag_ptr = getPtr(dummy_tag);
let dummy_sig_ptr = getField(dummy_tag_ptr, kWasmTagObjectSerializedSignatureOffset);
let ByteArrayMap = getField(dummy_sig_ptr, 0);
function fn(ptr, val) {
return [val, ptr];
}
let builder = new WasmModuleBuilder();
let $struct = builder.addStruct([makeField(kWasmI64, true)]);
let $sig_sl_ll = builder.addType(makeSig([kWasmI64, kWasmI64], [wasmRefType($struct), kWasmI64]));
let $sig_v_ll = builder.addType(makeSig([kWasmI64, kWasmI64], []));
let $fn = builder.addImport('import', 'fn', $sig_sl_ll);
builder.addFunction('boom', $sig_v_ll)
.exportFunc()
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprCallFunction, $fn,
kGCPrefix, kExprStructSet, $struct, 0,
]);
let instance = builder.instantiate({import: {fn}});
let { boom } = instance.exports;
function findObject(needle, start) {
function match() {
for (let k = 0; k < needle.length; ++k) {
if (getField(start, k * 4) != needle[k]) return false;
}
return true;
}
while (!match()) start += 4;
return start;
}
let needle = [
/*map=*/ByteArrayMap, /*PodArrayBase::length=*/(5 * 4) << kSmiTagSize, /*ReturnCount=*/2, kRef | ($struct << kKindBits),
kI64, kI64, kI64,
];
// Find the serialized signature, starting at the dummy Tag object (allocated
// before the serialized sig).
let serialized_sig_ptr = findObject(needle, dummy_tag_ptr);
setField(serialized_sig_ptr, 0xc /* offset of ref */, kI64);
boom(0x414141414141n, 0x42n);

View File

@ -0,0 +1,22 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --expose-gc --expose-externalize-string --sandbox-testing
const kSeqStringType = Sandbox.getInstanceTypeIdFor("SEQ_ONE_BYTE_STRING_TYPE");
const kStringLengthOffset = Sandbox.getFieldOffset(kSeqStringType, "length");
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let string = "foo" + "bar" + "baz";
assertEquals(Sandbox.getInstanceTypeIdOf(string), kSeqStringType);
let string_address = Sandbox.getAddressOf(string);
let orig_length = memory.getUint32(string_address + kStringLengthOffset, true);
assertEquals(orig_length, string.length);
let corrupted_length = Math.floor(Math.random() * 0x100000000);
memory.setUint32(string_address + kStringLengthOffset, corrupted_length, true);
// Externalization is one way to trigger a WriteToFlat on an external buffer.
externalizeString(string);

View File

@ -0,0 +1,30 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --expose-gc --expose-externalize-string --sandbox-testing
const kConsStringType =
Sandbox.getInstanceTypeIdFor("CONS_ONE_BYTE_STRING_TYPE");
const kStringLengthOffset = Sandbox.getFieldOffset(kConsStringType, "length");
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let first = "first long string to make cons string";
let second = "second long string to make cons string";
const string = first + second;
assertEquals(Sandbox.getInstanceTypeIdOf(string), kConsStringType);
// String must be in old space for externalization.
assertThrows(() => externalizeString(string));
gc();
gc();
let string_address = Sandbox.getAddressOf(string);
let orig_length = memory.getUint32(string_address + kStringLengthOffset, true);
assertEquals(orig_length, string.length);
let corrupted_length = Math.floor(Math.random() * 0x100000000);
memory.setUint32(string_address + kStringLengthOffset, corrupted_length, true);
// Externalization is one way to trigger a WriteToFlat on an external buffer.
externalizeString(string);

View File

@ -0,0 +1,52 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --expose-gc --expose-externalize-string --sandbox-testing
const kInternalizedStringType =
Sandbox.getInstanceTypeIdFor("INTERNALIZED_ONE_BYTE_STRING_TYPE");
const kConsStringType =
Sandbox.getInstanceTypeIdFor("CONS_ONE_BYTE_STRING_TYPE");
const kStringLengthOffset =
Sandbox.getFieldOffset(kInternalizedStringType, "length");
const kConsStringFirstOffset =
Sandbox.getFieldOffset(kConsStringType, "first");
const kConsStringSecondOffset =
Sandbox.getFieldOffset(kConsStringType, "second");
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let first = "first long string to make cons string";
assertEquals(Sandbox.getInstanceTypeIdOf(first), kInternalizedStringType);
let second = "second long string to make cons string";
assertEquals(Sandbox.getInstanceTypeIdOf(second), kInternalizedStringType);
const string = first + second;
assertEquals(Sandbox.getInstanceTypeIdOf(string), kConsStringType);
// String must be in old space for externalization.
assertThrows(() => externalizeString(string));
gc();
gc();
let string_address = Sandbox.getAddressOf(string);
// Corrupt the first child's length.
let first_address =
memory.getUint32(string_address + kConsStringFirstOffset, true) - 1;
assertEquals(first_address, Sandbox.getAddressOf(first));
let orig_length = memory.getUint32(first_address + kStringLengthOffset, true);
let corrupted_length = Math.floor(Math.random() * 0x100000000);
assertEquals(orig_length, first.length);
memory.setUint32(first_address + kStringLengthOffset, corrupted_length, true);
// Corrupt the second child's length.
let second_address =
memory.getUint32(string_address + kConsStringSecondOffset, true) - 1;
assertEquals(second_address, Sandbox.getAddressOf(second));
orig_length = memory.getUint32(second_address + kStringLengthOffset, true);
corrupted_length = Math.floor(Math.random() * 0x100000000);
assertEquals(orig_length, second.length);
memory.setUint32(second_address + kStringLengthOffset, corrupted_length, true);
// Externalization is one way to trigger a WriteToFlat on an external buffer.
externalizeString(string);

View File

@ -0,0 +1,43 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing --expose-gc --allow-natives-syntax
const kSeqStringType = Sandbox.getInstanceTypeIdFor("SEQ_ONE_BYTE_STRING_TYPE");
const kStringLengthOffset = Sandbox.getFieldOffset(kSeqStringType, "length");
// Helper function that spawns a worker thread that corrupts memory in the
// background, constantly flipping the given address between valueA and valueB.
function corruptInBackground(address, valueA, valueB) {
function workerTemplate(address, valueA, valueB) {
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
while (true) {
memory.setUint32(address, valueA, true);
memory.setUint32(address, valueB, true);
}
}
const workerCode = new Function(
`(${workerTemplate})(${address}, ${valueA}, ${valueB})`);
return new Worker(workerCode, {type: 'function'});
}
// Create the string object that we'll be corrupting.
let string = Array(0x100).fill("A").join("");
assertEquals(Sandbox.getInstanceTypeIdOf(string), kSeqStringType);
assertEquals(string.length, 0x100);
// Trigger some GCs to move the object to a stable position in memory.
gc();
gc();
// Start the worker thread to corrupt the string in the background.
let address = Sandbox.getAddressOf(string) + kStringLengthOffset;
let valueA = 0x1;
let valueB = 0x100;
let worker = corruptInBackground(address, valueA, valueB);
// Perform string Utf8 encoding in the foreground.
for (let i = 0; i < 1000; i++) {
%StringToCString(string);
}

View File

@ -0,0 +1,43 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing --expose-gc --allow-natives-syntax
const kSeqStringType = Sandbox.getInstanceTypeIdFor("SEQ_ONE_BYTE_STRING_TYPE");
const kStringLengthOffset = Sandbox.getFieldOffset(kSeqStringType, "length");
// Helper function that spawns a worker thread that corrupts memory in the
// background, constantly flipping the given address between valueA and valueB.
function corruptInBackground(address, valueA, valueB) {
function workerTemplate(address, valueA, valueB) {
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
while (true) {
memory.setUint32(address, valueA, true);
memory.setUint32(address, valueB, true);
}
}
const workerCode = new Function(
`(${workerTemplate})(${address}, ${valueA}, ${valueB})`);
return new Worker(workerCode, {type: 'function'});
}
// Create the string object that we'll be corrupting.
let string = Array(0x100).fill("A").join("");
assertEquals(Sandbox.getInstanceTypeIdOf(string), kSeqStringType);
assertEquals(string.length, 0x100);
// Trigger some GCs to move the object to a stable position in memory.
gc();
gc();
// Start the worker thread to corrupt the string in the background.
let address = Sandbox.getAddressOf(string) + kStringLengthOffset;
let valueA = 0x1;
let valueB = 0x100;
let worker = corruptInBackground(address, valueA, valueB);
// Perform string Utf8 encoding in the foreground.
for (let i = 0; i < 1000; i++) {
%StringUtf8Value(string);
}

View File

@ -0,0 +1,37 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --sandbox-testing --expose-gc --allow-natives-syntax
const kJSFunctionType = Sandbox.getInstanceTypeIdFor('JS_FUNCTION_TYPE');
const kSharedFunctionInfoOffset =
Sandbox.getFieldOffset(kJSFunctionType, 'shared_function_info');
const kSharedFunctionInfoType =
Sandbox.getInstanceTypeIdFor('SHARED_FUNCTION_INFO_TYPE');
const kFormalParameterCountOffset =
Sandbox.getFieldOffset(kSharedFunctionInfoType, 'formal_parameter_count');
function foo(a, b) {
return foo.arguments;
}
%PrepareFunctionForOptimization(foo);
assertArrayEquals([1], foo(1));
assertArrayEquals([1, 2, 3], foo(1, 2, 3));
%OptimizeFunctionOnNextCall(foo);
assertArrayEquals([1], foo(1));
assertArrayEquals([1, 2, 3], foo(1, 2, 3));
// Corrupt foo->shared_function_info->formal_parameter_count.
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let foo_sfi = Sandbox.getObjectAt(memory.getUint32(
Sandbox.getAddressOf(foo) + kSharedFunctionInfoOffset, true));
let formal_parameter_count = memory.getUint16(
Sandbox.getAddressOf(foo_sfi) + kFormalParameterCountOffset, true);
assertEquals(formal_parameter_count, 3);
memory.setUint16(
Sandbox.getAddressOf(foo_sfi) + kFormalParameterCountOffset,
formal_parameter_count + 1, true);
assertArrayEquals([1, 2, 3], foo(1, 2, 3));

View File

@ -0,0 +1,36 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --sandbox-fuzzing --allow-natives-syntax
function corrupt(func) {
const kHeapObjectTagMask = 0x3;
const kJSFunctionType = Sandbox.getInstanceTypeIdFor('JS_FUNCTION_TYPE');
const kSharedFunctionInfoOffset =
Sandbox.getFieldOffset(kJSFunctionType, 'shared_function_info');
const kSharedFunctionInfoType =
Sandbox.getInstanceTypeIdFor('SHARED_FUNCTION_INFO_TYPE');
const kFormalParameterCountOffset =
Sandbox.getFieldOffset(kSharedFunctionInfoType, 'formal_parameter_count');
const memview = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let func_addr = Sandbox.getAddressOf(func);
let sfi_addr = memview.getUint32(func_addr + kSharedFunctionInfoOffset, true) & ~kHeapObjectTagMask;
memview.setUint16(sfi_addr + kFormalParameterCountOffset, 32, true);
}
function f0(acc, value) {
value.b = value;
}
corrupt(f0);
function f3() {
const v5 = Array(Array);
v5.reduce(f0, 0);
}
%PrepareFunctionForOptimization(f3);
%PrepareFunctionForOptimization(f0);
f3();
%OptimizeFunctionOnNextCall(f3);
f3();

View File

@ -0,0 +1,31 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --expose-gc --sandbox-testing
const kJSArrayType = Sandbox.getInstanceTypeIdFor("JS_ARRAY_TYPE");
const kJSArrayLengthOffset = Sandbox.getFieldOffset(kJSArrayType, "length");
const kJSArrayElementsOffset = Sandbox.getFieldOffset(kJSArrayType, "elements");
const memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
// Allocate an array and promote it to the old generation.
const array = Array();
gc();
// Allocate a value in the young generation.
const value = new Number();
// Corrupt the JSArray such that
// 1. It's length becomes very large, allowing OOB writes
// 2. It's backing FixedArray points to 0x0, in RO space
let array_addr = Sandbox.getAddressOf(array);
memory.setUint32(array_addr + kJSArrayLengthOffset, 0x7ffffffe, true);
memory.setUint32(array_addr + kJSArrayElementsOffset, 0x1, true);
// OOB write to the JS array such that the write happens on a writable page
// even though the host object (supposedly a FixedArray) lives in RO space.
assertFalse(Sandbox.isWritableObjectAt(0));
assertTrue(Sandbox.isWritableObjectAt(1024 * 1024));
let index = (1024 * 1024) / 4;
array[index] = value;

View File

@ -0,0 +1,93 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing --expose-gc
const kSeqStringType = Sandbox.getInstanceTypeIdFor("SEQ_TWO_BYTE_STRING_TYPE");
const kStringHashOffset = Sandbox.getFieldOffset(kSeqStringType, "hash");
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
// Will return random (but deterministic, if a --random-seed is used) numbers.
function randomIntUpTo(n) {
return Math.floor(Math.random() * n);
}
// Helper function that spawns a worker thread that corrupts memory in the
// background, constantly flipping the given address between two values.
function corruptInBackground(address, bitToFlip) {
function workerTemplate(address, bitToFlip) {
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let oldValue = memory.getUint32(address, true);
let newValue = oldValue ^ bitToFlip;
while (true) {
memory.setUint32(address, newValue, true);
memory.setUint32(address, oldValue, true);
}
}
const workerCode = new Function(
`(${workerTemplate})(${address}, ${bitToFlip})`);
return new Worker(workerCode, {type: 'function'});
}
// Some random code for the parser...
// Need some unicode in here to get a Utf16 string (otherwise, we'd get a
// buffered stream during parsing, which probably complicates things a bit).
function test() {
function log(msg) {}
function launchRockets(n, destination) {
class RocketLauncher {
constructor(type) {
this.rocketType = type ?? '🚀';
this.launchCount = 0;
}
launch(destination) {
log(`Launching ${this.rocketType} to ${destination}!`);
this.launchCount++;
}
}
if (typeof destination === 'undefined') {
destination = "the moon";
}
let launcher = new RocketLauncher;
for (let i = 0; i < n; i++) {
launcher.launch(destination);
}
}
launchRockets(3);
launchRockets(2, "mars");
launchRockets(1, "pluto");
}
// Create a SeqTwoByteString (otherwise we have a slice string)
let source = (test.toString() + "\ntest();").split('').join('');
assertEquals(Sandbox.getInstanceTypeIdOf(source), kSeqStringType);
// Trigger some GCs to move the object to a stable position in memory.
gc();
gc();
// Start the worker thread to corrupt the string in the background.
let size = Sandbox.getSizeOf(source);
assertTrue(size > source.length * 2);
let offset = randomIntUpTo(size);
let string_address = Sandbox.getAddressOf(source);
let bitToFlip = 1 << randomIntUpTo(32);
corruptInBackground(string_address + offset, bitToFlip);
for (let i = 0; i < 1000; i++) {
// Modify the hash in between every attempt to avoid code caching.
// Use + 0x4 here to not change the type bits (see HashFieldTypeBits).
// Alternatively, use --no-compilation-cache.
let currentHash = memory.getUint32(string_address + kStringHashOffset, true);
let newHash = currentHash + 0x4;
memory.setUint32(string_address + kStringHashOffset, newHash, true);
try { eval(source); } catch {}
}

View File

@ -0,0 +1,42 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing
const kTypedArrayType = Sandbox.getInstanceTypeIdFor("JS_TYPED_ARRAY_TYPE");
const kTypedArrayByteOffsetOffset =
Sandbox.getFieldOffset(kTypedArrayType, "byte_offset");
const kTypedArrayByteLengthOffset =
Sandbox.getFieldOffset(kTypedArrayType, "byte_length");
const kTypedArrayLengthOffset =
Sandbox.getFieldOffset(kTypedArrayType, "length");
const GB = 1024 * 1024 * 1024;
const kMaxInSandboxBufferSize = 32*GB - 1;
// Something reasonable, must be smaller than the maximum module size.
const kBufferSize = 1 * GB;
// When stored on-heap, these offsets and sizes are left shifted to guarantee
// that they are always smaller than the maximum buffer size.
const kBoundedSizeShift = 29;
const kShiftedBufferSize = BigInt(kBufferSize) << BigInt(kBoundedSizeShift);
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let array = new Uint8Array(new ArrayBuffer(0));
let array_address = Sandbox.getAddressOf(array);
let byte_offset_address = array_address + kTypedArrayByteOffsetOffset;
memory.setBigUint64(byte_offset_address, 0xffffffffffffffffn, true);
let byte_length_offset_address = array_address + kTypedArrayByteLengthOffset;
memory.setBigUint64(byte_length_offset_address, kShiftedBufferSize, true);
let length_offset_address = array_address + kTypedArrayLengthOffset;
memory.setBigUint64(length_offset_address, kShiftedBufferSize, true);
assertEquals(array.byteOffset, kMaxInSandboxBufferSize);
assertEquals(array.byteLength, kBufferSize);
// WebAssembly.Validate (and similar APIs) will access the TypedArray's data by
// fetching the Data() of the associated ArrayBuffer's BackingStore, then
// adding the ByteOffset(). The Data() of the BackingStore must never be
// nullptr, otherwise we'd end up accessing out-of-sandbox memory.
WebAssembly.validate(array);

View File

@ -0,0 +1,34 @@
// Copyright 2025 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing --allow-natives-syntax
// Radix value doesn't matter for this test but must be valid.
const radix = 3;
// Test toString conversion with a valid value.
%DoubleToStringWithRadix(12.34, radix);
// Test toString conversion with invalid/special values. These should not cause
// any memory corruption but may cause program termination, so we shuffle them.
let specialValues = [0.0, -0.0, NaN, Infinity, -Infinity];
shuffleArray(specialValues);
for (let value of specialValues) {
%DoubleToStringWithRadix(value, radix);
}
// Finally, also check that invalid radix values don't cause memory corruption.
%DoubleToStringWithRadix(12.34, 0);
%DoubleToStringWithRadix(12.34, 1);
%DoubleToStringWithRadix(12.34, 2);
%DoubleToStringWithRadix(12.34, 50);
%DoubleToStringWithRadix(12.34, 100);
function shuffleArray(array) {
for (let i = array.length - 1; i >= 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}

View File

@ -0,0 +1,72 @@
// Copyright 2025 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing --expose-gc
function workerFunc() {
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let iteration = 0;
let address;
let valueA = 0;
let valueB = 255;
onmessage = function(e) {
address = e.data.addr;
setTimeout(doWork);
}
function doWork() {
if (iteration == 0) postMessage({running: true});
for (let i = 0; i < 21; i++) {
iteration++;
let value = iteration % 2 == 0 ? valueA : valueB;
memory.setUint8(address, value);
}
setTimeout(doWork);
}
}
let memory_corruption_worker = new Worker(workerFunc, {type: 'function'});
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function DigitCount(bigint) {
let addr = Sandbox.getAddressOf(bigint);
return memory.getUint32(addr + 4, true) >> 1;
}
// Matches ToStringFormatter's "chunk_divisor_".
let divisor = 10n ** 19n;
// For future reference, these are interesting values that trigger different
// specific ASan reports:
//let kTargetDigits = 127;
//let kTargetDigits = 505;
//let kTargetDigits = 1010;
//let kTargetDigits = 2020;
let kTargetDigits = 4040;
// Find one of the divisors that the to-string algorithm would use
// internally, then construct a BigInt that has the same number of
// 64-bit digits, but only sets the lowest bit in its topmost digit.
// The worker will then keep toggling the highest bits in that digit.
while (DigitCount(divisor) < kTargetDigits) divisor *= divisor;
let digits = DigitCount(divisor);
let bits = (digits - 1) * 64;
let bigint = 1n << BigInt(bits);
if (DigitCount(bigint) !== digits) throw new Error("digit count is off");
// Promote the BigInt so it won't move later.
gc(); gc();
let addr = Sandbox.getAddressOf(bigint);
// *8: bytes per digit
// +8: BigInt header size
// -1: get the last in-bounds byte
let top_byte_offset = digits * 8 + 8 - 1;
memory_corruption_worker.onmessage = function(e) {
// Run a couple of times to give the worker a chance to win the race.
for (let i = 0; i < 20; i++) {
console.log(("" + bigint).length);
}
quit();
}
memory_corruption_worker.postMessage({ addr: addr + top_byte_offset });

View File

@ -0,0 +1,33 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing
const kTypedArrayType = Sandbox.getInstanceTypeIdFor("JS_TYPED_ARRAY_TYPE");
// In the future, if we remove the raw_length field (and instead use
// raw_byte_length), we should also just change this test to corrupt
// raw_byte_length instead.
const kTypedArrayLengthOffset =
Sandbox.getFieldOffset(kTypedArrayType, "length");
const kTypedArrayExternalPointerOffset =
Sandbox.getFieldOffset(kTypedArrayType, "external_pointer");
const kTypedArrayBasePointerOffset =
Sandbox.getFieldOffset(kTypedArrayType, "base_pointer");
const GB = 1024 * 1024 * 1024;
const kMaxInSandboxBufferSize = 32*GB - 1;
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let array = new BigInt64Array(new ArrayBuffer(8));
let array_address = Sandbox.getAddressOf(array);
let length_address = array_address + kTypedArrayLengthOffset;
memory.setBigUint64(length_address, 0xffffffffffffffffn, true);
let pointer_address = array_address + kTypedArrayExternalPointerOffset;
memory.setBigUint64(pointer_address, 0xffffffffffffffffn, true);
let offset_address = array_address + kTypedArrayBasePointerOffset;
memory.setUint32(offset_address, 0xffffffff, true);
assertEquals(array.length, kMaxInSandboxBufferSize);
array[array.length - 1] = 1337n;

View File

@ -0,0 +1,25 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --sandbox-testing
const kSlicedStringType = Sandbox.getInstanceTypeIdFor("SLICED_ONE_BYTE_STRING_TYPE");
const kSlicedStringParentOffset = Sandbox.getFieldOffset(kSlicedStringType, "parent");
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
// Create a sliced string.
const sliced_string = "It's fun to play in the sand".substring(3);
// Create a two-byte string
const two_byte_string = "⛱️📦"
// Corrupt the parent pointer of the sliced string to point to the two-byte
// string.
memory.setUint32(
Sandbox.getAddressOf(sliced_string) + kSlicedStringParentOffset,
Sandbox.getAddressOf(two_byte_string),
true);
// Observe the shenanigans!
sliced_string.toLowerCase();

View File

@ -0,0 +1,21 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --sandbox-testing
const kJSArrayType = Sandbox.getInstanceTypeIdFor("JS_ARRAY_TYPE");
const kJSArrayLengthOffset = Sandbox.getFieldOffset(kJSArrayType, "length");
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
let array = [0.0, 1.1, 2.2, 3.3, 4.4];
// Corrupt the length of the JSArray and change it to a large value.
memory.setUint32(
Sandbox.getAddressOf(array) + kJSArrayLengthOffset,
0x10000,
true);
// Try to push nothing, which should succeed and not crash in any way.
array.push();

View File

@ -0,0 +1,91 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing --expose-gc
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
// module 0: exporter
let builder = new WasmModuleBuilder();
let $struct = builder.addStruct([makeField(kWasmI64, true)]);
let $sig_v_s = builder.addType(makeSig([wasmRefType($struct)], []));
builder.addFunction("writer", $sig_v_s)
.exportFunc()
.addBody([
kExprLocalGet, 0,
kExprI64Const, 42,
kGCPrefix, kExprStructSet, $struct, 0,
]);
builder.addFunction("dummy", kSig_v_l).exportFunc().addBody([]);
let instance0 = builder.instantiate();
let { writer, dummy } = instance0.exports;
// module 1: importer
builder = new WasmModuleBuilder();
$struct = builder.addStruct([makeField(kWasmI64, true)]);
$sig_v_s = builder.addType(makeSig([wasmRefType($struct)], []));
let $sig_v_l = builder.addType(kSig_v_l);
let $importWriter = builder.addImport('import', 'writer', $sig_v_l);
let $boom = builder.addFunction("boom", $sig_v_l)
.exportFunc()
.addBody([
kExprLocalGet, 0,
kExprCallFunction, $importWriter,
]);
// Prepare corruption utilities.
const kHeapObjectTag = 1;
const kJSFunctionType = Sandbox.getInstanceTypeIdFor('JS_FUNCTION_TYPE');
const kSharedFunctionInfoType = Sandbox.getInstanceTypeIdFor('SHARED_FUNCTION_INFO_TYPE');
const kJSFunctionSFIOffset = Sandbox.getFieldOffset(kJSFunctionType, 'shared_function_info');
const kSharedFunctionInfoTrustedFunctionDataOffset = Sandbox.getFieldOffset(kSharedFunctionInfoType, 'trusted_function_data');
assertEquals(kSharedFunctionInfoTrustedFunctionDataOffset, 4); // Required below, in the workerTemplate
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function getPtr(obj) {
return Sandbox.getAddressOf(obj) + kHeapObjectTag;
}
function getField(obj, offset) {
return memory.getUint32(obj + offset - kHeapObjectTag, true);
}
function setField(obj, offset, value) {
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
}
// Perform a few GCs to move objects to a stable place in memory.
gc();
gc();
let writer_sfi = getField(getPtr(writer), kJSFunctionSFIOffset);
let writer_tfd = getField(writer_sfi, kSharedFunctionInfoTrustedFunctionDataOffset);
let dummy_sfi = getField(getPtr(dummy), kJSFunctionSFIOffset);
let dummy_tfd = getField(dummy_sfi, kSharedFunctionInfoTrustedFunctionDataOffset);
function workerTemplate(writer_sfi, writer_tfd, dummy_tfd) {
const kHeapObjectTag = 1;
const kSharedFunctionInfoTrustedFunctionDataOffset = 4;
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function setField(obj, offset, value) {
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
}
while (true) {
setField(writer_sfi, kSharedFunctionInfoTrustedFunctionDataOffset, dummy_tfd);
setField(writer_sfi, kSharedFunctionInfoTrustedFunctionDataOffset, writer_tfd);
}
}
const workerCode = new Function(
`(${workerTemplate})(${writer_sfi}, ${writer_tfd}, ${dummy_tfd})`);
let worker = new Worker(workerCode, {type: 'function'});
// Before fixing the issue, this usually took 1-5 iterations until it got
// lucky enough to win the race condition.
for (let i = 0; i < 20; i++) {
try {
let instance1 = builder.instantiate({'import': {'writer': writer}});
instance1.exports.boom(0x414141414141n);
} catch {
// Just try again.
}
}

View File

@ -0,0 +1,101 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --sandbox-testing
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
const builder = new WasmModuleBuilder();
builder.exportMemoryAs("mem0", 0);
let $mem0 = builder.addMemory(1, 1);
let $box = builder.addStruct([makeField(kWasmFuncRef, true)]);
let $struct = builder.addStruct([makeField(kWasmI32, true)]);
let $sig_i_l = builder.addType(kSig_i_l);
let $f0 = builder.addFunction("func0", makeSig([wasmRefType($struct)], []))
.exportFunc()
.addBody([
kExprLocalGet, 0,
kExprI32Const, 42,
kGCPrefix, kExprStructSet, $struct, 0,
]);
let $f1 = builder.addFunction("func1", $sig_i_l).exportFunc().addBody([
kExprI32Const, 0,
]);
builder.addFunction("get_func0", kSig_r_v).exportFunc().addBody([
kExprRefFunc, $f0.index,
kGCPrefix, kExprStructNew, $box,
kGCPrefix, kExprExternConvertAny,
]);
builder.addFunction("get_func1", kSig_r_v).exportFunc().addBody([
kExprRefFunc, $f1.index,
kGCPrefix, kExprStructNew, $box,
kGCPrefix, kExprExternConvertAny,
]);
builder.addFunction("boom", makeSig([kWasmFuncRef, kWasmI64], [kWasmI32]))
.exportFunc()
.addBody([
kExprLocalGet, 1,
kExprLocalGet, 0,
kGCPrefix, kExprRefCast, $sig_i_l,
kExprCallRef, $sig_i_l,
])
let instance = builder.instantiate();
let func0 = instance.exports.func0;
let func1 = instance.exports.func1;
let boom = instance.exports.boom;
// Collect type feedback.
for (let i = 0; i < 10; i++) {
instance.exports.boom(func1, 0n);
}
// Prepare corruption utilities.
const kHeapObjectTag = 1;
const kMapOffset = 0;
const kStructField0Offset = 8; // 0:map, 4:hash
const kWasmFuncRefType = Sandbox.getInstanceTypeIdFor('WASM_FUNC_REF_TYPE')
const kWasmFuncRefInternalOffset = Sandbox.getFieldOffset(kWasmFuncRefType, 'trusted_internal');
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function getPtr(obj) {
return Sandbox.getAddressOf(obj) + kHeapObjectTag;
}
function getField(obj, offset) {
return memory.getUint32(obj + offset - kHeapObjectTag, true);
}
function setField(obj, offset, value) {
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
}
// Corrupt $f1: if it has the wrong WasmInternalFunction when we process
// feedback, we'll inline the wrong target.
let f0_box = getPtr(instance.exports.get_func0());
let f0 = getField(f0_box, kStructField0Offset);
let f0_int = getField(f0, kWasmFuncRefInternalOffset);
let f1_box = getPtr(instance.exports.get_func1());
let f1 = getField(f1_box, kStructField0Offset);
setField(f1, kWasmFuncRefInternalOffset, f0_int);
// Also corrupt $f0 to make it past the type check.
let f1_map = getField(f1, kMapOffset);
setField(f0, kMapOffset, f1_map);
// Trigger optimization. This would inline the wrong target; the signature
// check should kill the process instead.
%WasmTierUpFunction(instance.exports.boom);
// If the process was still alive, this would cause the sandbox violation.
instance.exports.boom(func0, 0x414141414141n);
assertUnreachable("Process should have been killed.");

View File

@ -0,0 +1,81 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --sandbox-testing --expose-gc
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
// Helpers and constants.
const kHeapObjectTag = 1;
const kWasmInstanceObjectType = Sandbox.getInstanceTypeIdFor('WASM_INSTANCE_OBJECT_TYPE');
const kWasmModuleObjectType = Sandbox.getInstanceTypeIdFor('WASM_MODULE_OBJECT_TYPE');
const kScriptType = Sandbox.getInstanceTypeIdFor('SCRIPT_TYPE');
const kWasmInstanceObjectModuleOffset = Sandbox.getFieldOffset(kWasmInstanceObjectType, 'module_object');
const kWasmModuleObjectManagedNativeModuleOffset = Sandbox.getFieldOffset(kWasmModuleObjectType, 'managed_native_module');
const kWasmModuleObjectScriptOffset = Sandbox.getFieldOffset(kWasmModuleObjectType, 'script');
const kScriptWasmManagedNativeModuleOffset = Sandbox.getFieldOffset(kScriptType, 'wasm_managed_native_module');
const memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function getPtr(obj) {
return Sandbox.getAddressOf(obj) + kHeapObjectTag;
}
function getField(obj, offset) {
return memory.getUint32(obj + offset - kHeapObjectTag, true);
}
function setField(obj, offset, value) {
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
}
// End of helpers and constants.
function manipulate_instance() {
// Update instance1->module to point to instance2->module.
const inst1_addr = getPtr(instance1);
const inst2_addr = getPtr(instance2);
// Find module1 and module2.
const module1_addr = getField(inst1_addr, kWasmInstanceObjectModuleOffset);
const module2_addr = getField(inst2_addr, kWasmInstanceObjectModuleOffset);
// Manipulate module1->managed_native_module to point to
// module2->managed_native_module.
const managed_native_module_2 =
getField(module2_addr, kWasmModuleObjectManagedNativeModuleOffset);
setField(
module1_addr, kWasmModuleObjectManagedNativeModuleOffset,
managed_native_module_2);
// Find both Scripts.
const script1_addr = getField(module1_addr, kWasmModuleObjectScriptOffset);
const script2_addr = getField(module2_addr, kWasmModuleObjectScriptOffset);
// Manipulate script1->managed_native_module to point to
// script2->managed_native_module.
assertEquals(
managed_native_module_2,
getField(script2_addr, kScriptWasmManagedNativeModuleOffset));
setField(
script1_addr, kScriptWasmManagedNativeModuleOffset, managed_native_module_2);
// Trigger GCs such that module1 is garbage-collected.
print('Triggering GCs....');
gc();
print('Manipulation done.');
// If nothing else held the NativeModule alive we are now returning to
// deallocated code pages.
}
const builder = new WasmModuleBuilder();
const imp_idx = builder.addImport('imp', 'f', kSig_v_v);
builder.addFunction('call_import', kSig_v_v).exportFunc().addBody([
kExprCallFunction, imp_idx
]);
const instance1 = builder.instantiate({imp: {f: manipulate_instance}});
builder.addGlobal(kWasmI32, true, false); // unused.
const instance2 = builder.instantiate({imp: {f: manipulate_instance}});
instance1.exports.call_import();

View File

@ -0,0 +1,71 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --wasm-staging --sandbox-testing
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
const builder = new WasmModuleBuilder();
builder.exportMemoryAs("mem0", 0);
let $mem0 = builder.addMemory(1, 1);
let $box = builder.addStruct([makeField(kWasmFuncRef, true)]);
let $sig_i_l = builder.addType(kSig_i_l);
// func0 and func1 have no other job than to have incompatible signatures.
builder.addFunction("func0", kSig_v_i).exportFunc().addBody([]);
builder.addFunction("func1", $sig_i_l).exportFunc().addBody([
kExprI32Const, 0,
]);
builder.addFunction("get_func0", kSig_r_v).exportFunc().addBody([
kExprRefFunc, 0,
kGCPrefix, kExprStructNew, $box,
kGCPrefix, kExprExternConvertAny,
]);
builder.addFunction("get_func1", kSig_r_v).exportFunc().addBody([
kExprRefFunc, 1,
kGCPrefix, kExprStructNew, $box,
kGCPrefix, kExprExternConvertAny,
]);
builder.addFunction("boom", kSig_i_l).exportFunc().addBody([
kExprLocalGet, 0,
kExprRefFunc, 1,
kExprCallRef, $sig_i_l,
])
let instance = builder.instantiate();
instance.exports.func0(0);
const kHeapObjectTag = 1;
const kStructField0Offset = 8; // 0:map, 4:hash
const kWasmFuncRefType = Sandbox.getInstanceTypeIdFor('WASM_FUNC_REF_TYPE');
const kWasmFuncRefInternalOffset = Sandbox.getFieldOffset(kWasmFuncRefType, 'trusted_internal');
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function getPtr(obj) {
return Sandbox.getAddressOf(obj) + kHeapObjectTag;
}
function getField(obj, offset) {
return memory.getUint32(obj + offset - kHeapObjectTag, true);
}
function setField(obj, offset, value) {
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
}
let f0_box = getPtr(instance.exports.get_func0());
let f0 = getField(f0_box, kStructField0Offset);
let f0_int = getField(f0, kWasmFuncRefInternalOffset);
let f1_box = getPtr(instance.exports.get_func1());
let f1 = getField(f1_box, kStructField0Offset);
setField(f1, kWasmFuncRefInternalOffset, f0_int);
// Signature confusion should kill the process.
instance.exports.boom(0x41414141414141n);
assertUnreachable("Process should have been killed.");

View File

@ -0,0 +1,73 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
// module 0
let builder = new WasmModuleBuilder();
let $struct = builder.addStruct([makeField(kWasmI64, true)]);
let $sig0 = builder.addType(makeSig([wasmRefType($struct), kWasmI64], []));
let $sig1_0 = builder.addType(makeSig([kWasmI64, kWasmI64], []));
let $writer = builder.addFunction("writer", $sig0)
.exportFunc()
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kGCPrefix, kExprStructSet, $struct, 0,
]);
let $placeholder = builder.addFunction("placeholder", $sig1_0).addBody([]);
let $t0 = builder
.addTable(wasmRefType($sig0), 1, 1, [kExprRefFunc, $writer.index])
.exportAs('table0');
let $t1 = builder
.addTable(wasmRefType($sig1_0), 1, 1, [kExprRefFunc, $placeholder.index])
.exportAs('table1');
let instance0 = builder.instantiate();
let { writer, table0, table1 } = instance0.exports;
// module 1
builder = new WasmModuleBuilder();
// $sig_1_1 and $sig1_0 are canonicalized to the same index.
let $sig1_1 = builder.addType(makeSig([kWasmI64, kWasmI64], []));
let $boom = builder.addFunction("boom", $sig1_1)
.exportFunc()
.addBody([
kExprLocalGet, 1,
kExprLocalGet, 0,
kExprI32Const, 0,
kExprCallIndirect, $sig1_1, 0,
]);
let $t_imp =
builder.addImportedTable('import', 'table', 1, 1, wasmRefType($sig1_1));
// Prepare corruption utilities.
const kHeapObjectTag = 1;
const kWasmTableType = Sandbox.getInstanceTypeIdFor('WASM_TABLE_OBJECT_TYPE');
const kWasmTableEntriesOffset = Sandbox.getFieldOffset(kWasmTableType, 'entries');
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function getPtr(obj) {
return Sandbox.getAddressOf(obj) + kHeapObjectTag;
}
function getField(obj, offset) {
return memory.getUint32(obj + offset - kHeapObjectTag, true);
}
function setField(obj, offset, value) {
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
}
// Set table1.entries[0] = table0.entries[0].
let t0_entries = getField(getPtr(table0), kWasmTableEntriesOffset);
let t0_entry = getField(t0_entries, 8);
let t1_entries = getField(getPtr(table1), kWasmTableEntriesOffset);
setField(t1_entries, 8, t0_entry);
// All checks pass - table type already equivalent, but entry replaced.
let instance1 = builder.instantiate({'import': {'table': table1}});
// This still calls the original function, because overwriting an entry
// doesn't affect the WasmDispatchTable.
instance1.exports.boom(0x414141414141n, 42n);

View File

@ -0,0 +1,85 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing --allow-natives-syntax
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
const builder = new WasmModuleBuilder();
builder.exportMemoryAs("mem0", 0);
let $mem0 = builder.addMemory(1, 1);
let $box = builder.addStruct([makeField(kWasmFuncRef, true)]);
let $struct = builder.addStruct([makeField(kWasmI32, true)]);
let $sig_i_l = builder.addType(kSig_i_l);
let $sig_v_struct = builder.addType(makeSig([wasmRefType($struct)], []));
let $f0 = builder.addFunction("func0", $sig_v_struct)
.exportFunc()
.addBody([
kExprLocalGet, 0,
kExprI32Const, 42,
kGCPrefix, kExprStructSet, $struct, 0,
]);
let $f1 = builder.addFunction("func1", $sig_i_l).exportFunc().addBody([
kExprI32Const, 0,
]);
let $t0 =
builder.addTable(wasmRefType($sig_i_l), 1, 1, [kExprRefFunc, $f1.index]);
builder.addExportOfKind("table0", kExternalTable, $t0.index);
builder.addFunction("boom", kSig_i_l)
.exportFunc()
.addBody([
kExprLocalGet, 0, // func parameter
kExprI32Const, 0, // func index
kExprCallIndirect, $sig_i_l, kTableZero,
])
let instance = builder.instantiate();
let boom = instance.exports.boom;
let func0 = instance.exports.func0;
let table0 = instance.exports.table0;
// Prepare corruption utilities.
const kHeapObjectTag = 1;
const kWasmTableType = Sandbox.getInstanceTypeIdFor('WASM_TABLE_OBJECT_TYPE');
const kWasmTableObjectTypeOffset = Sandbox.getFieldOffset(kWasmTableType, 'raw_type');
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function getPtr(obj) {
return Sandbox.getAddressOf(obj) + kHeapObjectTag;
}
function getField(obj, offset) {
return memory.getUint32(obj + offset - kHeapObjectTag, true);
}
function setField(obj, offset, value) {
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
}
// Without corruption, putting func0 into the table fails gracefully.
assertThrows(
() => { table0.set(0, func0); }, TypeError,
/assigned exported function has to be a subtype of the expected type/);
// Corrupt the table's type to accept putting $func0 into it.
let t0 = getPtr(table0);
const kSmiTagSize = 1;
let expected_old_type = %BuildRefTypeBitfield($sig_i_l, instance) << kSmiTagSize;
let new_type = %BuildRefTypeBitfield($sig_v_struct, instance) << kSmiTagSize;
assertEquals(expected_old_type, getField(t0, kWasmTableObjectTypeOffset));
setField(t0, kWasmTableObjectTypeOffset, new_type);
// This should run into a signature check that kills the process.
table0.set(0, func0);
// If the process was still alive, this would cause the sandbox violation.
instance.exports.boom(0x414141414141n);
assertUnreachable("Process should have been killed.");

View File

@ -0,0 +1,85 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --sandbox-testing --allow-natives-syntax
// Flags: --experimental-wasm-type-reflection
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
// module 0
let builder = new WasmModuleBuilder();
let $struct = builder.addStruct([makeField(kWasmI64, true)]);
let $sig0 = builder.addType(makeSig([], [wasmRefType($struct)]));
let sig1 = kSig_l_v;
let $sig1 = builder.addType(sig1);
let $sig0_func = builder.addFunction("sig0_placeholder", $sig0).addBody([
kGCPrefix, kExprStructNewDefault, $struct
]);
let $sig1_func = builder.addFunction("placeholder", $sig1).addBody([
kExprI64Const, 0,
]);
let $t0 = builder
.addTable(wasmRefType($sig0), 1, 1, [kExprRefFunc, $sig0_func.index])
.exportAs('table0');
let $t1 = builder
.addTable(wasmRefType($sig1), 1, 1, [kExprRefFunc, $sig1_func.index])
.exportAs('table1');
let instance0 = builder.instantiate();
let { table0, table1 } = instance0.exports;
// module 1
builder = new WasmModuleBuilder();
$struct = builder.addStruct([makeField(kWasmI64, true)]);
$sig0 = builder.addType(makeSig([], [wasmRefType($struct)]));
$sig1 = builder.addType(sig1);
let $boom = builder.addFunction("boom", kSig_v_v)
.exportFunc()
.addBody([
kExprI32Const, 0,
kExprCallIndirect, $sig0, 0,
kExprI64Const, 12,
kGCPrefix, kExprStructSet, $struct, 0,
]);
let $t_imp =
builder.addImportedTable('import', 'table', 1, 1, wasmRefType($sig0));
// Prepare corruption utilities.
const kHeapObjectTag = 1;
const kWasmTableType = Sandbox.getInstanceTypeIdFor('WASM_TABLE_OBJECT_TYPE');
const kWasmTableObjectTypeOffset = Sandbox.getFieldOffset(kWasmTableType, 'raw_type');
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
function getPtr(obj) {
return Sandbox.getAddressOf(obj) + kHeapObjectTag;
}
function getField(obj, offset) {
return memory.getUint32(obj + offset - kHeapObjectTag, true);
}
function setField(obj, offset, value) {
memory.setUint32(obj + offset - kHeapObjectTag, value, true);
}
const kSmiTagSize = 1;
// Put a WasmJSFunction into table1 while it still has type $sig1.
table1.set(0, new WebAssembly.Function(
{parameters: [], results: ['i64']},
() => 0x414141414141n));
// Now set table1's type to $sig0.
let t0 = getPtr(table0);
let t1 = getPtr(table1);
let t0_type = getField(t0, kWasmTableObjectTypeOffset);
let expected_old_type = %BuildRefTypeBitfield($sig1, instance0) << kSmiTagSize;
assertEquals(expected_old_type, getField(t1, kWasmTableObjectTypeOffset));
setField(t1, kWasmTableObjectTypeOffset, t0_type);
// Instantiation accepts the table due to its corrupted type.
let instance1 = builder.instantiate({'import': {'table': table1}});
instance1.exports.boom();
assertUnreachable("Process should have been killed.");