diff options
-rwxr-xr-x | scripts/fuzz_opt.py | 8 | ||||
-rw-r--r-- | src/shell-interface.h | 13 | ||||
-rw-r--r-- | src/tools/execution-results.h | 12 | ||||
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 4 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 13 | ||||
-rw-r--r-- | test/passes/Oz_fuzz-exec_all-features.txt | 15 | ||||
-rw-r--r-- | test/passes/Oz_fuzz-exec_all-features.wast | 15 |
7 files changed, 76 insertions, 4 deletions
diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 48cf9dfa3..30b0b52fa 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -237,6 +237,9 @@ IGNORE = '[binaryen-fuzzer-ignore]' # Traps are reported as [trap REASON] TRAP_PREFIX = '[trap ' +# Host limits are reported as [host limit REASON] +HOST_LIMIT_PREFIX = '[host limit ' + # --fuzz-exec reports calls as [fuzz-exec] calling foo FUZZ_EXEC_CALL_PREFIX = '[fuzz-exec] calling' @@ -346,7 +349,7 @@ def fix_spec_output(out): def run_vm(cmd): - # ignore some vm assertions, if bugs have already been filed + # ignore some types of errors known_issues = [ # can be caused by flatten, ssa, etc. passes 'local count too large', @@ -354,6 +357,9 @@ def run_vm(cmd): # note that this text is a little too broad, but the problem is rare # enough that it's unlikely to hide an unrelated issue 'found br_if of type', + # all host limitations are arbitrary and may differ between VMs and also + # be affected by optimizations, so ignore them. + HOST_LIMIT_PREFIX, ] try: return run(cmd) diff --git a/src/shell-interface.h b/src/shell-interface.h index eb0e9f91c..15f600592 100644 --- a/src/shell-interface.h +++ b/src/shell-interface.h @@ -31,9 +31,17 @@ namespace wasm { +// An exception emitted when exit() is called. struct ExitException {}; + +// An exception emitted when a wasm trap occurs. struct TrapException {}; +// An exception emitted when a host limitation is hit. (These are not wasm traps +// as they are not in the spec; for example, the spec has no limit on how much +// GC memory may be allocated, but hosts have limits.) +struct HostLimitException {}; + 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 @@ -265,6 +273,11 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { throw TrapException(); } + void hostLimit(const char* why) override { + std::cout << "[host limit " << why << "]\n"; + throw HostLimitException(); + } + void throwException(const WasmException& exn) override { throw exn; } }; diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index a183ad93b..50f627baf 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -90,6 +90,9 @@ struct ExecutionResults { std::map<Name, Literals> results; Loggings loggings; + // If set, we should ignore this and not compare it to anything. + bool ignore = false; + // get results of execution void get(Module& wasm) { LoggingExternalInterface interface(loggings); @@ -176,6 +179,10 @@ struct ExecutionResults { } bool operator==(ExecutionResults& other) { + if (ignore || other.ignore) { + std::cout << "ignoring comparison of ExecutionResults!\n"; + return true; + } for (auto& iter : other.results) { auto name = iter.first; if (results.find(name) == results.end()) { @@ -231,6 +238,11 @@ struct ExecutionResults { return instance.callFunction(func->name, arguments); } catch (const TrapException&) { return {}; + } catch (const HostLimitException&) { + // This should be ignored and not compared with, as optimizations can + // change whether a host limit is reached. + ignore = true; + return {}; } } }; diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 6662dd176..2f7707853 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -312,6 +312,10 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { throw FailToEvalException(std::string("trap: ") + why); } + void hostLimit(const char* why) override { + throw FailToEvalException(std::string("trap: ") + why); + } + void throwException(const WasmException& exn) override { std::stringstream ss; ss << "exception thrown: " << exn; diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index d76f3fabf..37bebdf6b 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -197,7 +197,7 @@ public: Flow visit(Expression* curr) { depth++; if (maxDepth != NO_LIMIT && depth > maxDepth) { - trap("interpreter recursion limit"); + hostLimit("interpreter recursion limit"); } auto ret = OverriddenVisitor<SubType, Flow>::visit(curr); if (!ret.breaking()) { @@ -1622,7 +1622,7 @@ public: // limits on 32-bit machines, and in particular on wasm32 VMs that do not // have 4GB support, so give up there. if (num >= (1 << 30) / sizeof(Literal)) { - trap("allocation failure"); + hostLimit("allocation failure"); } Literals data(num); if (curr->isWithDefault()) { @@ -1739,6 +1739,8 @@ public: virtual void trap(const char* why) { WASM_UNREACHABLE("unimp"); } + virtual void hostLimit(const char* why) { WASM_UNREACHABLE("unimp"); } + virtual void throwException(const WasmException& exn) { WASM_UNREACHABLE("unimp"); } @@ -2024,6 +2026,8 @@ public: void trap(const char* why) override { throw NonconstantException(); } + void hostLimit(const char* why) override { throw NonconstantException(); } + virtual void throwException(const WasmException& exn) override { throw NonconstantException(); } @@ -2076,6 +2080,7 @@ public: SubType& instance) = 0; virtual bool growMemory(Address oldSize, Address newSize) = 0; virtual void trap(const char* why) = 0; + virtual void hostLimit(const char* why) = 0; virtual void throwException(const WasmException& exn) = 0; // the default impls for load and store switch on the sizes. you can either @@ -3095,6 +3100,10 @@ private: instance.externalInterface->trap(why); } + void hostLimit(const char* why) override { + instance.externalInterface->hostLimit(why); + } + void throwException(const WasmException& exn) override { instance.externalInterface->throwException(exn); } diff --git a/test/passes/Oz_fuzz-exec_all-features.txt b/test/passes/Oz_fuzz-exec_all-features.txt index 5d00a3db1..0da22edbd 100644 --- a/test/passes/Oz_fuzz-exec_all-features.txt +++ b/test/passes/Oz_fuzz-exec_all-features.txt @@ -33,7 +33,7 @@ [LoggingExternalInterface logging 3] [trap cast error] [fuzz-exec] calling array-alloc-failure -[trap allocation failure] +[host limit allocation failure] (module (type $struct (struct (field (mut i32)))) (type $void_func (func)) @@ -277,3 +277,16 @@ [LoggingExternalInterface logging 3] [trap cast error] [fuzz-exec] calling array-alloc-failure +ignoring comparison of ExecutionResults! +[fuzz-exec] calling foo +[host limit allocation failure] +(module + (type $none_=>_i32 (func (result i32))) + (export "foo" (func $0)) + (func $0 (; has Stack IR ;) (result i32) + (i32.const 0) + ) +) +[fuzz-exec] calling foo +[fuzz-exec] note result: foo => 0 +ignoring comparison of ExecutionResults! diff --git a/test/passes/Oz_fuzz-exec_all-features.wast b/test/passes/Oz_fuzz-exec_all-features.wast index 4092f6b34..c88133c3a 100644 --- a/test/passes/Oz_fuzz-exec_all-features.wast +++ b/test/passes/Oz_fuzz-exec_all-features.wast @@ -240,3 +240,18 @@ ) ) ) +(module + (type $[mut:i8] (array (mut i8))) + (func "foo" (result i32) + ;; before opts this will trap on failing to allocate -1 >>> 0 bytes. after + ;; opts the unused value is removed so there is no trap, and a value is + ;; returned, which should not confuse the fuzzer. + (drop + (array.new_default_with_rtt $[mut:i8] + (i32.const -1) + (rtt.canon $[mut:i8]) + ) + ) + (i32.const 0) + ) +) |