Skip to content

Commit 954dbf0

Browse files
Samuel GroßV8 LUCI CQ
authored andcommitted
[sandbox] Add regression test for crbug.com/379774687
This is a follow-up for https://crrev.com/c/6035110 that adds a regression test that is able to trigger issues due to concurrent modifications of JS source code while it is being parsed. The testcase is effectively a mini-fuzzer for these issues and requires --random-seed to trigger the issue reliably. This way the test case is fairly robust and doesn't rely on a specific heap layout, and it is easily picked up by "real" fuzzers for further mutations. Bug: 379774687 Change-Id: I95d1991c997354a8afc5c299475862aa68fa577d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6048825 Commit-Queue: Samuel Groß <[email protected]> Reviewed-by: Toon Verwaest <[email protected]> Cr-Commit-Position: refs/heads/main@{#97449}
1 parent 2a37e31 commit 954dbf0

File tree

3 files changed

+99
-0
lines changed

3 files changed

+99
-0
lines changed

src/objects/name.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ V8_OBJECT class Name : public PrimitiveHeapObject {
220220
friend class V8HeapExplorer;
221221
friend class CodeStubAssembler;
222222
friend class StringBuiltinsAssembler;
223+
friend class SandboxTesting;
223224
friend class maglev::MaglevAssembler;
224225
friend class compiler::AccessBuilder;
225226
friend class compiler::WasmGraphBuilder;

src/sandbox/testing.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,7 @@ SandboxTesting::InstanceTypeMap& SandboxTesting::GetInstanceTypeMap() {
802802
types["JS_FUNCTION_TYPE"] = JS_FUNCTION_TYPE;
803803
types["JS_ARRAY_TYPE"] = JS_ARRAY_TYPE;
804804
types["SEQ_ONE_BYTE_STRING_TYPE"] = SEQ_ONE_BYTE_STRING_TYPE;
805+
types["SEQ_TWO_BYTE_STRING_TYPE"] = SEQ_TWO_BYTE_STRING_TYPE;
805806
types["INTERNALIZED_ONE_BYTE_STRING_TYPE"] =
806807
INTERNALIZED_ONE_BYTE_STRING_TYPE;
807808
types["SLICED_ONE_BYTE_STRING_TYPE"] = SLICED_ONE_BYTE_STRING_TYPE;
@@ -837,6 +838,10 @@ SandboxTesting::FieldOffsetMap& SandboxTesting::GetFieldOffsetMap() {
837838
fields[JS_ARRAY_TYPE]["length"] = JSArray::kLengthOffset;
838839
fields[SEQ_ONE_BYTE_STRING_TYPE]["length"] =
839840
offsetof(SeqOneByteString, length_);
841+
fields[SEQ_TWO_BYTE_STRING_TYPE]["hash"] =
842+
offsetof(SeqTwoByteString, raw_hash_field_);
843+
fields[SEQ_TWO_BYTE_STRING_TYPE]["length"] =
844+
offsetof(SeqTwoByteString, length_);
840845
fields[INTERNALIZED_ONE_BYTE_STRING_TYPE]["length"] =
841846
offsetof(InternalizedString, length_);
842847
fields[SLICED_ONE_BYTE_STRING_TYPE]["parent"] =
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2024 the V8 project authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// Flags: --sandbox-testing --expose-gc
6+
7+
const kSeqStringType = Sandbox.getInstanceTypeIdFor("SEQ_TWO_BYTE_STRING_TYPE");
8+
const kStringHashOffset = Sandbox.getFieldOffset(kSeqStringType, "hash");
9+
10+
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
11+
12+
// Will return random (but deterministic, if a --random-seed is used) numbers.
13+
function randomIntUpTo(n) {
14+
return Math.floor(Math.random() * n);
15+
}
16+
17+
// Helper function that spawns a worker thread that corrupts memory in the
18+
// background, constantly flipping the given address between two values.
19+
function corruptInBackground(address, bitToFlip) {
20+
function workerTemplate(address, bitToFlip) {
21+
let memory = new DataView(new Sandbox.MemoryView(0, 0x100000000));
22+
let oldValue = memory.getUint32(address, true);
23+
let newValue = oldValue ^ bitToFlip;
24+
while (true) {
25+
memory.setUint32(address, newValue, true);
26+
memory.setUint32(address, oldValue, true);
27+
}
28+
}
29+
const workerCode = new Function(
30+
`(${workerTemplate})(${address}, ${bitToFlip})`);
31+
return new Worker(workerCode, {type: 'function'});
32+
}
33+
34+
// Some random code for the parser...
35+
// Need some unicode in here to get a Utf16 string (otherwise, we'd get a
36+
// buffered stream during parsing, which probably complicates things a bit).
37+
function test() {
38+
function log(msg) {}
39+
40+
function launchRockets(n, destination) {
41+
class RocketLauncher {
42+
constructor(type) {
43+
this.rocketType = type ?? '🚀';
44+
this.launchCount = 0;
45+
}
46+
47+
launch(destination) {
48+
log(`Launching ${this.rocketType} to ${destination}!`);
49+
this.launchCount++;
50+
}
51+
}
52+
53+
if (typeof destination === 'undefined') {
54+
destination = "the moon";
55+
}
56+
57+
let launcher = new RocketLauncher;
58+
for (let i = 0; i < n; i++) {
59+
launcher.launch(destination);
60+
}
61+
}
62+
63+
launchRockets(3);
64+
launchRockets(2, "mars");
65+
launchRockets(1, "pluto");
66+
}
67+
68+
// Create a SeqTwoByteString (otherwise we have a slice string)
69+
let source = (test.toString() + "\ntest();").split('').join('');
70+
assertEquals(Sandbox.getInstanceTypeIdOf(source), kSeqStringType);
71+
72+
// Trigger some GCs to move the object to a stable position in memory.
73+
gc();
74+
gc();
75+
76+
// Start the worker thread to corrupt the string in the background.
77+
let size = Sandbox.getSizeOf(source);
78+
assertTrue(size > source.length * 2);
79+
let offset = randomIntUpTo(size);
80+
let string_address = Sandbox.getAddressOf(source);
81+
let bitToFlip = 1 << randomIntUpTo(32);
82+
corruptInBackground(string_address + offset, bitToFlip);
83+
84+
for (let i = 0; i < 1000; i++) {
85+
// Modify the hash in between every attempt to avoid code caching.
86+
// Use + 0x4 here to not change the type bits (see HashFieldTypeBits).
87+
// Alternatively, use --no-compilation-cache.
88+
let currentHash = memory.getUint32(string_address + kStringHashOffset, true);
89+
let newHash = currentHash + 0x4;
90+
memory.setUint32(string_address + kStringHashOffset, newHash, true);
91+
92+
try { eval(source); } catch {}
93+
}

0 commit comments

Comments
 (0)