summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--scripts/fuzz_shell.js7
-rw-r--r--src/tools/execution-results.h47
-rw-r--r--src/tools/fuzzing.h10
-rw-r--r--src/tools/fuzzing/fuzzing.cpp66
-rw-r--r--test/lit/exec/fuzzing-api.wast39
-rw-r--r--test/lit/exec/host-limit.wast2
-rw-r--r--test/passes/fuzz_metrics_noprint.bin.txt52
-rw-r--r--test/passes/simplify-locals_all-features.txt6
-rw-r--r--test/passes/simplify-locals_all-features.wast6
-rw-r--r--test/passes/simplify-locals_all-features_disable-exception-handling.txt6
-rw-r--r--test/passes/simplify-locals_all-features_disable-exception-handling.wast6
-rw-r--r--test/passes/translate-to-fuzz_all-features_metrics_noprint.txt83
12 files changed, 215 insertions, 115 deletions
diff --git a/scripts/fuzz_shell.js b/scripts/fuzz_shell.js
index 3d29b197c..4cf3ba358 100644
--- a/scripts/fuzz_shell.js
+++ b/scripts/fuzz_shell.js
@@ -138,6 +138,7 @@ function logValue(x, y) {
var tempRet0;
var imports = {
'fuzzing-support': {
+ // Logging.
'log-i32': logValue,
'log-i64': logValue,
'log-f32': logValue,
@@ -147,7 +148,13 @@ var imports = {
// we could avoid running JS on code with SIMD in it, but it is useful to
// fuzz such code as much as we can.)
'log-v128': logValue,
+
+ // Throw an exception from JS.
+ 'throw': () => {
+ throw 'some JS error';
+ }
},
+ // Emscripten support.
'env': {
'setTempRet0': function(x) { tempRet0 = x },
'getTempRet0': function() { return tempRet0 },
diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h
index 920bb200a..d9fefc44e 100644
--- a/src/tools/execution-results.h
+++ b/src/tools/execution-results.h
@@ -27,6 +27,7 @@ using Loggings = std::vector<Literal>;
// Logs every relevant import call parameter.
struct LoggingExternalInterface : public ShellExternalInterface {
+private:
Loggings& loggings;
struct State {
@@ -37,30 +38,40 @@ struct LoggingExternalInterface : public ShellExternalInterface {
uint32_t tempRet0 = 0;
} state;
+public:
LoggingExternalInterface(Loggings& loggings) : loggings(loggings) {}
Literals callImport(Function* import, const Literals& arguments) override {
if (import->module == "fuzzing-support") {
- std::cout << "[LoggingExternalInterface logging";
- loggings.push_back(Literal()); // buffer with a None between calls
- for (auto argument : arguments) {
- if (argument.type == Type::i64) {
- // To avoid JS legalization changing logging results, treat a logging
- // of an i64 as two i32s (which is what legalization would turn us
- // into).
- auto low = Literal(int32_t(argument.getInteger()));
- auto high = Literal(int32_t(argument.getInteger() >> int32_t(32)));
- std::cout << ' ' << low;
- loggings.push_back(low);
- std::cout << ' ' << high;
- loggings.push_back(high);
- } else {
- std::cout << ' ' << argument;
- loggings.push_back(argument);
+ if (import->base.startsWith("log")) {
+ // This is a logging function like log-i32 or log-f64
+ std::cout << "[LoggingExternalInterface logging";
+ loggings.push_back(Literal()); // buffer with a None between calls
+ for (auto argument : arguments) {
+ if (argument.type == Type::i64) {
+ // To avoid JS legalization changing logging results, treat a
+ // logging of an i64 as two i32s (which is what legalization would
+ // turn us into).
+ auto low = Literal(int32_t(argument.getInteger()));
+ auto high = Literal(int32_t(argument.getInteger() >> int32_t(32)));
+ std::cout << ' ' << low;
+ loggings.push_back(low);
+ std::cout << ' ' << high;
+ loggings.push_back(high);
+ } else {
+ std::cout << ' ' << argument;
+ loggings.push_back(argument);
+ }
}
+ std::cout << "]\n";
+ return {};
+ } else if (import->base == "throw") {
+ // Throw something. We use a (hopefully) private name here.
+ auto payload = std::make_shared<ExnData>("__private", Literals{});
+ throwException(WasmException{Literal(payload)});
+ } else {
+ WASM_UNREACHABLE("unknown fuzzer import");
}
- std::cout << "]\n";
- return {};
} else if (import->module == ENV) {
if (import->base == "log_execution") {
std::cout << "[LoggingExternalInterface log-execution";
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index 92e997913..69fee656c 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -104,6 +104,10 @@ private:
Name funcrefTableName;
+ std::unordered_map<Type, Name> logImportNames;
+
+ Name throwImportName;
+
std::unordered_map<Type, std::vector<Name>> globalsByType;
std::unordered_map<Type, std::vector<Name>> mutableGlobalsByType;
std::unordered_map<Type, std::vector<Name>> immutableGlobalsByType;
@@ -220,12 +224,16 @@ private:
void finalizeTable();
void prepareHangLimitSupport();
void addHangLimitSupport();
+ // Imports that we call to log out values.
void addImportLoggingSupport();
+ // An import that we call to throw an exception from outside.
+ void addImportThrowingSupport();
void addHashMemorySupport();
// Special expression makers
Expression* makeHangLimitCheck();
- Expression* makeLogging();
+ Expression* makeImportLogging();
+ Expression* makeImportThrowing(Type type);
Expression* makeMemoryHashLogging();
// Function creation
diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp
index d3f52d3b0..b7d075dcc 100644
--- a/src/tools/fuzzing/fuzzing.cpp
+++ b/src/tools/fuzzing/fuzzing.cpp
@@ -176,9 +176,10 @@ void TranslateToFuzzReader::build() {
setupGlobals();
if (wasm.features.hasExceptionHandling()) {
setupTags();
+ addImportThrowingSupport();
}
- modifyInitialFunctions();
addImportLoggingSupport();
+ modifyInitialFunctions();
// keep adding functions until we run out of input
while (!random.finished()) {
auto* func = addFunction();
@@ -583,16 +584,31 @@ void TranslateToFuzzReader::addHangLimitSupport() {
void TranslateToFuzzReader::addImportLoggingSupport() {
for (auto type : loggableTypes) {
- auto* func = new Function;
- Name name = std::string("log-") + type.toString();
- func->name = name;
+ auto func = std::make_unique<Function>();
+ Name baseName = std::string("log-") + type.toString();
+ func->name = Names::getValidFunctionName(wasm, baseName);
+ logImportNames[type] = func->name;
func->module = "fuzzing-support";
- func->base = name;
+ func->base = baseName;
func->type = Signature(type, Type::none);
- wasm.addFunction(func);
+ wasm.addFunction(std::move(func));
}
}
+void TranslateToFuzzReader::addImportThrowingSupport() {
+ // Throw some kind of exception from JS.
+ // TODO: Send an index, which is which exported wasm Tag we should throw, or
+ // something not exported if out of bounds. First we must also export
+ // tags sometimes.
+ throwImportName = Names::getValidFunctionName(wasm, "throw");
+ auto func = std::make_unique<Function>();
+ func->name = throwImportName;
+ func->module = "fuzzing-support";
+ func->base = "throw";
+ func->type = Signature(Type::none, Type::none);
+ wasm.addFunction(std::move(func));
+}
+
void TranslateToFuzzReader::addHashMemorySupport() {
// Add memory hasher helper (for the hash, see hash.h). The function looks
// like:
@@ -692,21 +708,30 @@ Expression* TranslateToFuzzReader::makeHangLimitCheck() {
builder.makeConst(int32_t(1)))));
}
-Expression* TranslateToFuzzReader::makeLogging() {
+Expression* TranslateToFuzzReader::makeImportLogging() {
auto type = getLoggableType();
- return builder.makeCall(
- std::string("log-") + type.toString(), {make(type)}, Type::none);
+ return builder.makeCall(logImportNames[type], {make(type)}, Type::none);
+}
+
+Expression* TranslateToFuzzReader::makeImportThrowing(Type type) {
+ // We throw from the import, so this call appears to be none and not
+ // unreachable.
+ assert(type == Type::none);
+
+ // TODO: This and makeThrow should probably be rare, as they halt the program.
+ return builder.makeCall(throwImportName, {}, Type::none);
}
Expression* TranslateToFuzzReader::makeMemoryHashLogging() {
auto* hash = builder.makeCall(std::string("hashMemory"), {}, Type::i32);
- return builder.makeCall(std::string("log-i32"), {hash}, Type::none);
+ return builder.makeCall(logImportNames[Type::i32], {hash}, Type::none);
}
// TODO: return std::unique_ptr<Function>
Function* TranslateToFuzzReader::addFunction() {
LOGGING_PERCENT = upToSquared(100);
- auto* func = new Function;
+ auto allocation = std::make_unique<Function>();
+ auto* func = allocation.get();
func->name = Names::getValidFunctionName(wasm, "func");
FunctionCreationContext context(*this, func);
assert(funcContext->typeLocals.empty());
@@ -765,7 +790,7 @@ Function* TranslateToFuzzReader::addFunction() {
}
// Add hang limit checks after all other operations on the function body.
- wasm.addFunction(func);
+ wasm.addFunction(std::move(allocation));
// Export some functions, but not all (to allow inlining etc.). Try to export
// at least one, though, to keep each testcase interesting. Only functions
// with valid params and returns can be exported because the trap fuzzer
@@ -1215,10 +1240,13 @@ void TranslateToFuzzReader::modifyInitialFunctions() {
// the end (currently that is not needed atm, but it might in the future).
for (Index i = 0; i < wasm.functions.size(); i++) {
auto* func = wasm.functions[i].get();
+ // We can't allow extra imports, as the fuzzing infrastructure wouldn't
+ // know what to provide. Keep only our own fuzzer imports.
+ if (func->imported() && func->module == "fuzzing-support") {
+ continue;
+ }
FunctionCreationContext context(*this, func);
if (func->imported()) {
- // We can't allow extra imports, as the fuzzing infrastructure wouldn't
- // know what to provide.
func->module = func->base = Name();
func->body = make(func->getResults());
}
@@ -1261,10 +1289,9 @@ void TranslateToFuzzReader::dropToLog(Function* func) {
void visitDrop(Drop* curr) {
if (parent.isLoggableType(curr->value->type) && parent.oneIn(2)) {
- replaceCurrent(parent.builder.makeCall(std::string("log-") +
- curr->value->type.toString(),
- {curr->value},
- Type::none));
+ auto target = parent.logImportNames[curr->value->type];
+ replaceCurrent(
+ parent.builder.makeCall(target, {curr->value}, Type::none));
}
}
};
@@ -1430,7 +1457,7 @@ Expression* TranslateToFuzzReader::_makenone() {
auto choice = upTo(100);
if (choice < LOGGING_PERCENT) {
if (choice < LOGGING_PERCENT / 2) {
- return makeLogging();
+ return makeImportLogging();
} else {
return makeMemoryHashLogging();
}
@@ -1455,6 +1482,7 @@ Expression* TranslateToFuzzReader::_makenone() {
.add(FeatureSet::Atomics, &Self::makeAtomic)
.add(FeatureSet::ExceptionHandling, &Self::makeTry)
.add(FeatureSet::ExceptionHandling, &Self::makeTryTable)
+ .add(FeatureSet::ExceptionHandling, &Self::makeImportThrowing)
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeCallRef)
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeStructSet)
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeArraySet)
diff --git a/test/lit/exec/fuzzing-api.wast b/test/lit/exec/fuzzing-api.wast
new file mode 100644
index 000000000..d37d7ef4a
--- /dev/null
+++ b/test/lit/exec/fuzzing-api.wast
@@ -0,0 +1,39 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --output=fuzz-exec and should not be edited.
+
+;; RUN: wasm-opt %s -all --fuzz-exec -o /dev/null 2>&1 | filecheck %s
+
+;; Test the fuzzing-support module imports.
+
+(module
+ (import "fuzzing-support" "log-i32" (func $log-i32 (param i32)))
+ (import "fuzzing-support" "log-f64" (func $log-f64 (param f64)))
+
+ (import "fuzzing-support" "throw" (func $throw))
+
+ ;; CHECK: [fuzz-exec] calling logging
+ ;; CHECK-NEXT: [LoggingExternalInterface logging 42]
+ ;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
+ (func $logging (export "logging")
+ (call $log-i32
+ (i32.const 42)
+ )
+ (call $log-f64
+ (f64.const 3.14159)
+ )
+ )
+
+ ;; CHECK: [fuzz-exec] calling throwing
+ ;; CHECK-NEXT: [exception thrown: __private ()]
+ ;; CHECK-NEXT: warning: no passes specified, not doing any work
+ (func $throwing (export "throwing")
+ (call $throw)
+ )
+)
+;; CHECK: [fuzz-exec] calling logging
+;; CHECK-NEXT: [LoggingExternalInterface logging 42]
+;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
+
+;; CHECK: [fuzz-exec] calling throwing
+;; CHECK-NEXT: [exception thrown: __private ()]
+;; CHECK-NEXT: [fuzz-exec] comparing logging
+;; CHECK-NEXT: [fuzz-exec] comparing throwing
diff --git a/test/lit/exec/host-limit.wast b/test/lit/exec/host-limit.wast
index e64a47d8a..ba5087f69 100644
--- a/test/lit/exec/host-limit.wast
+++ b/test/lit/exec/host-limit.wast
@@ -9,7 +9,7 @@
(module
(type $type$0 (array i8))
- (import "fuzzing-support" "log" (func $log (param i32)))
+ (import "fuzzing-support" "log-i32" (func $log (param i32)))
(global $global (mut (ref $type$0)) (array.new_default $type$0
(i32.const -1)
diff --git a/test/passes/fuzz_metrics_noprint.bin.txt b/test/passes/fuzz_metrics_noprint.bin.txt
index 2f1719633..fab2ccc9d 100644
--- a/test/passes/fuzz_metrics_noprint.bin.txt
+++ b/test/passes/fuzz_metrics_noprint.bin.txt
@@ -1,35 +1,35 @@
Metrics
total
- [exports] : 23
- [funcs] : 34
+ [exports] : 50
+ [funcs] : 72
[globals] : 9
[imports] : 4
[memories] : 1
[memory-data] : 2
- [table-data] : 6
+ [table-data] : 25
[tables] : 1
[tags] : 0
- [total] : 4303
- [vars] : 100
- Binary : 355
- Block : 684
- Break : 149
- Call : 219
+ [total] : 4381
+ [vars] : 218
+ Binary : 335
+ Block : 725
+ Break : 120
+ Call : 210
CallIndirect : 23
- Const : 643
- Drop : 50
- GlobalGet : 367
- GlobalSet : 258
- If : 206
- Load : 78
- LocalGet : 339
- LocalSet : 236
- Loop : 93
- Nop : 41
- RefFunc : 6
- Return : 45
- Select : 41
- Store : 36
- Switch : 1
- Unary : 304
- Unreachable : 129
+ Const : 692
+ Drop : 64
+ GlobalGet : 391
+ GlobalSet : 298
+ If : 236
+ Load : 71
+ LocalGet : 285
+ LocalSet : 209
+ Loop : 76
+ Nop : 63
+ RefFunc : 25
+ Return : 60
+ Select : 23
+ Store : 29
+ Switch : 2
+ Unary : 293
+ Unreachable : 151
diff --git a/test/passes/simplify-locals_all-features.txt b/test/passes/simplify-locals_all-features.txt
index befbe33ef..9daee90a2 100644
--- a/test/passes/simplify-locals_all-features.txt
+++ b/test/passes/simplify-locals_all-features.txt
@@ -1220,9 +1220,9 @@
(type $9 (func (result f64)))
(type $10 (func (param i32 i32) (result f64)))
(type $11 (func (param i32 i32) (result i32)))
- (import "fuzzing-support" "log1" (func $fimport$0 (type $FUNCSIG$i) (result i32)))
- (import "fuzzing-support" "log2" (func $fimport$1 (type $4) (param i32)))
- (import "fuzzing-support" "log3" (func $fimport$2 (type $7) (param f32)))
+ (import "env" "get1" (func $fimport$0 (type $FUNCSIG$i) (result i32)))
+ (import "fuzzing-support" "log-i32" (func $fimport$1 (type $4) (param i32)))
+ (import "fuzzing-support" "log-f32" (func $fimport$2 (type $7) (param f32)))
(global $global$0 (mut i32) (i32.const 10))
(memory $0 256 256 shared)
(func $nonatomics (type $FUNCSIG$i) (result i32)
diff --git a/test/passes/simplify-locals_all-features.wast b/test/passes/simplify-locals_all-features.wast
index 47bb7f1b5..0c296e378 100644
--- a/test/passes/simplify-locals_all-features.wast
+++ b/test/passes/simplify-locals_all-features.wast
@@ -1194,9 +1194,9 @@
(type $4 (func (param i32)))
(type $5 (func (param i32) (result i32)))
(type $6 (func (param i32 i32 i32 i32 i32 i32)))
- (import "fuzzing-support" "log1" (func $fimport$0 (result i32)))
- (import "fuzzing-support" "log2" (func $fimport$1 (param i32)))
- (import "fuzzing-support" "log3" (func $fimport$2 (param f32)))
+ (import "env" "get1" (func $fimport$0 (result i32)))
+ (import "fuzzing-support" "log-i32" (func $fimport$1 (param i32)))
+ (import "fuzzing-support" "log-f32" (func $fimport$2 (param f32)))
(memory 256 256 shared)
(global $global$0 (mut i32) (i32.const 10))
(func $nonatomics (result i32) ;; loads are reordered
diff --git a/test/passes/simplify-locals_all-features_disable-exception-handling.txt b/test/passes/simplify-locals_all-features_disable-exception-handling.txt
index 111223c49..923562867 100644
--- a/test/passes/simplify-locals_all-features_disable-exception-handling.txt
+++ b/test/passes/simplify-locals_all-features_disable-exception-handling.txt
@@ -1214,9 +1214,9 @@
(type $9 (func (result f64)))
(type $10 (func (param i32 i32) (result f64)))
(type $11 (func (param i32 i32) (result i32)))
- (import "fuzzing-support" "log1" (func $fimport$0 (type $FUNCSIG$i) (result i32)))
- (import "fuzzing-support" "log2" (func $fimport$1 (type $4) (param i32)))
- (import "fuzzing-support" "log3" (func $fimport$2 (type $7) (param f32)))
+ (import "env" "get1" (func $fimport$0 (type $FUNCSIG$i) (result i32)))
+ (import "fuzzing-support" "log-i32" (func $fimport$1 (type $4) (param i32)))
+ (import "fuzzing-support" "log-f32" (func $fimport$2 (type $7) (param f32)))
(global $global$0 (mut i32) (i32.const 10))
(memory $0 256 256 shared)
(func $nonatomics (type $FUNCSIG$i) (result i32)
diff --git a/test/passes/simplify-locals_all-features_disable-exception-handling.wast b/test/passes/simplify-locals_all-features_disable-exception-handling.wast
index 0fc110696..44c31e344 100644
--- a/test/passes/simplify-locals_all-features_disable-exception-handling.wast
+++ b/test/passes/simplify-locals_all-features_disable-exception-handling.wast
@@ -1194,9 +1194,9 @@
(type $4 (func (param i32)))
(type $5 (func (param i32) (result i32)))
(type $6 (func (param i32 i32 i32 i32 i32 i32)))
- (import "fuzzing-support" "log1" (func $fimport$0 (result i32)))
- (import "fuzzing-support" "log2" (func $fimport$1 (param i32)))
- (import "fuzzing-support" "log3" (func $fimport$2 (param f32)))
+ (import "env" "get1" (func $fimport$0 (result i32)))
+ (import "fuzzing-support" "log-i32" (func $fimport$1 (param i32)))
+ (import "fuzzing-support" "log-f32" (func $fimport$2 (param f32)))
(memory 256 256 shared)
(global $global$0 (mut i32) (i32.const 10))
(func $nonatomics (result i32) ;; loads are reordered
diff --git a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
index 3cc841610..c17c9cd1e 100644
--- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
+++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
@@ -1,51 +1,58 @@
Metrics
total
- [exports] : 4
- [funcs] : 7
+ [exports] : 3
+ [funcs] : 4
[globals] : 26
- [imports] : 5
+ [imports] : 6
[memories] : 1
[memory-data] : 20
- [table-data] : 2
+ [table-data] : 0
[tables] : 1
[tags] : 2
- [total] : 510
- [vars] : 15
- ArrayNew : 14
- ArrayNewFixed : 2
- AtomicFence : 1
- Binary : 64
- Block : 45
- Break : 2
- Call : 8
+ [total] : 665
+ [vars] : 20
+ ArrayGet : 2
+ ArrayLen : 2
+ ArrayNew : 15
+ ArrayNewFixed : 3
+ AtomicCmpxchg : 1
+ AtomicRMW : 1
+ Binary : 71
+ Block : 63
+ BrOn : 4
+ Break : 7
+ Call : 18
CallRef : 2
- Const : 127
- Drop : 5
- GlobalGet : 28
+ Const : 142
+ Drop : 10
+ GlobalGet : 33
GlobalSet : 16
- If : 12
- Load : 18
- LocalGet : 46
- LocalSet : 29
- Loop : 3
- MemoryCopy : 1
+ I31Get : 1
+ If : 21
+ Load : 20
+ LocalGet : 61
+ LocalSet : 52
+ Loop : 6
+ MemoryFill : 1
MemoryInit : 1
- Nop : 4
- Pop : 2
- RefCast : 1
- RefFunc : 7
- RefNull : 6
- Return : 9
+ Nop : 7
+ Pop : 1
+ RefAs : 11
+ RefEq : 1
+ RefFunc : 3
+ RefI31 : 1
+ RefNull : 14
+ Return : 5
SIMDExtract : 1
- Store : 1
- StringConst : 7
- StringEq : 1
- StringMeasure : 1
- StringSliceWTF : 1
- StructNew : 14
- Try : 2
+ Select : 2
+ StringConst : 5
+ StringEncode : 1
+ StructGet : 4
+ StructNew : 16
+ StructSet : 1
+ Try : 1
TryTable : 1
- TupleExtract : 3
+ TupleExtract : 2
TupleMake : 6
- Unary : 9
- Unreachable : 10
+ Unary : 20
+ Unreachable : 9