summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2020-04-13 11:37:29 -0700
committerGitHub <noreply@github.com>2020-04-13 11:37:29 -0700
commite0716f53362f233662537a27f2165ed9dbb301e3 (patch)
tree5439c8da3c4b4134159232718d62e3ea076698ea /src
parent585f8174c1339d7ac5eeceed89d2ac30e4ba8826 (diff)
downloadbinaryen-e0716f53362f233662537a27f2165ed9dbb301e3.tar.gz
binaryen-e0716f53362f233662537a27f2165ed9dbb301e3.tar.bz2
binaryen-e0716f53362f233662537a27f2165ed9dbb301e3.zip
Add --deterministic flag to wasm2js, for fuzzing (#2757)
In wasm2js we ignore things that trap in wasm that we can't really handle, like a load from memory out of bounds would trap in wasm, but in JS we don't want to emit a bounds check on each load. So wasm2js focuses on programs that don't trap. However, this is annoying in the fuzzer as it turns out that our behavior for places where wasm would trap was not deterministic. That is, wasm would trap, wasm2js would not trap and do behavior X, and wasm2js with optimizations would also not trap but do behavior Y != X. This produced false positives in the fuzzer (and might be annoying in manual debugging too). As a workaround, this adds a --deterministic flag to wasm2js, which tries to be deterministic about what it does for cases where wasm would trap. This handles the case of an int division by 0 which traps in wasm but without this flag could have different behavior in wasm2js with or without opts (see details in the patch).
Diffstat (limited to 'src')
-rw-r--r--src/tools/wasm2js.cpp33
-rw-r--r--src/wasm2js.h2
2 files changed, 31 insertions, 4 deletions
diff --git a/src/tools/wasm2js.cpp b/src/tools/wasm2js.cpp
index e7b62c16a..7f3433bb7 100644
--- a/src/tools/wasm2js.cpp
+++ b/src/tools/wasm2js.cpp
@@ -141,7 +141,7 @@ static void replaceInPlaceIfPossible(Ref target, Ref value) {
}
}
-static void optimizeJS(Ref ast) {
+static void optimizeJS(Ref ast, Wasm2JSBuilder::Flags flags) {
// Helpers
auto isBinary = [](Ref node, IString op) {
@@ -262,8 +262,21 @@ static void optimizeJS(Ref ast) {
node[1]->setString(L_NOT);
node[3]->setNull();
} else if (isOrZero(node) || isTrshiftZero(node)) {
- // Just being different from 0 is enough, casts don't matter.
- return node[2];
+ // Just being different from 0 is enough, casts don't matter. However,
+ // in deterministic mode we care about corner cases that would trap in
+ // wasm, like an integer divide by zero:
+ //
+ // if ((1 / 0) | 0) => condition is Infinity | 0 = 0 which is falsey
+ //
+ // while
+ //
+ // if (1 / 0) => condition is Infinity which is truthy
+ //
+ // Thankfully this is not common, and does not occur on % (1 % 0 is a NaN
+ // which has the right truthiness).
+ if (!(flags.deterministic && isBinary(node[2], DIV))) {
+ return node[2];
+ }
}
return node;
};
@@ -511,7 +524,7 @@ static void emitWasm(Module& wasm,
Wasm2JSBuilder wasm2js(flags, options);
auto js = wasm2js.processWasm(&wasm, name);
if (options.optimizeLevel >= 2) {
- optimizeJS(js);
+ optimizeJS(js, flags);
}
Wasm2JSGlue glue(wasm, output, flags, name);
glue.emitPre();
@@ -849,6 +862,18 @@ int main(int argc, const char* argv[]) {
Options::Arguments::Zero,
[&](Options* o, const std::string& argument) { flags.emscripten = true; })
.add(
+ "--deterministic",
+ "",
+ "Replace WebAssembly trapping behavior deterministically "
+ "(the default is to not care about what would trap in wasm, like a load "
+ "out of bounds or integer divide by zero; with this flag, we try to be "
+ "deterministic at least in what happens, which might or might not be "
+ "to trap like wasm, but at least should not vary)",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) {
+ flags.deterministic = true;
+ })
+ .add(
"--symbols-file",
"",
"Emit a symbols file that maps function indexes to their original names",
diff --git a/src/wasm2js.h b/src/wasm2js.h
index 4db734203..1d8003323 100644
--- a/src/wasm2js.h
+++ b/src/wasm2js.h
@@ -120,10 +120,12 @@ class Wasm2JSBuilder {
public:
struct Flags {
+ // see wasm2js.cpp for details
bool debug = false;
bool pedantic = false;
bool allowAsserts = false;
bool emscripten = false;
+ bool deterministic = false;
std::string symbolsFile;
};