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}
176 lines
5.5 KiB
JavaScript
176 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 Try catch wrapper.
|
|
*/
|
|
|
|
const babelTypes = require('@babel/types');
|
|
|
|
const common = require('./common.js');
|
|
const mutator = require('./mutator.js');
|
|
const random = require('../random.js');
|
|
|
|
// Default target probability for skipping try-catch completely.
|
|
const DEFAULT_SKIP_PROB = 0.2;
|
|
|
|
// Default target probability to wrap only on toplevel, i.e. to not nest
|
|
// try-catch.
|
|
const DEFAULT_TOPLEVEL_PROB = 0.3;
|
|
|
|
// Probability to deviate from defaults and use extreme cases.
|
|
const IGNORE_DEFAULT_PROB = 0.05;
|
|
|
|
// Member expressions to be wrapped. List of (object, property) identifier
|
|
// tuples.
|
|
const WRAPPED_MEMBER_EXPRESSIONS = [
|
|
['WebAssembly', 'Module'],
|
|
['WebAssembly', 'Instantiate'],
|
|
];
|
|
|
|
function wrapTryCatch(node) {
|
|
return babelTypes.tryStatement(
|
|
babelTypes.blockStatement([node]),
|
|
babelTypes.catchClause(
|
|
babelTypes.identifier('e'),
|
|
babelTypes.blockStatement([])));
|
|
}
|
|
|
|
function wrapTryCatchInFunction(node) {
|
|
const ret = wrapTryCatch(babelTypes.returnStatement(node));
|
|
const anonymousFun = babelTypes.functionExpression(
|
|
null, [], babelTypes.blockStatement([ret]));
|
|
return babelTypes.callExpression(anonymousFun, []);
|
|
}
|
|
|
|
// Wrap particular member expressions after `new` that are known to appear
|
|
// in initializer lists of `let` and `const`.
|
|
function replaceNewExpression(path) {
|
|
const callee = path.node.callee;
|
|
if (!babelTypes.isMemberExpression(callee) ||
|
|
!babelTypes.isIdentifier(callee.object) ||
|
|
!babelTypes.isIdentifier(callee.property)) {
|
|
return;
|
|
}
|
|
if (WRAPPED_MEMBER_EXPRESSIONS.some(
|
|
([object, property]) => callee.object.name === object &&
|
|
callee.property.name === property)) {
|
|
path.replaceWith(wrapTryCatchInFunction(path.node));
|
|
path.skip();
|
|
}
|
|
}
|
|
|
|
function replaceAndSkip(path) {
|
|
if (!babelTypes.isLabeledStatement(path.parent) ||
|
|
!babelTypes.isLoop(path.node)) {
|
|
// Don't wrap loops with labels as it makes continue
|
|
// statements syntactically invalid. We wrap the label
|
|
// instead below.
|
|
path.replaceWith(wrapTryCatch(path.node));
|
|
}
|
|
// Prevent infinite looping.
|
|
path.skip();
|
|
}
|
|
|
|
class AddTryCatchMutator extends mutator.Mutator {
|
|
callWithProb(path, fun) {
|
|
const probability = random.random();
|
|
if (probability < this.skipProb * this.loc) {
|
|
// Entirely skip try-catch wrapper.
|
|
path.skip();
|
|
} else if (probability < (this.skipProb + this.toplevelProb) * this.loc) {
|
|
// Only wrap on top-level.
|
|
fun(path);
|
|
}
|
|
}
|
|
|
|
get visitor() {
|
|
const thisMutator = this;
|
|
const accessStatement = {
|
|
enter(path) {
|
|
thisMutator.callWithProb(path, replaceAndSkip);
|
|
},
|
|
exit(path) {
|
|
// Apply nested wrapping (is only executed if not skipped above).
|
|
replaceAndSkip(path);
|
|
}
|
|
};
|
|
return {
|
|
Program: {
|
|
enter(path) {
|
|
// Track original source location fraction in [0, 1).
|
|
thisMutator.loc = 0;
|
|
// Target probability for skipping try-catch.
|
|
thisMutator.skipProb = DEFAULT_SKIP_PROB;
|
|
// Target probability for not nesting try-catch.
|
|
thisMutator.toplevelProb = DEFAULT_TOPLEVEL_PROB;
|
|
// Maybe deviate from target probability for the entire test.
|
|
if (random.choose(IGNORE_DEFAULT_PROB)) {
|
|
thisMutator.skipProb = random.uniform(0, 1);
|
|
thisMutator.toplevelProb = random.uniform(0, 1);
|
|
thisMutator.annotate(
|
|
path.node,
|
|
'Target skip probability ' + thisMutator.skipProb +
|
|
' and toplevel probability ' + thisMutator.toplevelProb);
|
|
}
|
|
}
|
|
},
|
|
Noop: {
|
|
enter(path) {
|
|
if (common.getSourceLoc(path.node)) {
|
|
thisMutator.loc = common.getSourceLoc(path.node);
|
|
}
|
|
},
|
|
},
|
|
ExpressionStatement: accessStatement,
|
|
IfStatement: accessStatement,
|
|
LabeledStatement: {
|
|
enter(path) {
|
|
// Apply an extra try-catch around the label of a loop, since we
|
|
// ignore the loop itself if it has a label.
|
|
if (babelTypes.isLoop(path.node.body)) {
|
|
thisMutator.callWithProb(path, replaceAndSkip);
|
|
}
|
|
},
|
|
exit(path) {
|
|
// Apply nested wrapping (is only executed if not skipped above).
|
|
if (babelTypes.isLoop(path.node.body)) {
|
|
replaceAndSkip(path);
|
|
}
|
|
},
|
|
},
|
|
// This covers {While|DoWhile|ForIn|ForOf|For}Statement.
|
|
Loop: accessStatement,
|
|
NewExpression: {
|
|
enter(path) {
|
|
thisMutator.callWithProb(path, replaceNewExpression);
|
|
},
|
|
exit(path) {
|
|
// Apply nested wrapping (is only executed if not skipped above).
|
|
replaceNewExpression(path);
|
|
}
|
|
},
|
|
SwitchStatement: accessStatement,
|
|
VariableDeclaration: {
|
|
enter(path) {
|
|
if (path.node.kind !== 'var' || babelTypes.isLoop(path.parent))
|
|
return;
|
|
thisMutator.callWithProb(path, replaceAndSkip);
|
|
},
|
|
exit(path) {
|
|
if (path.node.kind !== 'var' || babelTypes.isLoop(path.parent))
|
|
return;
|
|
// Apply nested wrapping (is only executed if not skipped above).
|
|
replaceAndSkip(path);
|
|
}
|
|
},
|
|
WithStatement: accessStatement,
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
AddTryCatchMutator: AddTryCatchMutator,
|
|
}
|