diff options
author | Alon Zakai <azakai@google.com> | 2024-12-09 14:46:24 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-09 14:46:24 -0800 |
commit | 7f62a423ee4bd908f485d01945b71786176b926a (patch) | |
tree | aae42c49412b0153c34ef5a26fea888bb404bdd7 /scripts | |
parent | 729ea41d145d369b203dca6f70b251ea365cb3d0 (diff) | |
download | binaryen-7f62a423ee4bd908f485d01945b71786176b926a.tar.gz binaryen-7f62a423ee4bd908f485d01945b71786176b926a.tar.bz2 binaryen-7f62a423ee4bd908f485d01945b71786176b926a.zip |
Fuzzer: Add call-ref, call-ref-catch imports (#7137)
Similar to call-export*, these imports call a wasm function from outside the
module. The difference is that we send a function reference for them to call
(rather than an export index).
This gives more coverage, first by sending a ref from wasm to JS, and also
since we will now try to call anything that is sent. Exports, in comparison,
are filtered by the fuzzer to things that JS can handle, so this may lead to
more traps, but maybe also some new situations. This also leads to adding
more logic to execution-results.h to model JS trapping properly.
fuzz_shell.js is refactored to allow sharing code between call-export* and
call-ref*.
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/fuzz_shell.js | 89 |
1 files changed, 52 insertions, 37 deletions
diff --git a/scripts/fuzz_shell.js b/scripts/fuzz_shell.js index 782040dac..95176bbe6 100644 --- a/scripts/fuzz_shell.js +++ b/scripts/fuzz_shell.js @@ -160,6 +160,49 @@ function callFunc(func) { return func.apply(null, args); } +// Calls a given function in a try-catch, swallowing JS exceptions, and return 1 +// if we did in fact swallow an exception. Wasm traps are not swallowed (see +// details below). +function tryCall(func) { + try { + func(); + return 0; + } catch (e) { + // 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; + if (!wasm2js) { + // When running native wasm, we can detect wasm traps. + if (e instanceof WebAssembly.RuntimeError) { + throw e; + } + } + var text = e + ''; + // We must not swallow host limitations here: a host limitation is a + // problem that means we must not compare the outcome here to any other + // VM. + var hostIssues = ['requested new array is too large', + 'out of memory', + 'Maximum call stack size exceeded']; + if (wasm2js) { + // When wasm2js does trap, it just throws an "abort" error. + hostIssues.push('abort'); + } + for (var hostIssue of hostIssues) { + if (text.includes(hostIssue)) { + throw e; + } + } + // Otherwise, this is a normal exception we want to catch (a wasm + // exception, or a conversion error on the wasm/JS boundary, etc.). + return 1; + } +} + // Table get/set operations need a BigInt if the table has 64-bit indexes. This // adds a proper cast as needed. function toAddressType(table, index) { @@ -204,43 +247,15 @@ var imports = { callFunc(exportList[index].value); }, 'call-export-catch': (index) => { - try { - callFunc(exportList[index].value); - return 0; - } catch (e) { - // 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; - if (!wasm2js) { - // When running native wasm, we can detect wasm traps. - if (e instanceof WebAssembly.RuntimeError) { - throw e; - } - } - var text = e + ''; - // We must not swallow host limitations here: a host limitation is a - // problem that means we must not compare the outcome here to any other - // VM. - var hostIssues = ['requested new array is too large', - 'out of memory', - 'Maximum call stack size exceeded']; - if (wasm2js) { - // When wasm2js does trap, it just throws an "abort" error. - hostIssues.push('abort'); - } - for (var hostIssue of hostIssues) { - if (text.includes(hostIssue)) { - throw e; - } - } - // Otherwise, this is a normal exception we want to catch (a wasm - // exception, or a conversion error on the wasm/JS boundary, etc.). - return 1; - } + return tryCall(() => callFunc(exportList[index].value)); + }, + + // Funcref operations. + 'call-ref': (ref) => { + callFunc(ref); + }, + 'call-ref-catch': (ref) => { + return tryCall(() => callFunc(ref)); }, }, // Emscripten support. |