Commit 3f88d2da authored by Michael Achenbach's avatar Michael Achenbach Committed by Commit Bot

[foozzie] Add tests and fix mocks

This adds tests for the mock logic used in differential fuzzing. The
tests uncovered a couple of issues in the mock files that are also
fixed.

This also does some minor code clean up in the mock code.

Bug: chromium:1044942
Change-Id: I5b67f70f8b104bb681548f742ab863395a88360f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2043843
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Reviewed-by: 's avatarMathias Bynens <mathias@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66304}
parent b12ba06e
......@@ -12,6 +12,9 @@ group("v8_mjsunit") {
data = [
"./",
"../../tools/clusterfuzz/v8_mock.js",
"../../tools/clusterfuzz/v8_mock_archs.js",
"../../tools/clusterfuzz/v8_mock_webassembly.js",
"../../tools/codemap.js",
"../../tools/consarray.js",
"../../tools/csvparser.js",
......
// 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.
// Flags: --allow-natives-syntax
// Files: tools/clusterfuzz/v8_mock.js
// Test foozzie mocks for differential fuzzing.
// Deterministic Math.random.
assertEquals(0.1, Math.random());
assertEquals(0.2, Math.random());
assertEquals(0.3, Math.random());
// Deterministic date.
assertEquals(1477662728698, Date.now());
assertEquals(1477662728701, Date.now());
assertEquals(1477662728705, new Date().getTime());
// Deterministic arguments in constructor keep working.
assertEquals(819134640000,
new Date('December 17, 1995 03:24:00 GMT+1000').getTime());
// Dummy performance methods.
assertEquals(1.2, performance.now());
assertEquals([], performance.measureMemory());
// Worker messages follow a predefined deterministic pattern.
const worker = new Worker(``, {type: 'string'});
assertEquals(0, worker.getMessage());
assertEquals(-1, worker.getMessage());
// NaN patterns in typed arrays are mocked out. Test that we get no
// difference between unoptimized and optimized code.
function testSameOptimized(pattern, create_fun) {
const expected = new Uint32Array(pattern);
%PrepareFunctionForOptimization(create_fun);
assertEquals(expected, create_fun());
%OptimizeFunctionOnNextCall(create_fun);
assertEquals(expected, create_fun());
}
function testArrayType(arrayType, pattern) {
// Test passing NaNs to constructor with array.
let create = function() {
return new Uint32Array(new arrayType([-NaN]).buffer);
};
testSameOptimized(pattern, create);
// Test passing NaNs to constructor with iterator.
create = function() {
const iter = function*(){ yield* [-NaN]; }();
return new Uint32Array(new arrayType(iter).buffer);
};
testSameOptimized(pattern, create);
// Test setting NaN property.
create = function() {
const arr = new arrayType(1);
arr[0] = -NaN;
return new Uint32Array(arr.buffer);
};
// Test passing NaN using set.
testSameOptimized(pattern, create);
create = function() {
const arr = new arrayType(1);
arr.set([-NaN], 0);
return new Uint32Array(arr.buffer);
};
testSameOptimized(pattern, create);
}
testArrayType(Float32Array, [1065353216]);
testArrayType(Float64Array, [0, 1072693248]);
// 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.
// Flags: --allow-natives-syntax
// Files: tools/clusterfuzz/v8_mock.js
// Files: tools/clusterfuzz/v8_mock_archs.js
// Test foozzie architecture-specific mocks for differential fuzzing.
// Max typed array length is mocked and restricted to 1MiB buffer.
const maxBytes = 1048576;
// The maximum also holds for array buffer and shared array buffer.
assertEquals(maxBytes, new ArrayBuffer(maxBytes + 1).byteLength);
assertEquals(maxBytes, new SharedArrayBuffer(maxBytes + 1).byteLength);
function testArrayType(type) {
const name = type.name;
const bytesPerElem = type.BYTES_PER_ELEMENT;
const maxElem = maxBytes / bytesPerElem;
function testLength(expectedLength, arr) {
const expectedBytes = expectedLength * bytesPerElem;
assertEquals(expectedBytes, arr.byteLength, name);
assertEquals(expectedLength, arr.length, name);
}
// Test length argument in constructor.
testLength(maxElem - 1, new type(maxElem - 1));
testLength(maxElem, new type(maxElem));
testLength(maxElem, new type(maxElem + 1));
// Test buffer argument in constructor.
// Unaligned offsets don't throw.
const buffer = new ArrayBuffer(maxBytes);
new type(buffer, 1);
new type(buffer, 3);
// Offsets work or are capped.
function bytes(elements) {
return elements * bytesPerElem;
}
testLength(maxElem, new type(buffer, 0));
testLength(maxElem - 1, new type(buffer, bytes(1)));
testLength(1, new type(buffer, bytes(maxElem - 1)));
testLength(0, new type(buffer, bytes(maxElem)));
testLength(0, new type(buffer, bytes(maxElem + 1)));
// Offset and length work or are capped.
testLength(1, new type(buffer, 0, 1));
testLength(1, new type(buffer, bytesPerElem, 1));
testLength(maxElem - 2, new type(buffer, bytes(1), maxElem - 2));
testLength(maxElem - 1, new type(buffer, bytes(1), maxElem - 1));
testLength(maxElem - 1, new type(buffer, bytes(1), maxElem));
testLength(0, new type(buffer, bytes(maxElem - 1), 0));
testLength(1, new type(buffer, bytes(maxElem - 1), 1));
testLength(1, new type(buffer, bytes(maxElem - 1), 2));
// Insertion with "set" works or is capped.
let set0 = 0;
let set1 = 1;
if (name.startsWith("Big")) {
set0 = 0n;
set1 = 1n;
}
arr = new type(4);
arr.set([set1], 1);
assertEquals(new type([set0, set1, set0, set0]), arr, name);
arr.set([set1, set1], 3); // Capped to 2.
assertEquals(new type([set0, set1, set1, set1]), arr, name);
}
testArrayType(Int8Array);
testArrayType(Uint8Array);
testArrayType(Uint8ClampedArray);
testArrayType(Int16Array);
testArrayType(Uint16Array);
testArrayType(Int32Array);
testArrayType(Uint32Array);
testArrayType(BigInt64Array);
testArrayType(BigUint64Array);
testArrayType(Float32Array);
testArrayType(Float64Array);
// 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.
// Flags: --allow-natives-syntax
// Files: tools/clusterfuzz/v8_mock.js
// Files: tools/clusterfuzz/v8_mock_webassembly.js
// Test foozzie webassembly-specfific mocks for differential fuzzing.
// No reference errors when accessing WebAssembly.
WebAssembly[0];
WebAssembly[" "];
WebAssembly.foo;
WebAssembly.foo();
WebAssembly.foo().bar;
WebAssembly.foo().bar();
WebAssembly.foo().bar[0];
......@@ -13,8 +13,8 @@
var prettyPrinted = function prettyPrinted(msg) { return msg; };
// Mock Math.random.
(function () {
var index = 0
(function() {
let index = 0
Math.random = function() {
index = (index + 1) % 10;
return index / 10.0;
......@@ -22,55 +22,42 @@ var prettyPrinted = function prettyPrinted(msg) { return msg; };
})();
// Mock Date.
(function () {
var index = 0
var mockDate = 1477662728696
var mockDateNow = function() {
index = (index + 1) % 10
mockDate = mockDate + index + 1
return mockDate
(function() {
let index = 0;
let mockDate = 1477662728696;
const mockDateNow = function() {
index = (index + 1) % 10;
mockDate = mockDate + index + 1;
return mockDate;
}
var origDate = Date;
var constructDate = function(args) {
if (args.length == 1) {
var result = new origDate(args[0]);
} else if (args.length == 2) {
var result = new origDate(args[0], args[1]);
} else if (args.length == 3) {
var result = new origDate(args[0], args[1], args[2]);
} else if (args.length == 4) {
var result = new origDate(args[0], args[1], args[2], args[3]);
} else if (args.length == 5) {
var result = new origDate(args[0], args[1], args[2], args[3], args[4]);
} else if (args.length == 6) {
var result = new origDate(
args[0], args[1], args[2], args[3], args[4], args[5]);
} else if (args.length >= 7) {
var result = new origDate(
args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
const origDate = Date;
const constructDate = function(args) {
let result;
if (args.length) {
result = new origDate(...args);
} else {
var result = new origDate(mockDateNow());
result = new origDate(mockDateNow());
}
result.constructor = function(...args) { return constructDate(args); }
Object.defineProperty(
result, "constructor", { configurable: false, writable: false });
return result
return result;
}
var handler = {
apply: function (target, thisArg, args) {
return constructDate(args)
apply: function(target, thisArg, args) {
return constructDate(args);
},
construct: function (target, args, newTarget) {
return constructDate(args)
construct: function(target, args, newTarget) {
return constructDate(args);
},
get: function(target, property, receiver) {
if (property == "now") {
return mockDateNow;
}
if (property == "prototype") {
return origDate.prototype
return origDate.prototype;
}
},
}
......@@ -79,42 +66,65 @@ var prettyPrinted = function prettyPrinted(msg) { return msg; };
})();
// Mock performance methods.
performance.now = function () { return 1.2; }
performance.measureMemory = function () { return []; }
performance.now = function() { return 1.2; };
performance.measureMemory = function() { return []; };
// Mock readline so that test cases don't hang.
readline = function () { return "foo"; }
readline = function() { return "foo"; };
// Mock stack traces.
Error.prepareStackTrace = function (error, structuredStackTrace) {
Error.prepareStackTrace = function(error, structuredStackTrace) {
return "";
};
Object.defineProperty(
Error, 'prepareStackTrace', { configurable: false, writable: false });
// Mock buffer access in float typed arrays because of varying NaN patterns.
(function () {
(function() {
const origIsNaN = isNaN;
const mock = function(arrayType) {
const deNaNify = function(value) { return origIsNaN(value) ? 1 : value; };
const mock = function(type) {
// Remove NaN values from parameters to "set" function.
const set = type.prototype.set;
type.prototype.set = function(array, offset) {
if (Array.isArray(array)) {
array = array.map(deNaNify);
}
set.apply(this, [array, offset]);
};
const handler = {
// Remove NaN values from parameters to constructor.
construct: function(target, args) {
for (let i = 0; i < args.length; i++) {
if (origIsNaN(args[i])) {
args[i] = 1;
if (args[i] != null &&
typeof args[i][Symbol.iterator] === 'function') {
// Consume iterators.
args[i] = Array.from(args[i]);
}
if (Array.isArray(args[i])) {
args[i] = args[i].map(deNaNify);
}
}
const obj = new (
Function.prototype.bind.call(arrayType, null, ...args));
Function.prototype.bind.call(type, null, ...args));
return new Proxy(obj, {
get: function(x, prop) {
if (typeof x[prop] == "function")
return x[prop].bind(obj)
return x[prop].bind(obj);
return x[prop];
},
// Remove NaN values that get assigned.
set: function(target, prop, value, receiver) {
target[prop] = deNaNify(value);
return value;
}
});
},
};
return new Proxy(arrayType, handler);
return new Proxy(type, handler);
}
Float32Array = mock(Float32Array);
......@@ -122,11 +132,11 @@ Object.defineProperty(
})();
// Mock Worker.
(function () {
var index = 0;
(function() {
let index = 0;
// TODO(machenbach): Randomize this for each test case, but keep stable
// during comparison. Also data and random above.
var workerMessages = [
const workerMessages = [
undefined, 0, -1, "", "foo", 42, [], {}, [0], {"x": 0}
];
Worker = function(code){
......
......@@ -10,23 +10,40 @@
// This file is loaded before each correctness test cases and won't get
// minimized.
// Mock maximum typed-array length and limit to 1MiB.
(function () {
// Mock maximum typed-array buffer and limit to 1MiB. Otherwise we might
// get range errors. We ignore those by crashing, but that reduces coverage,
// hence, let's reduce the range-error rate.
(function() {
// Math.min might be manipulated in test cases.
let min = Math.min;
let mock = function(arrayType) {
let handler = {
const min = Math.min;
const maxBytes = 1048576;
const mock = function(type) {
const maxLength = maxBytes / (type.BYTES_PER_ELEMENT || 1);
const handler = {
construct: function(target, args) {
for (let i = 0; i < args.length; i++) {
if (typeof args[i] != "object") {
args[i] = min(1048576, args[i]);
if (args[0] && typeof args[0] != "object") {
// Length used as first argument.
args[0] = min(maxLength, Number(args[0]));
} else if (args[0] instanceof ArrayBuffer && args.length > 1) {
// Buffer used as first argument.
const buffer = args[0];
args[1] = Number(args[1]);
// Ensure offset is multiple of bytes per element.
args[1] = args[1] - (args[1] % type.BYTES_PER_ELEMENT);
// Limit offset to length of buffer.
args[1] = min(args[1], buffer.byteLength || 0);
if (args.length > 2) {
// If also length is given, limit it to the maximum that's possible
// given buffer and offset.
const maxBytesLeft = buffer.byteLength - args[1];
const maxLengthLeft = maxBytesLeft / type.BYTES_PER_ELEMENT;
args[2] = min(Number(args[2]), maxLengthLeft);
}
}
return new (
Function.prototype.bind.apply(arrayType, [null].concat(args)));
return new (Function.prototype.bind.apply(type, [null].concat(args)));
},
};
return new Proxy(arrayType, handler);
return new Proxy(type, handler);
}
ArrayBuffer = mock(ArrayBuffer);
......@@ -44,9 +61,11 @@
Float64Array = mock(Float64Array);
})();
// Mock typed array set function and limit maximum offset to 1MiB.
(function () {
let typedArrayTypes = [
// Mock typed array set function and cap offset to not throw a range error.
(function() {
// Math.min might be manipulated in test cases.
const min = Math.min;
const types = [
Int8Array,
Uint8Array,
Uint8ClampedArray,
......@@ -59,10 +78,14 @@
Float32Array,
Float64Array,
];
for (let typedArrayType of typedArrayTypes) {
let set = typedArrayType.prototype.set
typedArrayType.prototype.set = function(array, offset) {
set.apply(this, [array, offset > 1048576 ? 1048576 : offset])
for (const type of types) {
const set = type.prototype.set;
type.prototype.set = function(array, offset) {
if (Array.isArray(array)) {
offset = Number(offset);
offset = min(offset, this.length - array.length);
}
set.call(this, array, offset);
};
}
})();
......@@ -4,7 +4,7 @@
// This mocks out the WebAssembly object with a permissive dummy.
(function () {
(function() {
const handler = {
get: function(x, prop) {
if (prop == Symbol.toPrimitive) {
......@@ -13,6 +13,6 @@
return dummy;
},
};
const dummy = new Proxy(function () { return dummy; }, handler);
const dummy = new Proxy(function() { return dummy; }, handler);
WebAssembly = dummy;
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment