summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2018-11-30 09:54:57 -0800
committerGitHub <noreply@github.com>2018-11-30 09:54:57 -0800
commit0e068c386ef1588c09e57a4d081be626d83bc31c (patch)
tree0a432d17fd6a26b6cce092ab81558aeb4270da77 /src
parentf16a7605b8226502062c6ca24323d431669c90ec (diff)
downloadbinaryen-0e068c386ef1588c09e57a4d081be626d83bc31c.tar.gz
binaryen-0e068c386ef1588c09e57a4d081be626d83bc31c.tar.bz2
binaryen-0e068c386ef1588c09e57a4d081be626d83bc31c.zip
Fuzzing: log values during execution (#1779)
Before we just looked at function return values when looking for differences before and after running some passes, while fuzzing. This adds logging of values during execution, which can represent control flow, monitor locals, etc., giving a lot more opportunities for the fuzzer to find problems. Also: * Clean up the sigToFunctionType function, which allocated a struct and returned it. This makes it safer by returning the struct by value, which is also easier to use in this PR. * Fix printing of imported function calls without a function type - turns out we always generate function types in loading, so we didn't notice this was broken, but this new fuzzer feature hit it.
Diffstat (limited to 'src')
-rw-r--r--src/asm_v_wasm.h2
-rw-r--r--src/asmjs/asm_v_wasm.cpp8
-rw-r--r--src/passes/Print.cpp3
-rw-r--r--src/shell-interface.h2
-rw-r--r--src/tools/execution-results.h33
-rw-r--r--src/tools/fuzzing.h29
-rw-r--r--src/wasm/wasm-s-parser.cpp2
7 files changed, 65 insertions, 14 deletions
diff --git a/src/asm_v_wasm.h b/src/asm_v_wasm.h
index c79c73674..08416ef80 100644
--- a/src/asm_v_wasm.h
+++ b/src/asm_v_wasm.h
@@ -66,7 +66,7 @@ std::string getSigFromStructs(Type result, const ListType& operands) {
Type sigToType(char sig);
-FunctionType* sigToFunctionType(std::string sig);
+FunctionType sigToFunctionType(std::string sig);
FunctionType* ensureFunctionType(std::string sig, Module* wasm);
diff --git a/src/asmjs/asm_v_wasm.cpp b/src/asmjs/asm_v_wasm.cpp
index deb8ba71f..a937239f3 100644
--- a/src/asmjs/asm_v_wasm.cpp
+++ b/src/asmjs/asm_v_wasm.cpp
@@ -92,11 +92,11 @@ Type sigToType(char sig) {
}
}
-FunctionType* sigToFunctionType(std::string sig) {
- auto ret = new FunctionType;
- ret->result = sigToType(sig[0]);
+FunctionType sigToFunctionType(std::string sig) {
+ FunctionType ret;
+ ret.result = sigToType(sig[0]);
for (size_t i = 1; i < sig.size(); i++) {
- ret->params.push_back(sigToType(sig[i]));
+ ret.params.push_back(sigToType(sig[i]));
}
return ret;
}
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index a70a96f17..02fc2a83b 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -877,6 +877,9 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
emitImportHeader(curr);
if (curr->type.is()) {
visitFunctionType(currModule->getFunctionType(curr->type), &curr->name);
+ } else {
+ auto functionType = sigToFunctionType(getSig(curr));
+ visitFunctionType(&functionType, &curr->name);
}
o << ')';
o << maybeNewLine;
diff --git a/src/shell-interface.h b/src/shell-interface.h
index 85010a4b2..fbc0740c5 100644
--- a/src/shell-interface.h
+++ b/src/shell-interface.h
@@ -33,7 +33,7 @@ namespace wasm {
struct ExitException {};
struct TrapException {};
-struct ShellExternalInterface final : ModuleInstance::ExternalInterface {
+struct ShellExternalInterface : ModuleInstance::ExternalInterface {
// The underlying memory can be accessed through unaligned pointers which
// isn't well-behaved in C++. WebAssembly nonetheless expects it to behave
// properly. Avoid emitting unaligned load/store by checking for alignment
diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h
index ca4b819d9..935aff4cc 100644
--- a/src/tools/execution-results.h
+++ b/src/tools/execution-results.h
@@ -24,20 +24,37 @@
namespace wasm {
+typedef std::vector<Literal> Loggings;
+
+// Logs every single import call parameter.
+struct LoggingExternalInterface : public ShellExternalInterface {
+ Loggings& loggings;
+
+ LoggingExternalInterface(Loggings& loggings) : loggings(loggings) {}
+
+ Literal callImport(Function* import, LiteralList& arguments) override {
+ std::cout << "[LoggingExternalInterface logging";
+ loggings.push_back(Literal()); // buffer with a None between calls
+ for (auto argument : arguments) {
+ std::cout << ' ' << argument;
+ loggings.push_back(argument);
+ }
+ std::cout << "]\n";
+ return Literal();
+ }
+};
+
// gets execution results from a wasm module. this is useful for fuzzing
//
// we can only get results when there are no imports. we then call each method
// that has a result, with some values
struct ExecutionResults {
std::map<Name, Literal> results;
+ Loggings loggings;
// get results of execution
void get(Module& wasm) {
- if (ImportInfo(wasm).getNumImports() > 0) {
- std::cout << "[fuzz-exec] imports, so quitting\n";
- return;
- }
- ShellExternalInterface interface;
+ LoggingExternalInterface interface(loggings);
try {
ModuleInstance instance(wasm, &interface);
// execute all exported methods (that are therefore preserved through opts)
@@ -84,6 +101,10 @@ struct ExecutionResults {
abort();
}
}
+ if (loggings != other.loggings) {
+ std::cout << "logging not identical!\n";
+ abort();
+ }
return true;
}
@@ -92,7 +113,7 @@ struct ExecutionResults {
}
Literal run(Function* func, Module& wasm) {
- ShellExternalInterface interface;
+ LoggingExternalInterface interface(loggings);
try {
ModuleInstance instance(wasm, &interface);
return run(func, wasm, instance);
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index 3b1a418b5..7954229c7 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -128,6 +128,7 @@ public:
setupMemory();
setupTable();
setupGlobals();
+ addImportLoggingSupport();
// keep adding functions until we run out of input
while (!finishedInput) {
auto* func = addFunction();
@@ -184,10 +185,13 @@ private:
// Whether to emit atomic waits (which in single-threaded mode, may hang...)
static const bool ATOMIC_WAITS = false;
- // after we finish the input, we start going through it again, but xoring
+ // After we finish the input, we start going through it again, but xoring
// so it's not identical
int xorFactor = 0;
+ // The chance to emit a logging operation for a none expression. We
+ // randomize this in each function.
+ unsigned LOGGING_PERCENT = 0;
void readData(std::vector<char> input) {
bytes.swap(input);
@@ -300,6 +304,19 @@ private:
wasm.addExport(export_);
}
+ void addImportLoggingSupport() {
+ for (auto type : { i32, i64, f32, f64 }) {
+ auto* func = new Function;
+ Name name = std::string("log-") + printType(type);
+ func->name = name;
+ func->module = "fuzzing-support";
+ func->base = name;
+ func->params.push_back(type);
+ func->result = none;
+ wasm.addFunction(func);
+ }
+ }
+
Expression* makeHangLimitCheck() {
return builder.makeSequence(
builder.makeIf(
@@ -364,6 +381,7 @@ private:
std::map<Type, std::vector<Index>> typeLocals; // type => list of locals with that type
Function* addFunction() {
+ LOGGING_PERCENT = upToSquared(100);
Index num = wasm.functions.size();
func = new Function;
func->name = std::string("func_") + std::to_string(num);
@@ -711,6 +729,8 @@ private:
Expression* _makenone() {
auto choice = upTo(100);
+ if (choice < LOGGING_PERCENT) return makeLogging();
+ choice = upTo(100);
if (choice < 50) return makeSetLocal(none);
if (choice < 60) return makeBlock(none);
if (choice < 70) return makeIf(none);
@@ -1496,6 +1516,13 @@ private:
}
}
+ // special makers
+
+ Expression* makeLogging() {
+ auto type = pick(i32, i64, f32, f64);
+ return builder.makeCall(std::string("log-") + printType(type), { make(type) }, none);
+ }
+
// special getters
Type getType() {
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 42dd9df0d..de1e4f2e9 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -570,7 +570,7 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) {
// see https://github.com/WebAssembly/spec/pull/301
if (type.isNull()) {
// if no function type name provided, then we generated one
- std::unique_ptr<FunctionType> functionType = std::unique_ptr<FunctionType>(sigToFunctionType(getSigFromStructs(result, params)));
+ auto functionType = make_unique<FunctionType>(sigToFunctionType(getSigFromStructs(result, params)));
for (auto& existing : wasm.functionTypes) {
if (existing->structuralComparison(*functionType)) {
type = existing->name;