c68c06996a
See for example: * https://9to5google.com/2020/06/12/google-android-chrome-blacklist-blocklist-more-inclusive/ * https://thenextweb.com/dd/2020/06/15/github-plans-to-replace-racially-insensitive-terms-like-master-and-whitelist/ Change-Id: Id40e8b20f8dcab6db291159c33db5197aedf9fae Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/151304 Reviewed-by: Ben Konyi <bkonyi@google.com> Commit-Queue: Phil Quitslund <pquitslund@google.com>
353 lines
9.4 KiB
Dart
353 lines
9.4 KiB
Dart
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
// Random-walk fuzzer for the Dart VM.
|
|
//
|
|
// Start with all the classes and libraries and various interesting values.
|
|
// Repeatedly choose one as a receiver, construct a message it is likely to
|
|
// understand, send the message, and add the result.
|
|
//
|
|
// Intentionally not run on the build bots because through dart:io it could
|
|
// trash their setups.
|
|
|
|
library fuzzer;
|
|
|
|
import 'dart:io';
|
|
import 'dart:math';
|
|
import 'dart:mirrors';
|
|
import 'dart:typed_data';
|
|
|
|
var excludeList = [
|
|
'dart.io.exit',
|
|
'dart.io.exitCode',
|
|
'dart.io.sleep',
|
|
'dart.io.Process.killPid',
|
|
];
|
|
|
|
final bool trace = false;
|
|
|
|
void main(List<String> args) {
|
|
int seed;
|
|
if (args.length == 1) {
|
|
seed = int.parse(args[0]);
|
|
} else {
|
|
// Dart's built-in random number generator doesn't provide access to the
|
|
// seed when it is chosen by the implementation. We need to be able to
|
|
// report this seed to make runs of the fuzzer reproducible, so we create
|
|
// the seed ourselves.
|
|
|
|
// When running on many machines in parallel, the current time alone
|
|
// is a poor choice of seed.
|
|
seed = 0;
|
|
try {
|
|
var f = new File("/dev/urandom").openSync();
|
|
seed = (seed << 8) | f.readByteSync();
|
|
seed = (seed << 8) | f.readByteSync();
|
|
seed = (seed << 8) | f.readByteSync();
|
|
seed = (seed << 8) | f.readByteSync();
|
|
f.close();
|
|
} catch (e) {
|
|
print("Failed to read from /dev/urandom: $e");
|
|
}
|
|
|
|
seed ^= new DateTime.now().millisecondsSinceEpoch;
|
|
seed &= 0xFFFFFFFF;
|
|
}
|
|
random = new Random(seed);
|
|
|
|
// Information needed to reproduce this run.
|
|
print("Dart VM fuzzer");
|
|
print("Executable: ${Platform.executable}");
|
|
print("Arguments: ${Platform.executableArguments}");
|
|
print("Version: ${Platform.version}");
|
|
print("Seed: ${seed}");
|
|
print("------------------------------------------");
|
|
|
|
setupInterestingValues();
|
|
setupClasses();
|
|
|
|
// Bound the number of steps in our random walk so that if any issue is found
|
|
// it can be reproduced without having to wait too long.
|
|
for (int i = 0; i < 100000; i++) {
|
|
fuzz(randomElementOf(candidateReceivers));
|
|
if (maybe(0.01)) garbageCollect();
|
|
}
|
|
}
|
|
|
|
Random random;
|
|
|
|
bool maybe(probability) => random.nextDouble() < probability;
|
|
|
|
randomElementOf(list) {
|
|
return list.length == 0 ? null : list[random.nextInt(list.length)];
|
|
}
|
|
|
|
class Candidate<T> {
|
|
Candidate origin;
|
|
String message;
|
|
T mirror;
|
|
trace() {
|
|
if (origin == null) {
|
|
print("");
|
|
} else {
|
|
origin.trace();
|
|
print(" $message");
|
|
}
|
|
print(mirror);
|
|
}
|
|
}
|
|
|
|
List<Candidate<ObjectMirror>> candidateReceivers =
|
|
new List<Candidate<ObjectMirror>>();
|
|
List<Candidate<InstanceMirror>> candidateArguments =
|
|
new List<Candidate<InstanceMirror>>();
|
|
|
|
void addInstance(var instance) {
|
|
addInstanceMirror(reflect(instance));
|
|
}
|
|
|
|
void addInstanceMirror(InstanceMirror mirror,
|
|
[Candidate origin, String message]) {
|
|
var c = new Candidate<InstanceMirror>();
|
|
c.mirror = mirror;
|
|
c.origin = origin;
|
|
c.message = message;
|
|
|
|
candidateReceivers.add(c);
|
|
candidateArguments.add(c);
|
|
}
|
|
|
|
void addObjectMirror(ObjectMirror mirror) {
|
|
var c = new Candidate<ObjectMirror>();
|
|
c.mirror = mirror;
|
|
c.origin = null;
|
|
c.message = null;
|
|
|
|
candidateReceivers.add(c);
|
|
}
|
|
|
|
void setupInterestingValues() {
|
|
addInstance(null);
|
|
addInstance(true);
|
|
addInstance(false);
|
|
|
|
addInstance([]);
|
|
addInstance(const []);
|
|
addInstance({});
|
|
addInstance(const {});
|
|
|
|
addInstance(() => null);
|
|
|
|
addInstance(-1);
|
|
addInstance(0);
|
|
addInstance(1);
|
|
addInstance(2);
|
|
|
|
addInstance(1 << 31);
|
|
addInstance(1 << 31 + 1);
|
|
addInstance(1 << 31 - 1);
|
|
|
|
addInstance(1 << 32);
|
|
addInstance(1 << 32 + 1);
|
|
addInstance(1 << 32 - 1);
|
|
|
|
addInstance(1 << 63);
|
|
addInstance(1 << 63 + 1);
|
|
addInstance(1 << 63 - 1);
|
|
|
|
addInstance(1 << 64);
|
|
addInstance(1 << 64 + 1);
|
|
addInstance(1 << 64 - 1);
|
|
|
|
addInstance(-1.0);
|
|
addInstance(0.0);
|
|
addInstance(1.0);
|
|
addInstance(2.0);
|
|
addInstance(double.nan);
|
|
addInstance(double.infinity);
|
|
addInstance(double.negativeInfinity);
|
|
addInstance(double.minPositive);
|
|
addInstance(double.maxFinite);
|
|
|
|
addInstance("foo"); // ASCII string
|
|
addInstance("blåbærgrød"); // Latin1 string
|
|
addInstance("Îñţérñåţîöñåļîžåţîờñ"); // Unicode string
|
|
addInstance("𝄞"); // Surrogate pairs
|
|
addInstance("𝄞"[0]); // Surrogate pairs
|
|
addInstance("𝄞"[1]); // Surrogate pairs
|
|
addInstance("\u{0}"); // Non-printing character
|
|
addInstance("\u{1}"); // Non-printing character
|
|
addInstance("f\u{0}oo"); // Internal NUL
|
|
addInstance("blåbæ\u{0}rgrød"); // Internal NUL
|
|
addInstance("Îñţérñåţîö\u{0}ñåļîžåţîờñ"); // Internal NUL
|
|
addInstance("\u{0}𝄞"); // Internal NUL
|
|
|
|
for (int len = 0; len < 8; len++) {
|
|
addInstance(fillInt(new Int8List(len)));
|
|
addInstance(fillInt(new Int16List(len)));
|
|
addInstance(fillInt(new Int32List(len)));
|
|
addInstance(fillInt(new Int64List(len)));
|
|
addInstance(fillInt(new Uint8List(len)));
|
|
addInstance(fillInt(new Uint16List(len)));
|
|
addInstance(fillInt(new Uint32List(len)));
|
|
addInstance(fillInt(new Uint64List(len)));
|
|
addInstance(fillFloat(new Float32List(len)));
|
|
addInstance(fillFloat(new Float64List(len)));
|
|
}
|
|
|
|
randomInstance(ignore) {
|
|
return randomElementOf(candidateArguments).mirror.reflectee;
|
|
}
|
|
|
|
for (int len = 0; len < 8; len++) {
|
|
addInstance(new List.generate(len, randomInstance));
|
|
}
|
|
}
|
|
|
|
void fillInt(TypedData d) {
|
|
for (var i = 0; i < d.length; i++) {
|
|
d[i] = random.nextInt(0xFFFFFFFF);
|
|
}
|
|
}
|
|
|
|
void fillFloat(TypedData d) {
|
|
for (var i = 0; i < d.length; i++) {
|
|
d[i] = random.nextDouble();
|
|
}
|
|
}
|
|
|
|
void setupClasses() {
|
|
currentMirrorSystem().libraries.values.forEach((lib) {
|
|
if (lib.simpleName == #fuzzer) return; // Don't recurse.
|
|
addObjectMirror(lib);
|
|
lib.declarations.values.forEach((decl) {
|
|
if (decl is ClassMirror) {
|
|
addObjectMirror(decl);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
MethodMirror randomMethodOf(receiver) {
|
|
if (receiver is ClassMirror) {
|
|
return randomElementOf(receiver.declarations.values
|
|
.where((d) => d is MethodMirror && d.isStatic)
|
|
.toList());
|
|
} else if (receiver is LibraryMirror) {
|
|
return randomElementOf(
|
|
receiver.declarations.values.where((d) => d is MethodMirror).toList());
|
|
} else if (receiver is InstanceMirror) {
|
|
var methods = [];
|
|
var cls = receiver.type;
|
|
while (cls != reflectClass(Object)) {
|
|
cls.declarations.values.forEach((d) {
|
|
if (d is MethodMirror && !d.isStatic) methods.add(d);
|
|
});
|
|
cls = cls.superclass;
|
|
}
|
|
return randomElementOf(methods);
|
|
}
|
|
throw new Error("UNREACHABLE");
|
|
}
|
|
|
|
String prettyMessageName(receiver, method) {
|
|
var r = "?", m = "?";
|
|
if (receiver is InstanceMirror) {
|
|
r = MirrorSystem.getName(receiver.type.simpleName);
|
|
} else if (receiver is ClassMirror) {
|
|
r = MirrorSystem.getName(receiver.simpleName);
|
|
r = "$r class";
|
|
} else if (receiver is LibraryMirror) {
|
|
r = MirrorSystem.getName(receiver.simpleName);
|
|
r = "$r lib";
|
|
}
|
|
m = MirrorSystem.getName(method.simpleName);
|
|
return "$r>>#$m";
|
|
}
|
|
|
|
void fuzz(Candidate c) {
|
|
ObjectMirror receiver = c.mirror;
|
|
MethodMirror method = randomMethodOf(receiver);
|
|
if (method == null) return;
|
|
if (excludeList.contains(MirrorSystem.getName(method.qualifiedName))) return;
|
|
|
|
List positional = randomPositionalArgumentsFor(method);
|
|
Map named = randomNamedArgumentsFor(method);
|
|
InstanceMirror result;
|
|
|
|
String message = prettyMessageName(receiver, method);
|
|
if (trace) {
|
|
c.trace();
|
|
print(message);
|
|
}
|
|
|
|
if (method.isConstructor) {
|
|
try {
|
|
result = receiver.newInstance(method.simpleName, positional, named);
|
|
} catch (e) {}
|
|
} else if (method.isRegularMethod) {
|
|
try {
|
|
result = receiver.invoke(method.simpleName, positional, named);
|
|
} catch (e) {}
|
|
} else if (method.isGetter) {
|
|
try {
|
|
result = receiver.getField(method.simpleName);
|
|
} catch (e) {}
|
|
} else if (method.isSetter) {
|
|
try {
|
|
result = receiver.setField(method.simpleName, positional[0]);
|
|
} catch (e) {}
|
|
}
|
|
|
|
if (result != null) {
|
|
addInstanceMirror(result, c, message);
|
|
}
|
|
}
|
|
|
|
InstanceMirror randomArgumentWithBias(TypeMirror bias) {
|
|
if (maybe(0.75)) {
|
|
for (var candidate in candidateArguments) {
|
|
if (candidate.mirror.type.isAssignableTo(bias)) {
|
|
return candidate.mirror;
|
|
}
|
|
}
|
|
}
|
|
return randomElementOf(candidateArguments).mirror;
|
|
}
|
|
|
|
List randomPositionalArgumentsFor(MethodMirror method) {
|
|
var result = [];
|
|
for (int i = 0; i < method.parameters.length; i++) {
|
|
ParameterMirror p = method.parameters[i];
|
|
if (!p.isNamed && (!p.isOptional || maybe(0.5))) {
|
|
result.add(randomArgumentWithBias(p.type));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Map randomNamedArgumentsFor(MethodMirror method) {
|
|
var result = {};
|
|
for (int i = 0; i < method.parameters.length; i++) {
|
|
ParameterMirror p = method.parameters[i];
|
|
if (p.isNamed && maybe(0.5)) {
|
|
result[p.simpleName] = randomArgumentWithBias(p.type);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void garbageCollect() {
|
|
// Chain a bunch of moderately sized arrays, then let go of them. Using a
|
|
// moderate size avoids our allocations going directly to a large object
|
|
// page in old space.
|
|
var n;
|
|
for (int i = 0; i < 2048; i++) {
|
|
var m = new List(512);
|
|
m[0] = n;
|
|
n = m;
|
|
}
|
|
}
|