diff options
author | Alon Zakai <azakai@google.com> | 2024-02-20 15:22:28 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-20 15:22:28 -0800 |
commit | 60b2daea5467e419899e9201a342cc817d6fd68e (patch) | |
tree | b4bcdbc5f1fa99b58f9edc5726bc44f9e2e5949b /scripts | |
parent | 403153868b936250c52d150ef158419a1d67bf55 (diff) | |
download | binaryen-60b2daea5467e419899e9201a342cc817d6fd68e.tar.gz binaryen-60b2daea5467e419899e9201a342cc817d6fd68e.tar.bz2 binaryen-60b2daea5467e419899e9201a342cc817d6fd68e.zip |
Fuzzer: Remove --emit-js-shell logic and reuse fuzz_shell.js instead (#6310)
We had two JS files that could run a wasm file for fuzzing purposes:
* --emit-js-shell, which emitted a custom JS file that runs the wasm.
* scripts/fuzz_shell.js, which was a generic file that did the same.
Both of those load the wasm and then call the exports in order and print out
logging as it goes of their return values (if any), exceptions, etc. Then the
fuzzer compares that output to running the same wasm in another VM, etc. The
difference is that one was custom for the wasm file, and one was generic. Aside
from that they are similar and duplicated a bunch of code.
This PR improves things by removing 1 and using 2 in all places, that is, we
now use the generic file everywhere.
I believe we added 1 because we thought a generic file can't do all the
things we need, like know the order of exports and the types of return values,
but in practice there are ways to do those things: The exports are in fact
in the proper order (JS order of iteration is deterministic, thankfully), and
for the type we don't want to print type internals anyhow since that would
limit fuzzing --closed-world. We do need to be careful with types in JS (see
notes in the PR about the type of null) but it's not too bad. As for the types
of params, it's fine to pass in null for them all anyhow (null converts to a
number or a reference without error).
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/fuzz_opt.py | 11 | ||||
-rw-r--r-- | scripts/fuzz_shell.js | 49 |
2 files changed, 43 insertions, 17 deletions
diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 8e80c2bb9..8dc87cb7a 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -665,8 +665,11 @@ def run_d8_js(js, args=[], liftoff=True): return run_vm(cmd) +FUZZ_SHELL_JS = in_binaryen('scripts', 'fuzz_shell.js') + + def run_d8_wasm(wasm, liftoff=True): - return run_d8_js(in_binaryen('scripts', 'fuzz_shell.js'), [wasm], liftoff=liftoff) + return run_d8_js(FUZZ_SHELL_JS, [wasm], liftoff=liftoff) def all_disallowed(features): @@ -746,8 +749,7 @@ class CompareVMs(TestCaseHandler): name = 'd8' def run(self, wasm, extra_d8_flags=[]): - run([in_bin('wasm-opt'), wasm, '--emit-js-wrapper=' + wasm + '.js'] + FEATURE_OPTS) - return run_vm([shared.V8, wasm + '.js'] + shared.V8_OPTS + extra_d8_flags + ['--', wasm]) + return run_vm([shared.V8, FUZZ_SHELL_JS] + shared.V8_OPTS + extra_d8_flags + ['--', wasm]) def can_run(self, wasm): # INITIAL_CONTENT is disallowed because some initial spec testcases @@ -1036,7 +1038,8 @@ class Wasm2JS(TestCaseHandler): compare_between_vms(before, interpreter, 'Wasm2JS (vs interpreter)') def run(self, wasm): - wrapper = run([in_bin('wasm-opt'), wasm, '--emit-js-wrapper=/dev/stdout'] + FEATURE_OPTS) + with open(FUZZ_SHELL_JS) as f: + wrapper = f.read() cmd = [in_bin('wasm2js'), wasm, '--emscripten'] # avoid optimizations if we have nans, as we don't handle them with # full precision and optimizations can change things diff --git a/scripts/fuzz_shell.js b/scripts/fuzz_shell.js index 4a3b42d9f..743e838b5 100644 --- a/scripts/fuzz_shell.js +++ b/scripts/fuzz_shell.js @@ -38,15 +38,34 @@ var detrand = (function() { }; })(); -// Fuzz integration. -function logValue(x, y) { +// Print out a value in a way that works well for fuzzing. +function printed(x, y) { if (typeof y !== 'undefined') { - console.log('[LoggingExternalInterface logging ' + x + ' ' + y + ']'); + // A pair of i32s which are a legalized i64. + return x + ' ' + y; + } else if (x === null) { + // JS has just one null. Print that out rather than typeof null which is + // 'object', below. + return 'null'; + } else if (typeof x !== 'number' && typeof x !== 'string') { + // Something that is not a number or string, like a reference. We can't + // print a reference because it could look different after opts - imagine + // that a function gets renamed internally (that is, the problem is that + // JS printing will emit some info about the reference and not a stable + // external representation of it). In those cases just print the type, + // which will be 'object' or 'function'. + return typeof x; } else { - console.log('[LoggingExternalInterface logging ' + x + ']'); + // A number. Print the whole thing. + return '' + x; } } +// Fuzzer integration. +function logValue(x, y) { + console.log('[LoggingExternalInterface logging ' + printed(x, y) + ']'); +} + // Set up the imports. var imports = { 'fuzzing-support': { @@ -94,21 +113,25 @@ function refreshView() { } // Run the wasm. -var sortedExports = []; for (var e in exports) { - sortedExports.push(e); -} -sortedExports.sort(); -sortedExports.forEach(function(e) { - if (typeof exports[e] !== 'function') return; + if (typeof exports[e] !== 'function') { + continue; + } + // Send the function a null for each parameter. Null can be converted without + // error to both a number and a reference. + var func = exports[e]; + var args = []; + for (var i = 0; i < func.length; i++) { + args.push(null); + } try { console.log('[fuzz-exec] calling ' + e); - var result = exports[e](); + var result = func.apply(null, args); if (typeof result !== 'undefined') { - console.log('[fuzz-exec] note result: $' + e + ' => ' + result); + console.log('[fuzz-exec] note result: ' + e + ' => ' + printed(result)); } } catch (e) { console.log('exception!');// + [e, e.stack]); } -}); +} |