320d98709f
This is a JavaScript fuzzer originally authored by Oliver Chang. It is a mutation based fuzzer using Babel code transformations. For more information see the included README.md. The original code was altered: - Add new V8 copyright headers. - Make the test expectation generator aware of the headers. - Fix file endings for presubmit checks. - Fix `npm test` on fresh checkout with a new fake DB. - Make test skipping work with new v8/tools location. - OWNERS file. - New title section in README.md. No-Try: true Bug: chromium:1109770 Change-Id: Ie71752c0a37491a50500c49060a3c526716ef933 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2320330 Commit-Queue: Michael Achenbach <machenbach@chromium.org> Reviewed-by: Maya Lekova <mslekova@chromium.org> Cr-Commit-Position: refs/heads/master@{#69164}
169 lines
5.5 KiB
JavaScript
169 lines
5.5 KiB
JavaScript
// Copyright 2020 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.
|
|
|
|
/**
|
|
* @fileoverview Script mutator for differential fuzzing.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const assert = require('assert');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const common = require('./mutators/common.js');
|
|
const random = require('./random.js');
|
|
const sourceHelpers = require('./source_helpers.js');
|
|
|
|
const { filterDifferentialFuzzFlags } = require('./exceptions.js');
|
|
const { DifferentialFuzzMutator, DifferentialFuzzSuppressions } = require(
|
|
'./mutators/differential_fuzz_mutator.js');
|
|
const { ScriptMutator } = require('./script_mutator.js');
|
|
|
|
|
|
const USE_ORIGINAL_FLAGS_PROB = 0.2;
|
|
|
|
/**
|
|
* Randomly chooses a configuration from experiments. The configuration
|
|
* parameters are expected to be passed from a bundled V8 build. Constraints
|
|
* mentioned below are enforced by PRESUBMIT checks on the V8 side.
|
|
*
|
|
* @param {Object[]} experiments List of tuples (probability, first config name,
|
|
* second config name, second d8 name). The probabilities are integers in
|
|
* [0,100]. We assume the sum of all probabilities is 100.
|
|
* @param {Object[]} additionalFlags List of tuples (probability, flag strings).
|
|
* Probability is in [0,1).
|
|
* @return {string[]} List of flags for v8_foozzie.py.
|
|
*/
|
|
function chooseRandomFlags(experiments, additionalFlags) {
|
|
// Add additional flags to second config based on experiment percentages.
|
|
const extra_flags = [];
|
|
for (const [p, flags] of additionalFlags) {
|
|
if (random.choose(p)) {
|
|
for (const flag of flags.split(' ')) {
|
|
extra_flags.push('--second-config-extra-flags=' + flag);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate flags determining the experiment.
|
|
let acc = 0;
|
|
const threshold = random.random() * 100;
|
|
for (let [prob, first_config, second_config, second_d8] of experiments) {
|
|
acc += prob;
|
|
if (acc > threshold) {
|
|
return [
|
|
'--first-config=' + first_config,
|
|
'--second-config=' + second_config,
|
|
'--second-d8=' + second_d8,
|
|
].concat(extra_flags);
|
|
}
|
|
}
|
|
// Unreachable.
|
|
assert(false);
|
|
}
|
|
|
|
function loadJSONFromBuild(name) {
|
|
assert(process.env.APP_DIR);
|
|
const fullPath = path.join(path.resolve(process.env.APP_DIR), name);
|
|
return JSON.parse(fs.readFileSync(fullPath, 'utf-8'));
|
|
}
|
|
|
|
function hasMjsunit(dependencies) {
|
|
return dependencies.some(dep => dep.relPath.endsWith('mjsunit.js'));
|
|
}
|
|
|
|
function hasJSTests(dependencies) {
|
|
return dependencies.some(dep => dep.relPath.endsWith('jstest_stubs.js'));
|
|
}
|
|
|
|
class DifferentialScriptMutator extends ScriptMutator {
|
|
constructor(settings, db_path) {
|
|
super(settings, db_path);
|
|
|
|
// Mutators for differential fuzzing.
|
|
this.differential = [
|
|
new DifferentialFuzzSuppressions(settings),
|
|
new DifferentialFuzzMutator(settings),
|
|
];
|
|
|
|
// Flag configurations from the V8 build directory.
|
|
this.experiments = loadJSONFromBuild('v8_fuzz_experiments.json');
|
|
this.additionalFlags = loadJSONFromBuild('v8_fuzz_flags.json');
|
|
}
|
|
|
|
/**
|
|
* Performes the high-level mutation and afterwards adds flags for the
|
|
* v8_foozzie.py harness.
|
|
*/
|
|
mutateMultiple(inputs) {
|
|
const result = super.mutateMultiple(inputs);
|
|
const originalFlags = [];
|
|
|
|
// Keep original JS flags in some cases. Let the harness pass them to
|
|
// baseline _and_ comparison run.
|
|
if (random.choose(USE_ORIGINAL_FLAGS_PROB)) {
|
|
for (const flag of filterDifferentialFuzzFlags(result.flags)) {
|
|
originalFlags.push('--first-config-extra-flags=' + flag);
|
|
originalFlags.push('--second-config-extra-flags=' + flag);
|
|
}
|
|
}
|
|
|
|
// Add flags for the differnetial-fuzzing settings.
|
|
const fuzzFlags = chooseRandomFlags(this.experiments, this.additionalFlags);
|
|
result.flags = fuzzFlags.concat(originalFlags);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Mutatates a set of inputs.
|
|
*
|
|
* Additionally we prepare inputs by tagging each with the original source
|
|
* path for later printing. The mutated sources are post-processed by the
|
|
* differential-fuzz mutators, adding extra printing and other substitutions.
|
|
*/
|
|
mutateInputs(inputs) {
|
|
inputs.forEach(input => common.setOriginalPath(input, input.relPath));
|
|
|
|
const result = super.mutateInputs(inputs);
|
|
this.differential.forEach(mutator => mutator.mutate(result));
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Adds extra dependencies for differential fuzzing.
|
|
*/
|
|
resolveDependencies(inputs) {
|
|
const dependencies = super.resolveDependencies(inputs);
|
|
// The suppression file neuters functions not working with differential
|
|
// fuzzing. It can also be used to temporarily silence some functionality
|
|
// leading to dupes of an active bug.
|
|
dependencies.push(
|
|
sourceHelpers.loadResource('differential_fuzz_suppressions.js'));
|
|
// Extra printing and tracking functionality.
|
|
dependencies.push(
|
|
sourceHelpers.loadResource('differential_fuzz_library.js'));
|
|
// Make Chakra tests print more.
|
|
dependencies.push(
|
|
sourceHelpers.loadResource('differential_fuzz_chakra.js'));
|
|
|
|
if (hasMjsunit(dependencies)) {
|
|
// Make V8 tests print more. We guard this as the functionality
|
|
// relies on mjsunit.js.
|
|
dependencies.push(sourceHelpers.loadResource('differential_fuzz_v8.js'));
|
|
}
|
|
|
|
if (hasJSTests(dependencies)) {
|
|
dependencies.push(
|
|
sourceHelpers.loadResource('differential_fuzz_jstest.js'));
|
|
}
|
|
|
|
return dependencies;
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
DifferentialScriptMutator: DifferentialScriptMutator,
|
|
};
|