| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
|
|
|
|
|
|
|
|
|
| |
This mostly moves the code around and avoids some duplication. It
also tracks the list of exports with both names and values, so that
if we compile more than one module, we can still access exports
from the previous.
Also add a first test of running fuzz_shell.js in node.
This does make build() append the exports, which was done before
on the main module but not the second one. That only affects the
wasm-split fuzzer, which is not active yet, so this is still NFC.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The main addition here is a bundle_clusterfuzz.py script which will package up
the exact files that should be uploaded to ClusterFuzz. It also documents the
process and bundling and testing. You can do
bundle.py OUTPUT_FILE.tgz
That bundles wasm-opt from ./bin., which is enough for local testing. For
actually uploading to ClusterFuzz, we need a portable build, and @dschuff
had the idea to reuse the emsdk build, which works nicely. Doing
bundle.py OUTPUT_FILE.tgz --build-dir=/path/to/emsdk/upstream/
will bundle wasm-opt (+libs) from the emsdk. I verified that those builds
work on ClusterFuzz.
I added several forms of testing here. First, our main fuzzer fuzz_opt.py now
has a ClusterFuzz testcase handler, which simulates a ClusterFuzz environment.
Second, there are smoke tests that run in the unit test suite, and can also be
run separately:
python -m unittest test/unit/test_cluster_fuzz.py
Those unit tests can also run on a given bundle, e.g. one created from an
emsdk build, for testing right before upload:
BINARYEN_CLUSTER_FUZZ_BUNDLE=/path/to/bundle.tgz python -m unittest test/unit/test_cluster_fuzz.py
A third piece of testing is to add a --fuzz-passes test. That is a mode for
-ttf (translate random data into a valid wasm fuzz testcase) that uses random
data to pick and run a set of passes, to further shape the wasm. (--fuzz-passes
had no previous testing, and this PR fixes it and tidies it up a little, adding some
newer passes too).
Otherwise this PR includes the key run.py script that is bundled and then
executed by ClusterFuzz, basically a python script that runs wasm-opt -ttf [..]
to generate testcases, sets up their JS, and emits them.
fuzz_shell.js, which is the JS to execute testcases, will now check if it is
provided binary data of a wasm file. If so, it does not read a wasm file from
argv[1]. (This is needed because ClusterFuzz expects a single file for the
testcase, so we make a JS file with bundled wasm inside it.)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This adds two new imports to fuzzer modules:
* call-export, which gets an export index and calls it.
* call-export-catch, which does the call in a try-catch, swallowing
any error, and returning 1 if it saw an error.
The former gives us calls back into the wasm, possibly making various
trips between wasm and JS in interesting ways. The latter adds a
try-catch which helps fuzz wasm EH.
We do these calls using a wasm export index, i.e., the index in
the list of exports. This is simple, but it does have the downside that
it makes executing the wasm sensitive to changes in exports (e.g.
wasm-merge adds more), which requires some handling in the fuzzer.
|
|
|
|
| |
The old code assumed the index was a JS number, but if the table has
64-bit indexes it must be a BigInt. Detect that and cast as needed.
|
|
|
|
|
| |
Continues the work from #7027 which added throwing from JS, this adds
table get/set operations from JS, to further increase our coverage of
Wasm/JS interactions (the table can be used from both sides).
|
|
|
|
|
|
|
|
|
|
|
| |
We already generated (throw ..) instructions in wasm, but it makes sense to model
throws from outside as well, as they cross the module boundary. This adds a new fuzzer
import to the generated modules, "throw", that just does a throw from JS etc.
Also be more precise about handling fuzzing-support imports in fuzz-exec: we now
check that logging functions start with "log*" and error otherwise (this check is
now needed given we have "throw", which is not logging). Also fix a minor issue
with name conflicts for logging functions by using getValidFunctionName for them,
both for logging and for throw.
|
|
|
|
|
|
|
|
|
|
|
|
| |
The support is added but not enabled as this is still finding bugs.
The first part here is to add Split testcase handler to the fuzzer,
which runs a wasm, then runs it again after splitting it and then
linking it at runtime, and checking for different results.
The second part is support for linking two modules at runtime
in the fuzzer's JS code, that works in tandem with the first part.
New options are added to load and link a second wasm, and to
pick which exports to run.
|
|
|
|
|
|
|
|
|
|
|
| |
Previously we printed strings as WTF-8 in the output of fuzz-exec, but this
could produce invalid unicode output and did not make unprintable characters
visible. Fix both these problems by escaping the output, using the JSON string
escape procedure since the string to be escaped is WTF-16. Reimplement the same
escaping procedure in fuzz_shell.js so that the way we print strings when
running on a real JS engine matches the way we print them in our own fuzz-exec
interpreter.
Fixes #6435.
|
|
|
|
|
|
|
|
|
| |
We had exception: in one and exception thrown: in another. Making those
consistent allows fuzz_shell.js to print the exception after that prefix, which
makes debugging easier sometimes.
Also canonicalize tag names. Like funcref names, JS VMs print out the internal
name, which can change after opts, so canonicalize it.
|
|
|
|
| |
Before this we only printed the type of a BigInt and not the value.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
One problem was that spec testcases had exports with names that are not
valid to write as JS exports.name. For example an export with a - in the
name would end up as exports.foo-bar etc. Since #6310 that is fixed as
we do not emit such JS (we use the generic fuzz_shell.js script which iterates
over the keys in exports with exports[name]).
Also fix a few trivial fuzzer issues that initial content uncovered:
- Ignore a wat file with invalid utf-8.
- Print string literals in the same way from JS as from C++.
- Enable the stringref flag in V8.
- Remove tag imports (the same as we do for global and function and other imports).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We already have passes to legalize i64 imports and exports, which the fuzzer will
run so that we can run wasm files in JS VMs. SIMD and multivalue also pose a
problem as they trap on the boundary. In principle we could legalize them as well,
but that is substantial effort, so instead just prune them: given a wasm module,
remove any imports or exports that use SIMD or multivalue (or anything else that
is not legal for JS).
Running this in the fuzzer will allow us to not skip running v8 on any testcase we
enable SIMD and multivalue for.
(Multivalue is allowed in newer VMs, so that part of this PR could be removed
eventually.)
Also remove the limitation on running v8 with multimemory (v8 now supports
that).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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).
|
|
|
|
|
|
|
|
|
|
|
| |
Fuzzing Asyncify has a significant cost both in terms of the complexity in
the fuzzer and the slowness of the fuzzing. In practice it was useful years ago
when Asyncify was written but hasn't found anything for a while, and Asyncify
is really deprecated given JSPI. For all those reasons, remove it from the fuzzer.
We do still have lots of normal coverage of asyncify in lit tests, unit tests, and
the Emscripten test suite.
Removing this will also make future improvements to the fuzzer simpler.
|
|
|
|
| |
With this, the fuzz shell can run a hello world Java file compiled by j2wasm.
|
|
|
|
|
| |
I saw a testcase fail on the internal assertion of the buffer being too small.
Enlarge it to use as much of the memory we have anyhow to reduce that
risk (we can use 15 pages instead of 1, without changing anything else).
|
|
|
|
|
|
|
|
|
| |
Sometimes the fuzzer produces valid modules that trap during instantiation. When
that happens, the JS harness used to run the fuzzer output in d8 would
previously throw an error, creating spurious fuzzer failures on valid modules.
Update fuzz_shell.js to catch and supress errors during instantiation (but not
validation) to avoid these spurious failures.
Fixes #4865.
|
|
|
|
|
| |
Fuzzing Asyncify on data with a memory growth showed that the harness
did not handle a growth. When growth happens we must recreate the view.
For simplicity, always refresh, in a location that dominates all the uses.
|
|
|
|
| |
We need the memory to be exported in order to read and
write stuff for the asyncify data structure.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The main benefit here is comparing VMs, instead of just comparing
each VM to itself after opts. Comparing VMs is a little tricky since there
is room for nondeterminism with how results are printed and other
annoying things, which is why that didn't work well earlier.
With this PR I can run 10's of thousands of iterations without finding
any issues between v8 and the binaryen interpreter. That's after
fixing the various issues over the last few days as found by this:
#2760 #2757 #2750 #2752
Aside from that main benefit I ended up adding more improvements
to make it practical to do all that testing:
Randomize global fuzz settings like whether we allow NaNs and
out-of-bounds memory accesses. (This was necessary here since
we have to disable cross-VM comparisons if NaNs are enabled.)
Better logging of statistics like how many times each handler
was run.
Remove redundant FuzzExecImmediately handler (looks like
after past refactorings it was no longer adding any value).
Deterministic testcase handling: if you run e.g. fuzz_opt.py 42 it
will run one testcase and exactly the same one. If you run without
an argument it will run forever until it fails, and if it fails, it prints out
that ID so that you can easily reproduce it (I guess, on the same
binaryen + same python, not sure how python's deterministic
RNG changes between versions and builds).
Upgrade to Python 3.
|
|
|
|
|
|
|
| |
After some discussion this seems like a less confusing name: what the pass does is "asyncify" code, after all.
The one downside is the name overlaps with the old emscripten "Asyncify" utility, which we'll need to clarify in the docs there.
This keeps the old --bysyncify flag around for now, which is helpful for avoiding temporary breakage on CI as we move the emscripten side as well.
|
| |
|
|
Gets fuzzing support for Bysyncify working.
* Add the python to run the fuzzing on bysyncify.
* Add a JS script to load and run a testcase with bysyncify support. The code has all the runtime support for sleep/resume etc., which it does on calls to imports at random in a deterministic manner.
* Export memory from fuzzer so JS can access it.
* Fix tiny builder bug with makeExport.
|