mirrored from https://chromium.googlesource.com/v8/v8.git
-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[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}
- Loading branch information
Samuel Groß
authored and
V8 LUCI CQ
committed
Nov 27, 2024
1 parent
2a37e31
commit 954dbf0
Showing
3 changed files
with
99 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 {} | ||
} |