Commit 242df3a2 authored by Clemens Backes's avatar Clemens Backes Committed by Commit Bot

[wasm][fuzzer] Check result of compiled code vs interpreter

The plain "wasm fuzzer" (which takes the fuzzer input as the wasm wire
bytes) was already running both the interpreter and compiled code, but
it did not compare the results of both.
This CL fixes this by reusing some logic that was already present in the
fuzzers based on the {WasmCompileFuzzer} class.

R=ahaas@chromium.org

Bug: chromium:1113681, chromium:1112099
Change-Id: I9d407f66dfcba0eec90f050630b028edd5fae1d1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2339624
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69310}
parent d16f404c
...@@ -1182,9 +1182,9 @@ class WasmInterpreterInternals { ...@@ -1182,9 +1182,9 @@ class WasmInterpreterInternals {
TrapReason GetTrapReason() { return trap_reason_; } TrapReason GetTrapReason() { return trap_reason_; }
bool PossibleNondeterminism() { return possible_nondeterminism_; } bool PossibleNondeterminism() const { return possible_nondeterminism_; }
uint64_t NumInterpretedCalls() { return num_interpreted_calls_; } uint64_t NumInterpretedCalls() const { return num_interpreted_calls_; }
CodeMap* codemap() { return &codemap_; } CodeMap* codemap() { return &codemap_; }
...@@ -3856,7 +3856,9 @@ WasmInterpreter::WasmInterpreter(Isolate* isolate, const WasmModule* module, ...@@ -3856,7 +3856,9 @@ WasmInterpreter::WasmInterpreter(Isolate* isolate, const WasmModule* module,
// used in the {unique_ptr} in the header. // used in the {unique_ptr} in the header.
WasmInterpreter::~WasmInterpreter() = default; WasmInterpreter::~WasmInterpreter() = default;
WasmInterpreter::State WasmInterpreter::state() { return internals_->state(); } WasmInterpreter::State WasmInterpreter::state() const {
return internals_->state();
}
void WasmInterpreter::InitFrame(const WasmFunction* function, WasmValue* args) { void WasmInterpreter::InitFrame(const WasmFunction* function, WasmValue* args) {
internals_->InitFrame(function, args); internals_->InitFrame(function, args);
...@@ -3870,19 +3872,19 @@ void WasmInterpreter::Pause() { internals_->Pause(); } ...@@ -3870,19 +3872,19 @@ void WasmInterpreter::Pause() { internals_->Pause(); }
void WasmInterpreter::Reset() { internals_->Reset(); } void WasmInterpreter::Reset() { internals_->Reset(); }
WasmValue WasmInterpreter::GetReturnValue(int index) { WasmValue WasmInterpreter::GetReturnValue(int index) const {
return internals_->GetReturnValue(index); return internals_->GetReturnValue(index);
} }
TrapReason WasmInterpreter::GetTrapReason() { TrapReason WasmInterpreter::GetTrapReason() const {
return internals_->GetTrapReason(); return internals_->GetTrapReason();
} }
bool WasmInterpreter::PossibleNondeterminism() { bool WasmInterpreter::PossibleNondeterminism() const {
return internals_->PossibleNondeterminism(); return internals_->PossibleNondeterminism();
} }
uint64_t WasmInterpreter::NumInterpretedCalls() { uint64_t WasmInterpreter::NumInterpretedCalls() const {
return internals_->NumInterpretedCalls(); return internals_->NumInterpretedCalls();
} }
......
...@@ -66,7 +66,7 @@ class WasmInterpreter { ...@@ -66,7 +66,7 @@ class WasmInterpreter {
//========================================================================== //==========================================================================
// Execution controls. // Execution controls.
//========================================================================== //==========================================================================
State state(); State state() const;
void InitFrame(const WasmFunction* function, WasmValue* args); void InitFrame(const WasmFunction* function, WasmValue* args);
// Pass -1 as num_steps to run till completion, pause or breakpoint. // Pass -1 as num_steps to run till completion, pause or breakpoint.
State Run(int num_steps = -1); State Run(int num_steps = -1);
...@@ -75,16 +75,16 @@ class WasmInterpreter { ...@@ -75,16 +75,16 @@ class WasmInterpreter {
void Reset(); void Reset();
// Stack inspection and modification. // Stack inspection and modification.
WasmValue GetReturnValue(int index = 0); WasmValue GetReturnValue(int index = 0) const;
TrapReason GetTrapReason(); TrapReason GetTrapReason() const;
// Returns true if the thread executed an instruction which may produce // Returns true if the thread executed an instruction which may produce
// nondeterministic results, e.g. float div, float sqrt, and float mul, // nondeterministic results, e.g. float div, float sqrt, and float mul,
// where the sign bit of a NaN is nondeterministic. // where the sign bit of a NaN is nondeterministic.
bool PossibleNondeterminism(); bool PossibleNondeterminism() const;
// Returns the number of calls / function frames executed on this thread. // Returns the number of calls / function frames executed on this thread.
uint64_t NumInterpretedCalls(); uint64_t NumInterpretedCalls() const;
//========================================================================== //==========================================================================
// Testing functionality. // Testing functionality.
......
...@@ -41,16 +41,39 @@ MaybeHandle<WasmInstanceObject> CompileAndInstantiateForTesting( ...@@ -41,16 +41,39 @@ MaybeHandle<WasmInstanceObject> CompileAndInstantiateForTesting(
isolate, thrower, module.ToHandleChecked(), {}, {}); isolate, thrower, module.ToHandleChecked(), {}, {});
} }
bool InterpretWasmModuleForTesting(Isolate* isolate, WasmInterpretationResult GetInterpretationResult(
Handle<WasmInstanceObject> instance, Isolate* isolate, const WasmInterpreter& interpreter,
size_t argc, WasmValue* args) { WasmInterpreter::State interpreter_result) {
bool stack_overflow = isolate->has_pending_exception();
isolate->clear_pending_exception();
if (stack_overflow) return WasmInterpretationResult::Failed();
if (interpreter.state() == WasmInterpreter::TRAPPED) {
return WasmInterpretationResult::Trapped(
interpreter.PossibleNondeterminism());
}
if (interpreter_result == WasmInterpreter::FINISHED) {
return WasmInterpretationResult::Finished(
interpreter.GetReturnValue().to<int32_t>(),
interpreter.PossibleNondeterminism());
}
// The interpreter did not finish within the limited number of steps, so it
// might execute an infinite loop or infinite recursion. Return "failed"
// status in that case.
return WasmInterpretationResult::Failed();
}
WasmInterpretationResult InterpretWasmModuleForTesting(
Isolate* isolate, Handle<WasmInstanceObject> instance, size_t argc,
WasmValue* args) {
HandleScope handle_scope(isolate); // Avoid leaking handles. HandleScope handle_scope(isolate); // Avoid leaking handles.
WasmCodeRefScope code_ref_scope; WasmCodeRefScope code_ref_scope;
MaybeHandle<WasmExportedFunction> maybe_function =
GetExportedFunction(isolate, instance, "main");
Handle<WasmExportedFunction> function; Handle<WasmExportedFunction> function;
if (!maybe_function.ToHandle(&function)) { if (!GetExportedFunction(isolate, instance, "main").ToHandle(&function)) {
return false; return WasmInterpretationResult::Failed();
} }
int function_index = function->function_index(); int function_index = function->function_index();
const FunctionSig* signature = const FunctionSig* signature =
...@@ -106,13 +129,7 @@ bool InterpretWasmModuleForTesting(Isolate* isolate, ...@@ -106,13 +129,7 @@ bool InterpretWasmModuleForTesting(Isolate* isolate,
arguments.get()); arguments.get());
WasmInterpreter::State interpreter_result = interpreter.Run(kMaxNumSteps); WasmInterpreter::State interpreter_result = interpreter.Run(kMaxNumSteps);
if (isolate->has_pending_exception()) { return GetInterpretationResult(isolate, interpreter, interpreter_result);
// Stack overflow during interpretation.
isolate->clear_pending_exception();
return false;
}
return interpreter_result != WasmInterpreter::PAUSED;
} }
int32_t RunWasmModuleForTesting(Isolate* isolate, int32_t RunWasmModuleForTesting(Isolate* isolate,
...@@ -152,23 +169,7 @@ WasmInterpretationResult InterpretWasmModule( ...@@ -152,23 +169,7 @@ WasmInterpretationResult InterpretWasmModule(
interpreter.InitFrame(&instance->module()->functions[function_index], args); interpreter.InitFrame(&instance->module()->functions[function_index], args);
WasmInterpreter::State interpreter_result = interpreter.Run(kMaxNumSteps); WasmInterpreter::State interpreter_result = interpreter.Run(kMaxNumSteps);
bool stack_overflow = isolate->has_pending_exception(); return GetInterpretationResult(isolate, interpreter, interpreter_result);
isolate->clear_pending_exception();
if (stack_overflow) return WasmInterpretationResult::Stopped();
if (interpreter.state() == WasmInterpreter::TRAPPED) {
return WasmInterpretationResult::Trapped(
interpreter.PossibleNondeterminism());
}
if (interpreter_result == WasmInterpreter::FINISHED) {
return WasmInterpretationResult::Finished(
interpreter.GetReturnValue().to<int32_t>(),
interpreter.PossibleNondeterminism());
}
return WasmInterpretationResult::Stopped();
} }
MaybeHandle<WasmExportedFunction> GetExportedFunction( MaybeHandle<WasmExportedFunction> GetExportedFunction(
......
...@@ -37,14 +37,6 @@ int32_t CallWasmFunctionForTesting(Isolate* isolate, ...@@ -37,14 +37,6 @@ int32_t CallWasmFunctionForTesting(Isolate* isolate,
ErrorThrower* thrower, const char* name, ErrorThrower* thrower, const char* name,
int argc, Handle<Object> argv[]); int argc, Handle<Object> argv[]);
// Interprets the exported wasm function "main". Returns false if it was not
// possible to execute the function (e.g. because it does not exist), or if the
// interpretation does not finish after kMaxNumSteps. Otherwise returns true.
// The arguments array is extended with default values if necessary.
bool InterpretWasmModuleForTesting(Isolate* isolate,
Handle<WasmInstanceObject> instance,
size_t argc, WasmValue* args);
// Decode, verify, and run the function labeled "main" in the // Decode, verify, and run the function labeled "main" in the
// given encoded module. The module should have no imports. // given encoded module. The module should have no imports.
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start, int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
...@@ -61,7 +53,7 @@ MaybeHandle<WasmInstanceObject> CompileAndInstantiateForTesting( ...@@ -61,7 +53,7 @@ MaybeHandle<WasmInstanceObject> CompileAndInstantiateForTesting(
class WasmInterpretationResult { class WasmInterpretationResult {
public: public:
static WasmInterpretationResult Stopped() { return {kStopped, 0, false}; } static WasmInterpretationResult Failed() { return {kFailed, 0, false}; }
static WasmInterpretationResult Trapped(bool possible_nondeterminism) { static WasmInterpretationResult Trapped(bool possible_nondeterminism) {
return {kTrapped, 0, possible_nondeterminism}; return {kTrapped, 0, possible_nondeterminism};
} }
...@@ -70,7 +62,10 @@ class WasmInterpretationResult { ...@@ -70,7 +62,10 @@ class WasmInterpretationResult {
return {kFinished, result, possible_nondeterminism}; return {kFinished, result, possible_nondeterminism};
} }
bool stopped() const { return status_ == kStopped; } // {failed()} captures different reasons: The module was invalid, no function
// to call was found in the module, the function did not termine within a
// limited number of steps, or a stack overflow happened.
bool failed() const { return status_ == kFailed; }
bool trapped() const { return status_ == kTrapped; } bool trapped() const { return status_ == kTrapped; }
bool finished() const { return status_ == kFinished; } bool finished() const { return status_ == kFinished; }
...@@ -82,7 +77,7 @@ class WasmInterpretationResult { ...@@ -82,7 +77,7 @@ class WasmInterpretationResult {
bool possible_nondeterminism() const { return possible_nondeterminism_; } bool possible_nondeterminism() const { return possible_nondeterminism_; }
private: private:
enum Status { kFinished, kTrapped, kStopped }; enum Status { kFinished, kTrapped, kFailed };
const Status status_; const Status status_;
const int32_t result_; const int32_t result_;
...@@ -102,6 +97,14 @@ WasmInterpretationResult InterpretWasmModule( ...@@ -102,6 +97,14 @@ WasmInterpretationResult InterpretWasmModule(
Isolate* isolate, Handle<WasmInstanceObject> instance, Isolate* isolate, Handle<WasmInstanceObject> instance,
int32_t function_index, WasmValue* args); int32_t function_index, WasmValue* args);
// Interprets the exported wasm function "main". Returns a "failed" result if it
// was not possible to execute the function (e.g. because it does not exist), or
// if the interpretation does not finish after kMaxNumSteps. The arguments array
// is extended with default values if necessary.
WasmInterpretationResult InterpretWasmModuleForTesting(
Isolate* isolate, Handle<WasmInstanceObject> instance, size_t argc,
WasmValue* args);
// Runs the module instance with arguments. // Runs the module instance with arguments.
int32_t RunWasmModuleForTesting(Isolate* isolate, int32_t RunWasmModuleForTesting(Isolate* isolate,
Handle<WasmInstanceObject> instance, int argc, Handle<WasmInstanceObject> instance, int argc,
......
...@@ -46,10 +46,9 @@ void InterpretAndExecuteModule(i::Isolate* isolate, ...@@ -46,10 +46,9 @@ void InterpretAndExecuteModule(i::Isolate* isolate,
thrower.Reset(); // Ignore errors. thrower.Reset(); // Ignore errors.
return; return;
} }
if (!testing::InterpretWasmModuleForTesting(isolate, instance, 0, nullptr)) { testing::WasmInterpretationResult interpreter_result =
isolate->clear_pending_exception(); testing::InterpretWasmModuleForTesting(isolate, instance, 0, nullptr);
return; if (interpreter_result.failed()) return;
}
// Try to instantiate and execute the module_object. // Try to instantiate and execute the module_object.
maybe_instance = isolate->wasm_engine()->SyncInstantiate( maybe_instance = isolate->wasm_engine()->SyncInstantiate(
...@@ -61,10 +60,21 @@ void InterpretAndExecuteModule(i::Isolate* isolate, ...@@ -61,10 +60,21 @@ void InterpretAndExecuteModule(i::Isolate* isolate,
thrower.Reset(); // Ignore errors. thrower.Reset(); // Ignore errors.
return; return;
} }
if (testing::RunWasmModuleForTesting(isolate, instance, 0, nullptr) < 0) { int32_t result_compiled =
isolate->clear_pending_exception(); testing::RunWasmModuleForTesting(isolate, instance, 0, nullptr);
return; if (interpreter_result.trapped() != isolate->has_pending_exception()) {
const char* exception_text[] = {"no exception", "exception"};
FATAL("interpreter: %s; compiled: %s",
exception_text[interpreter_result.trapped()],
exception_text[isolate->has_pending_exception()]);
} }
if (interpreter_result.finished()) {
CHECK_EQ(interpreter_result.result(), result_compiled);
}
// Cleanup any pending exception.
isolate->clear_pending_exception();
} }
namespace { namespace {
...@@ -320,7 +330,6 @@ void WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data, ...@@ -320,7 +330,6 @@ void WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data,
ErrorThrower interpreter_thrower(i_isolate, "Interpreter"); ErrorThrower interpreter_thrower(i_isolate, "Interpreter");
ModuleWireBytes wire_bytes(buffer.begin(), buffer.end()); ModuleWireBytes wire_bytes(buffer.begin(), buffer.end());
// Compile with Turbofan here. Liftoff will be tested later.
auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate); auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
MaybeHandle<WasmModuleObject> compiled_module; MaybeHandle<WasmModuleObject> compiled_module;
{ {
...@@ -361,7 +370,7 @@ void WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data, ...@@ -361,7 +370,7 @@ void WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data,
// 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_result.stopped()) return; if (interpreter_result.failed()) return;
// 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
......
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