/* -*- 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 file is used to detect and find cases where the Visage's parser is // accepting more inputs than SpiderMonkey's parser. // // 1. Find new cases: // // To find new cases, we have to build with libFuzzer. The JS Shell can easily // be built with libFuzzer by adding --enable-fuzzing to the configure script // command line. // // Create a directory, and copy all test cases from the JS shell to this // directory: // // $ mkdir fuzzer-input // $ cd fuzzer-input // $ find ../ -name \*.js -print0 | xargs -I '{}' -0 -n1 cp '{}' $(pwd) // // Once the JS Shell is built, set the FUZZER environment variable to this // script location. // // $ FUZZER="./fuzz-tests/differential-parsing.js" build.dir/dist/bin/js -- \ // -use_value_profile=1 -print_pcs=1 -timeout=5 -max_len=32 -only_ascii=1 \ // ./fuzzer-input // // 2. Test a crashing test case: // // Once a new crashing test case is found, this script can be used to // reproduce the crashing conditions. // // To do so, you need a JS Shell and to load this script and use the testFile // function with the location of the crashing file. // // $ build.dir/dist/bin/js // js> load("./fuzz-tests/differential-parsing.js"); // js> testFile("./crash-42"); // Parse Script C++: fail // Parse Module C++: fail // Parse Script Rust: succeed // Parse Module Rust: fail // Hit MOZ_CRASH(Rust accept more than C++) // /* global crash, os, parse, timeout */ // This global will hold the current fuzzing buffer for each iteration. var fuzzBuf; function timed(sec, f) { // If the function `f` takes more than 3 seconds, then the evaluation ends // prematurely and returns in libFuzzer handler without considering this // test case as interesting. timeout(sec, function() { return false; }); f(); // Remove the timeout handler, to not kill future executions. timeout(-1); } var parseScriptCpp = { module: false, smoosh: false }; var parseScriptRust = { module: false, smoosh: true }; var parseModuleRust = { module: true, smoosh: true }; var parseModuleCpp = { module: true, smoosh: false }; function test(code, verbose = false) { var isScriptCpp = false, isModuleCpp = false, isScriptRust = false, isModuleRust = false; try { parse(code, parseScriptCpp); isScriptCpp = true; if (verbose) { console.log("Parse Script C++: succeed"); } } catch (exc) { if (verbose) { console.log("Parse Script C++: fail"); } } try { parse(code, parseModuleCpp); isModuleCpp = true; if (verbose) { console.log("Parse Module C++: succeed"); } } catch (exc) { if (verbose) { console.log("Parse Module C++: fail"); } } try { parse(code, parseScriptRust); isScriptRust = true; if (verbose) { console.log("Parse Script Rust: succeed"); } } catch (exc) { if (verbose) { console.log("Parse Script Rust: fail"); } } try { parse(code, parseModuleRust); isModuleRust = true; if (verbose) { console.log("Parse Module Rust: succeed"); } } catch (exc) { if (verbose) { console.log("Parse Module Rust: fail"); } } if ((isScriptRust && !isScriptCpp) || (isModuleRust && !isModuleCpp)) { crash("Rust accept more than C++"); } } function JSFuzzIterate() { // This function is called per iteration. You must ensure that: // // 1) Each of your actions/decisions is only based on fuzzBuf, // in particular not on Math.random(), Date/Time or other // external inputs. // // 2) Your actions should be deterministic. The same fuzzBuf // should always lead to the same set of actions/decisions. // // 3) You can modify the global where needed, but ensure that // each iteration is isolated from one another by cleaning // any modifications to the global after each iteration. // In particular, iterations must not depend on or influence // each other in any way (see also 1)). // // 4) You must catch all exceptions. let code = String.fromCharCode(...fuzzBuf); timed(3, _ => test(code)); return 0; } function testFile(file) { let content = os.file.readFile(file); test(content, true); }