summaryrefslogtreecommitdiffstats
path: root/js/src/fuzz-tests/parsing-evaluate.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/fuzz-tests/parsing-evaluate.js')
-rw-r--r--js/src/fuzz-tests/parsing-evaluate.js83
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;
+}