Commit 41b34f2a authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm][fuzzer] Fix detection of traps

Instead of returning 0xDEADBEEF, return a struct with proper
information. Otherwise a function returning 0xDEADBEEF would be
misidentified as trapping in the interpreter.

R=ahaas@chromium.org

Bug: chromium:906997
Change-Id: I92fc3a9972d76d2f8a5b313bf6be6eb027cfc1e9
Reviewed-on: https://chromium-review.googlesource.com/c/1344111Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57658}
parent a6e3cdd9
...@@ -160,10 +160,9 @@ int32_t CompileAndRunAsmWasmModule(Isolate* isolate, const byte* module_start, ...@@ -160,10 +160,9 @@ int32_t CompileAndRunAsmWasmModule(Isolate* isolate, const byte* module_start,
return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0, return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
nullptr); nullptr);
} }
int32_t InterpretWasmModule(Isolate* isolate, WasmInterpretationResult InterpretWasmModule(
Handle<WasmInstanceObject> instance, Isolate* isolate, Handle<WasmInstanceObject> instance,
ErrorThrower* thrower, int32_t function_index, int32_t function_index, WasmValue* args) {
WasmValue* args, bool* possible_nondeterminism) {
// Don't execute more than 16k steps. // Don't execute more than 16k steps.
constexpr int kMaxNumSteps = 16 * 1024; constexpr int kMaxNumSteps = 16 * 1024;
...@@ -186,17 +185,19 @@ int32_t InterpretWasmModule(Isolate* isolate, ...@@ -186,17 +185,19 @@ int32_t InterpretWasmModule(Isolate* isolate,
bool stack_overflow = isolate->has_pending_exception(); bool stack_overflow = isolate->has_pending_exception();
isolate->clear_pending_exception(); isolate->clear_pending_exception();
*possible_nondeterminism = thread->PossibleNondeterminism(); if (stack_overflow) return WasmInterpretationResult::Stopped();
if (stack_overflow) return 0xDEADBEEF;
if (thread->state() == WasmInterpreter::TRAPPED) return 0xDEADBEEF; if (thread->state() == WasmInterpreter::TRAPPED) {
return WasmInterpretationResult::Trapped(thread->PossibleNondeterminism());
}
if (interpreter_result == WasmInterpreter::FINISHED) if (interpreter_result == WasmInterpreter::FINISHED) {
return thread->GetReturnValue().to<int32_t>(); return WasmInterpretationResult::Finished(
thread->GetReturnValue().to<int32_t>(),
thread->PossibleNondeterminism());
}
thrower->RangeError( return WasmInterpretationResult::Stopped();
"Interpreter did not finish execution within its step bound");
return -1;
} }
MaybeHandle<WasmExportedFunction> GetExportedFunction( MaybeHandle<WasmExportedFunction> GetExportedFunction(
......
...@@ -61,13 +61,48 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start, ...@@ -61,13 +61,48 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
MaybeHandle<WasmInstanceObject> CompileAndInstantiateForTesting( MaybeHandle<WasmInstanceObject> CompileAndInstantiateForTesting(
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes); Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes);
class WasmInterpretationResult {
public:
static WasmInterpretationResult Stopped() { return {kStopped, 0, false}; }
static WasmInterpretationResult Trapped(bool possible_nondeterminism) {
return {kTrapped, 0, possible_nondeterminism};
}
static WasmInterpretationResult Finished(int32_t result,
bool possible_nondeterminism) {
return {kFinished, result, possible_nondeterminism};
}
bool stopped() const { return status_ == kStopped; }
bool trapped() const { return status_ == kTrapped; }
bool finished() const { return status_ == kFinished; }
int32_t result() const {
DCHECK_EQ(status_, kFinished);
return result_;
}
bool possible_nondeterminism() const { return possible_nondeterminism_; }
private:
enum Status { kFinished, kTrapped, kStopped };
const Status status_;
const int32_t result_;
const bool possible_nondeterminism_;
WasmInterpretationResult(Status status, int32_t result,
bool possible_nondeterminism)
: status_(status),
result_(result),
possible_nondeterminism_(possible_nondeterminism) {}
};
// Interprets the given module, starting at the function specified by // Interprets the given module, starting at the function specified by
// {function_index}. The return type of the function has to be int32. The module // {function_index}. The return type of the function has to be int32. The module
// should not have any imports or exports // should not have any imports or exports
int32_t InterpretWasmModule(Isolate* isolate, WasmInterpretationResult InterpretWasmModule(
Handle<WasmInstanceObject> instance, Isolate* isolate, Handle<WasmInstanceObject> instance,
ErrorThrower* thrower, int32_t function_index, int32_t function_index, WasmValue* args);
WasmValue* args, bool* possible_nondeterminism);
// Runs the module instance with arguments. // Runs the module instance with arguments.
int32_t RunWasmModuleForTesting(Isolate* isolate, int32_t RunWasmModuleForTesting(Isolate* isolate,
......
...@@ -316,29 +316,22 @@ int WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data, ...@@ -316,29 +316,22 @@ int WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data,
if (!compiles) return 0; if (!compiles) return 0;
int32_t result_interpreter;
bool possible_nondeterminism = false;
{
MaybeHandle<WasmInstanceObject> interpreter_instance = MaybeHandle<WasmInstanceObject> interpreter_instance =
i_isolate->wasm_engine()->SyncInstantiate( i_isolate->wasm_engine()->SyncInstantiate(
i_isolate, &interpreter_thrower, compiled_module.ToHandleChecked(), i_isolate, &interpreter_thrower, compiled_module.ToHandleChecked(),
MaybeHandle<JSReceiver>(), MaybeHandle<JSArrayBuffer>()); MaybeHandle<JSReceiver>(), MaybeHandle<JSArrayBuffer>());
// Ignore instantiation failure. // Ignore instantiation failure.
if (interpreter_thrower.error()) { if (interpreter_thrower.error()) return 0;
return 0;
}
result_interpreter = testing::InterpretWasmModule( testing::WasmInterpretationResult interpreter_result =
i_isolate, interpreter_instance.ToHandleChecked(), &interpreter_thrower, testing::InterpretWasmModule(i_isolate,
0, interpreter_args.get(), &possible_nondeterminism); interpreter_instance.ToHandleChecked(), 0,
} interpreter_args.get());
// Do not execute the generated code if the interpreter did not finished after // Do not execute the generated code if the interpreter did not finished after
// a bounded number of steps. // a bounded number of steps.
if (interpreter_thrower.error()) { if (interpreter_result.stopped()) return 0;
return 0;
}
// The WebAssembly spec allows the sign bit of NaN to be non-deterministic. // The WebAssembly spec allows the sign bit of NaN to be non-deterministic.
// This sign bit can make the difference between an infinite loop and // This sign bit can make the difference between an infinite loop and
...@@ -346,12 +339,7 @@ int WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data, ...@@ -346,12 +339,7 @@ int WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data,
// the generated code will not go into an infinite loop and cause a timeout in // the generated code will not go into an infinite loop and cause a timeout in
// Clusterfuzz. Therefore we do not execute the generated code if the result // Clusterfuzz. Therefore we do not execute the generated code if the result
// may be non-deterministic. // may be non-deterministic.
if (possible_nondeterminism) { if (interpreter_result.possible_nondeterminism()) return 0;
return 0;
}
bool expect_exception =
result_interpreter == static_cast<int32_t>(0xDEADBEEF);
int32_t result_compiled; int32_t result_compiled;
{ {
...@@ -367,13 +355,16 @@ int WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data, ...@@ -367,13 +355,16 @@ int WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data,
"main", num_args, compiler_args.get()); "main", num_args, compiler_args.get());
} }
if (expect_exception != i_isolate->has_pending_exception()) { if (interpreter_result.trapped() != i_isolate->has_pending_exception()) {
const char* exception_text[] = {"no exception", "exception"}; const char* exception_text[] = {"no exception", "exception"};
FATAL("interpreter: %s; compiled: %s", exception_text[expect_exception], FATAL("interpreter: %s; compiled: %s",
exception_text[interpreter_result.trapped()],
exception_text[i_isolate->has_pending_exception()]); exception_text[i_isolate->has_pending_exception()]);
} }
if (!expect_exception) CHECK_EQ(result_interpreter, result_compiled); if (!interpreter_result.trapped()) {
CHECK_EQ(interpreter_result.result(), result_compiled);
}
// Cleanup any pending exception. // Cleanup any pending exception.
i_isolate->clear_pending_exception(); i_isolate->clear_pending_exception();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment