summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2023-02-23 11:23:21 -0800
committerGitHub <noreply@github.com>2023-02-23 11:23:21 -0800
commitf2596c7300ad4933529471e30008bdc2eafbc538 (patch)
tree8e5e0f92cad4f503755b6532e5572017f7595507 /src
parent549b0f45be3749fd5f612a2eb225ca1cc40081a6 (diff)
downloadbinaryen-f2596c7300ad4933529471e30008bdc2eafbc538.tar.gz
binaryen-f2596c7300ad4933529471e30008bdc2eafbc538.tar.bz2
binaryen-f2596c7300ad4933529471e30008bdc2eafbc538.zip
[Fuzzer] Simplify the hang limit mechanism (#5513)
Previously the idea was that we started with HANG_LIMIT = 10 or so, and we'd decrement it by one in each potentially-recursive call and loop entry. When we reached 0 we'd start to unwind the stack. Then, after we unwound it all the way, we'd reset HANG_LIMIT before calling the next export. That approach adds complexity that each "execution wrapper", like for JS or for --fuzz-exec, had to manually reset HANG_LIMIT. That was done by calling an export. Calls to those exports had to appear in various places, which is sort of a hack. The new approach here does the following when the hang limit reaches zero: It resets HANG_LIMIT, and it traps. The trap unwinds the call stack all the way out. When the next export is called, it will have a fresh hang limit since we reset it before the trap. This does have downsides. Before, we did not always trap when we hit the hang limit but rather we'd emit something unreachable, like a return. The idea was that we'd leave the current function scope at least, so we don't hang forever. That let us still execute a small amount of code "on the way out" as we unwind the stack. I'm not sure it's worth the complexity for that. The advantages of this PR are to simplify the code, and also it makes more fuzzing approaches easy to implement. I'd like to add a wasm-ctor-eval fuzzer, and having to add hacks to call the hang limit init export in it would be tricky. With this PR, the execution model is simple in the fuzzer: The exports are called one by one, in order, and that's it - no extra magic execution needs to be done. Also bump the hang limit from 10 to 100, just to give some more chance for code to run.
Diffstat (limited to 'src')
-rw-r--r--src/tools/execution-results.h6
-rw-r--r--src/tools/fuzzing/fuzzing.cpp28
-rw-r--r--src/tools/fuzzing/parameters.h2
-rw-r--r--src/tools/js-wrapper.h2
-rw-r--r--src/tools/spec-wrapper.h3
-rw-r--r--src/tools/wasm2c-wrapper.h6
6 files changed, 10 insertions, 37 deletions
diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h
index d12c84d1e..569052086 100644
--- a/src/tools/execution-results.h
+++ b/src/tools/execution-results.h
@@ -226,12 +226,8 @@ struct ExecutionResults {
FunctionResult run(Function* func, Module& wasm, ModuleRunner& instance) {
try {
- Literals arguments;
- // init hang support, if present
- if (auto* ex = wasm.getExportOrNull("hangLimitInitializer")) {
- instance.callFunction(ex->value, arguments);
- }
// call the method
+ Literals arguments;
for (const auto& param : func->getParams()) {
// zeros in arguments TODO: more?
if (!param.isDefaultable()) {
diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp
index 4dac794e2..bb3a76766 100644
--- a/src/tools/fuzzing/fuzzing.cpp
+++ b/src/tools/fuzzing/fuzzing.cpp
@@ -426,26 +426,6 @@ void TranslateToFuzzReader::addHangLimitSupport() {
builder.makeConst(int32_t(HANG_LIMIT)),
Builder::Mutable);
wasm.addGlobal(std::move(glob));
-
- Name exportName = "hangLimitInitializer";
- auto funcName = Names::getValidFunctionName(wasm, exportName);
- auto* func = new Function;
- func->name = funcName;
- func->type = Signature(Type::none, Type::none);
- func->body = builder.makeGlobalSet(HANG_LIMIT_GLOBAL,
- builder.makeConst(int32_t(HANG_LIMIT)));
- wasm.addFunction(func);
-
- if (wasm.getExportOrNull(exportName)) {
- // We must export our actual hang limit function - remove anything
- // previously existing.
- wasm.removeExport(exportName);
- }
- auto* export_ = new Export;
- export_->name = exportName;
- export_->value = func->name;
- export_->kind = ExternalKind::Function;
- wasm.addExport(export_);
}
void TranslateToFuzzReader::addImportLoggingSupport() {
@@ -473,11 +453,17 @@ TranslateToFuzzReader::FunctionCreationContext::~FunctionCreationContext() {
}
Expression* TranslateToFuzzReader::makeHangLimitCheck() {
+ // If the hang limit global reaches 0 then we trap and reset it. That allows
+ // calls to other exports to proceed, with hang checking, after the trap halts
+ // the currently called export.
return builder.makeSequence(
builder.makeIf(
builder.makeUnary(UnaryOp::EqZInt32,
builder.makeGlobalGet(HANG_LIMIT_GLOBAL, Type::i32)),
- makeTrivial(Type::unreachable)),
+ builder.makeSequence(
+ builder.makeGlobalSet(HANG_LIMIT_GLOBAL,
+ builder.makeConst(int32_t(HANG_LIMIT))),
+ builder.makeUnreachable())),
builder.makeGlobalSet(
HANG_LIMIT_GLOBAL,
builder.makeBinary(BinaryOp::SubInt32,
diff --git a/src/tools/fuzzing/parameters.h b/src/tools/fuzzing/parameters.h
index 1ba7b064f..e92c88210 100644
--- a/src/tools/fuzzing/parameters.h
+++ b/src/tools/fuzzing/parameters.h
@@ -59,7 +59,7 @@ constexpr Address USABLE_MEMORY = 16;
// the number of runtime iterations (function calls, loop backbranches) we
// allow before we stop execution with a trap, to prevent hangs. 0 means
// no hang protection.
-constexpr int HANG_LIMIT = 10;
+constexpr int HANG_LIMIT = 100;
//
constexpr size_t VeryImportant = 4;
diff --git a/src/tools/js-wrapper.h b/src/tools/js-wrapper.h
index 85bc3d7ba..edee301ca 100644
--- a/src/tools/js-wrapper.h
+++ b/src/tools/js-wrapper.h
@@ -91,8 +91,6 @@ inline std::string generateJSWrapper(Module& wasm) {
continue; // something exported other than a function
}
auto* func = wasm.getFunction(exp->value);
- ret += "if (instance.exports.hangLimitInitializer) "
- "instance.exports.hangLimitInitializer();\n";
ret += "try {\n";
ret += std::string(" console.log('[fuzz-exec] calling ") +
exp->name.toString() + "');\n";
diff --git a/src/tools/spec-wrapper.h b/src/tools/spec-wrapper.h
index 95ead4739..5c0b8cfc8 100644
--- a/src/tools/spec-wrapper.h
+++ b/src/tools/spec-wrapper.h
@@ -31,8 +31,7 @@ inline std::string generateSpecWrapper(Module& wasm) {
if (!func) {
continue; // something exported other than a function
}
- ret += std::string("(invoke \"hangLimitInitializer\") (invoke \"") +
- exp->name.toString() + "\" ";
+ ret += std::string("(invoke \"") + exp->name.toString() + "\" ";
for (const auto& param : func->getParams()) {
// zeros in arguments TODO more?
TODO_SINGLE_COMPOUND(param);
diff --git a/src/tools/wasm2c-wrapper.h b/src/tools/wasm2c-wrapper.h
index 984a9b53a..53343f2d1 100644
--- a/src/tools/wasm2c-wrapper.h
+++ b/src/tools/wasm2c-wrapper.h
@@ -137,12 +137,6 @@ int main(int argc, char** argv) {
// compile times are O(size * num_setjmps).
for (size_t curr = 0;; curr++) {
)";
- if (wasm.getExportOrNull("hangLimitInitializer")) {
- ret += R"(
- // If present, call the hang limit initializer before each export.
- (*Z_hangLimitInitializerZ_vv)();
-)";
- }
ret += R"(
// Prepare to call the export, so we can catch traps.
if (WASM_RT_SETJMP(g_jmp_buf) != 0) {