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,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();