summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/fuzz_opt.py8
-rw-r--r--src/shell-interface.h13
-rw-r--r--src/tools/execution-results.h12
-rw-r--r--src/tools/wasm-ctor-eval.cpp4
-rw-r--r--src/wasm-interpreter.h13
-rw-r--r--test/passes/Oz_fuzz-exec_all-features.txt15
-rw-r--r--test/passes/Oz_fuzz-exec_all-features.wast15
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)
+ )
+)