-
Notifications
You must be signed in to change notification settings - Fork 841
Fuzzer: Log exported globals #8466
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a8029a3
778bc62
33d66f6
97e494a
c2a4599
7bff825
c26fb4c
86f8fad
a01f99a
f05bf56
612b26b
f35fcce
b055b9f
c77cd71
12f9c58
9d27b0a
331e7a9
1984359
ea1777a
7641556
dc394e2
0a6dcb5
6b460de
4f56b37
d09d5d7
d9fc4ba
c4b482f
cf70f87
b572696
89edd11
2d4b718
cc5b2ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -198,6 +198,11 @@ function callFunc(func) { | |
| return func.apply(null, args); | ||
| } | ||
|
|
||
| // wasm2js does not define RuntimeError, so use that to check for it. wasm2js | ||
| // overrides the entire WebAssembly object with a polyfill, so we know exactly | ||
| // what it contains, and we need to handle some things differently below. | ||
| var wasm2js = !WebAssembly.RuntimeError; | ||
|
|
||
| // Calls a given function in a try-catch. Return 1 if an exception was thrown. | ||
| // If |rethrow| is set, and an exception is thrown, it is caught and rethrown. | ||
| // Wasm traps are not swallowed (see details below). | ||
|
|
@@ -213,11 +218,7 @@ function callFunc(func) { | |
|
|
||
| // We only want to catch exceptions, not wasm traps: traps should still | ||
| // halt execution. Handling this requires different code in wasm2js, so | ||
| // check for that first (wasm2js does not define RuntimeError, so use | ||
| // that for the check - when wasm2js is run, we override the entire | ||
| // WebAssembly object with a polyfill, so we know exactly what it | ||
| // contains). | ||
| var wasm2js = !WebAssembly.RuntimeError; | ||
| // check for that first. | ||
| if (!wasm2js) { | ||
| // When running native wasm, we can detect wasm traps. | ||
| if (e instanceof WebAssembly.RuntimeError) { | ||
|
|
@@ -555,13 +556,53 @@ function build(binary, isSecond) { | |
| name = e; | ||
| value = exports[e]; | ||
| } else { | ||
| // We are given an object form exportList, which has both a name and a | ||
| // We are given an object from exportList, which has both a name and a | ||
| // value. | ||
| name = e.name; | ||
| value = e.value; | ||
| } | ||
|
|
||
| // Check for a global. Note we must be careful in wasm2js mode, where we | ||
| // can't do instanceof here (the wasm polyfill there doesn't have such | ||
| // things). In wasm2js we strip global exports to avoid needing to handle | ||
| // them here (using stub-unsupported-js). | ||
| if (!wasm2js && (value instanceof WebAssembly.Global)) { | ||
| // We can log a global value and do other operations to check for bugs. | ||
| // First, do some operations on the Global wrapper itself. | ||
| JSON.stringify(value); | ||
| value.foobar; | ||
|
|
||
| // Log it at the right time later using a lambda. Note that we can't just | ||
| // capture |value| for the lambda, as the loop modifies it. | ||
| (() => { | ||
| var global = value; | ||
| value = () => { | ||
| // Time to log. Look at the exported value itself, not the global | ||
| // wrapper. | ||
| let actualValue; | ||
| try { | ||
| actualValue = global.value; | ||
| } catch (e) { | ||
| if (e.message.startsWith('get WebAssembly.Global.value')) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| // Just log a null instead of a value we cannot access from JS, | ||
| // like an exnref. | ||
| actualValue = null; | ||
| } else { | ||
| throw e; | ||
| } | ||
| } | ||
| if (typeof actualValue === 'object') { | ||
| // logRef can do a little more than logValue, so use it when possible. | ||
| logRef(actualValue); | ||
| } else { | ||
| logValue(actualValue); | ||
| } | ||
| }; | ||
| })(); | ||
| } | ||
|
|
||
| if (typeof value !== 'function') { | ||
| // Nothing we can call. | ||
| continue; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -487,23 +487,33 @@ struct ExecutionResults { | |
| // execute all exported methods (that are therefore preserved through | ||
| // opts) | ||
| for (auto& exp : wasm.exports) { | ||
| if (exp->kind != ExternalKind::Function) { | ||
| continue; | ||
| } | ||
| std::cout << "[fuzz-exec] calling " << exp->name << "\n"; | ||
| auto* func = wasm.getFunction(*exp->getInternalName()); | ||
| FunctionResult ret = run(func, wasm, instance); | ||
| results[exp->name] = ret; | ||
| if (auto* values = std::get_if<Literals>(&ret)) { | ||
| // ignore the result if we hit an unreachable and returned no value | ||
| if (values->size() > 0) { | ||
| std::cout << "[fuzz-exec] note result: " << exp->name << " => "; | ||
| for (auto value : *values) { | ||
| printValue(value); | ||
| std::cout << '\n'; | ||
| if (exp->kind == ExternalKind::Function) { | ||
| std::cout << "[fuzz-exec] calling " << exp->name << "\n"; | ||
| auto* func = wasm.getFunction(*exp->getInternalName()); | ||
| FunctionResult ret = run(func, wasm, instance); | ||
| results[exp->name] = ret; | ||
| if (auto* values = std::get_if<Literals>(&ret)) { | ||
| // ignore the result if we hit an unreachable and returned no value | ||
| if (values->size() > 0) { | ||
| std::cout << "[fuzz-exec] note result: " << exp->name << " => "; | ||
| for (auto value : *values) { | ||
| printValue(value); | ||
| std::cout << '\n'; | ||
| } | ||
| } | ||
| } | ||
| } else if (exp->kind == ExternalKind::Global) { | ||
| // Log the global's value. (We use "calling" here to match the output | ||
| // for calls, which simplifies the fuzzer.) | ||
| std::cout << "[fuzz-exec] calling " << exp->name << "\n"; | ||
|
Comment on lines
+506
to
+508
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we remove "calling" everywhere as a follow-up? |
||
| Literals* value = instance.getExportedGlobalOrNull(exp->name); | ||
| assert(value); | ||
| assert(value->size() == 1); | ||
| std::cout << "[LoggingExternalInterface logging "; | ||
| printValue((*value)[0]); | ||
| std::cout << "]\n"; | ||
| } | ||
| // Ignore other exports for now. TODO | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not surprised to be surprised once again by JS semantics. Even the
var global = valueis not enough to avoid the need for this outer lambda?