diff options
Diffstat (limited to 'js/src/fuzz-tests/parsing-evaluate.js')
-rw-r--r-- | js/src/fuzz-tests/parsing-evaluate.js | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/js/src/fuzz-tests/parsing-evaluate.js b/js/src/fuzz-tests/parsing-evaluate.js new file mode 100644 index 0000000000..1714e051a9 --- /dev/null +++ b/js/src/fuzz-tests/parsing-evaluate.js @@ -0,0 +1,83 @@ +/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This fuzzing target aims to stress the SpiderMonkey parser. However, for +// this purpose, it does *not* use `parse()` because some past bugs in the +// parser could only be triggered in the runtime later. Instead, we use +// the `evaluate` function which parses and runs the code. This brings in +// other problems like timeouts and permanent side-effects. We try to minimize +// the amount of permanent side-effects from running the code by running it +// in a fresh global for each iteration. We also use a special function +// called `sanitizeGlobal` to remove any harmful shell functions from the +// global prior to running. Many of these shell functions would otherwise +// have permanent side-effects of some sort or be disruptive to testing like +// increasing the amount of timeouts or leak memory. Finally, the target also +// tries to catch timeouts locally and signal back any timeouts by returning 1 +// from the iteration function. + +// This global will hold the current fuzzing buffer for each iteration. +var fuzzBuf; + +loadRelativeToScript("util/sanitize.js"); + +deterministicgc(true); + +// Set a default value for timeouts to 1 second, but allow this to +// be set on the command line as well using -e fuzzTimeout=VAL. +if (typeof fuzzTimeout === "undefined") { + fuzzTimeout = 1; +} + +function JSFuzzIterate() { + try { + let code = String.fromCharCode(...fuzzBuf); + let result = null; + + // Create a new global and sanitize it such that its potentially permanent + // side-effects are reduced to a minimum. + let global = newGlobal(); + sanitizeGlobal(global); + + // Work around memory leaks when the hook is not set + evaluate(` + setModuleResolveHook(function(module, specifier) { + throw "Module '" + specifier + "' not found"; + }); + setModuleResolveHook = function() {}; + `, { global: global, catchTermination: true }); + + // Start a timer and set a timeout in addition + let lfStart = monotonicNow(); + timeout(fuzzTimeout, function() { return false; }); + + try { + result = evaluate(code, { global: global, catchTermination: true }); + } catch(exc) { + print(exc); + } + + timeout(-1); + let lfStop = monotonicNow(); + + // Reset some things that could have been altered by the code we ran + gczeal(0); + schedulegc(0); + setGCCallback({ action: "majorGC" }); + clearSavedFrames(); + + // If we either ended terminating the script, or we took longer than + // the timeout set (but timeout didn't kick in), then we return 1 to + // signal libFuzzer that the sample just be abandoned. + if (result === "terminated" || (lfStop - lfStart > (fuzzTimeout * 1000 + 200))) { + return 1; + } + + return 0; + } catch(exc) { + print("Caught toplevel exception: " + exc); + } + + return 1; +} |