7b53a0e010
The wasm-async fuzzer uses the bytes provided by the fuzzer engine directly as wasm module bytes, compiles them with async compilation, and then tries to execute the "main" function of the module. This "main" can have an infinite loop which causes a timeout in the fuzzer. With this CL the "main" function is first executed with the interpreter. If the execution in the interpreter finishes within 16k steps, which means that there is no infinite loop, also the compiled code is executed. I added the raw fuzzer input as a test case because in this case I really want to test the fuzzer and not V8. R=clemensh@chromium.org Bug: chromium:761784 Change-Id: Id1fe5da0da8670ec821ab9979fdb9454dbde1162 Reviewed-on: https://chromium-review.googlesource.com/651046 Commit-Queue: Andreas Haas <ahaas@chromium.org> Reviewed-by: Clemens Hammacher <clemensh@chromium.org> Cr-Commit-Position: refs/heads/master@{#47874}
225 lines
8.3 KiB
C++
225 lines
8.3 KiB
C++
// Copyright 2016 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "test/common/wasm/wasm-module-runner.h"
|
|
|
|
#include "src/handles.h"
|
|
#include "src/isolate.h"
|
|
#include "src/objects-inl.h"
|
|
#include "src/objects.h"
|
|
#include "src/property-descriptor.h"
|
|
#include "src/wasm/module-decoder.h"
|
|
#include "src/wasm/wasm-interpreter.h"
|
|
#include "src/wasm/wasm-js.h"
|
|
#include "src/wasm/wasm-module.h"
|
|
#include "src/wasm/wasm-objects.h"
|
|
#include "src/wasm/wasm-result.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace wasm {
|
|
namespace testing {
|
|
|
|
uint32_t GetInitialMemSize(const WasmModule* module) {
|
|
return WasmModule::kPageSize * module->initial_pages;
|
|
}
|
|
|
|
std::unique_ptr<WasmModule> DecodeWasmModuleForTesting(
|
|
Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
|
|
const byte* module_end, ModuleOrigin origin, bool verify_functions) {
|
|
// Decode the module, but don't verify function bodies, since we'll
|
|
// be compiling them anyway.
|
|
ModuleResult decoding_result = SyncDecodeWasmModule(
|
|
isolate, module_start, module_end, verify_functions, origin);
|
|
|
|
if (decoding_result.failed()) {
|
|
// Module verification failed. throw.
|
|
thrower->CompileError("DecodeWasmModule failed: %s",
|
|
decoding_result.error_msg().c_str());
|
|
}
|
|
|
|
return std::move(decoding_result.val);
|
|
}
|
|
|
|
bool InterpretWasmModuleForTesting(Isolate* isolate,
|
|
Handle<WasmInstanceObject> instance,
|
|
const char* name, size_t argc,
|
|
WasmValue* args) {
|
|
MaybeHandle<WasmExportedFunction> maybe_function =
|
|
GetExportedFunction(isolate, instance, "main");
|
|
Handle<WasmExportedFunction> function;
|
|
if (!maybe_function.ToHandle(&function)) {
|
|
return false;
|
|
}
|
|
int function_index = function->function_index();
|
|
FunctionSig* signature = instance->module()->functions[function_index].sig;
|
|
size_t param_count = signature->parameter_count();
|
|
std::unique_ptr<WasmValue[]> arguments(new WasmValue[param_count]);
|
|
|
|
memcpy(arguments.get(), args, std::min(param_count, argc));
|
|
|
|
// Fill the parameters up with default values.
|
|
for (size_t i = argc; i < param_count; ++i) {
|
|
switch (signature->GetParam(i)) {
|
|
case MachineRepresentation::kWord32:
|
|
arguments[i] = WasmValue(int32_t{0});
|
|
break;
|
|
case MachineRepresentation::kWord64:
|
|
arguments[i] = WasmValue(int64_t{0});
|
|
break;
|
|
case MachineRepresentation::kFloat32:
|
|
arguments[i] = WasmValue(0.0f);
|
|
break;
|
|
case MachineRepresentation::kFloat64:
|
|
arguments[i] = WasmValue(0.0);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
// Don't execute more than 16k steps.
|
|
constexpr int kMaxNumSteps = 16 * 1024;
|
|
|
|
Zone zone(isolate->allocator(), ZONE_NAME);
|
|
|
|
WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
|
|
WasmInterpreter::HeapObjectsScope heap_objects_scope(interpreter, instance);
|
|
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
|
|
thread->Reset();
|
|
thread->InitFrame(&(instance->module()->functions[function_index]), args);
|
|
WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
|
|
|
|
return interpreter_result != WasmInterpreter::PAUSED;
|
|
}
|
|
|
|
int32_t RunWasmModuleForTesting(Isolate* isolate, Handle<JSObject> instance,
|
|
int argc, Handle<Object> argv[]) {
|
|
ErrorThrower thrower(isolate, "RunWasmModule");
|
|
return CallWasmFunctionForTesting(isolate, instance, &thrower, "main", argc,
|
|
argv);
|
|
}
|
|
|
|
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
|
|
const byte* module_end) {
|
|
HandleScope scope(isolate);
|
|
ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
|
|
MaybeHandle<WasmInstanceObject> instance = SyncCompileAndInstantiate(
|
|
isolate, &thrower, ModuleWireBytes(module_start, module_end), {}, {});
|
|
if (instance.is_null()) {
|
|
return -1;
|
|
}
|
|
return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
|
|
nullptr);
|
|
}
|
|
|
|
int32_t CompileAndRunAsmWasmModule(Isolate* isolate, const byte* module_start,
|
|
const byte* module_end) {
|
|
HandleScope scope(isolate);
|
|
ErrorThrower thrower(isolate, "CompileAndRunAsmWasmModule");
|
|
MaybeHandle<WasmModuleObject> module = wasm::SyncCompileTranslatedAsmJs(
|
|
isolate, &thrower, ModuleWireBytes(module_start, module_end),
|
|
Handle<Script>::null(), Vector<const byte>());
|
|
DCHECK_EQ(thrower.error(), module.is_null());
|
|
if (module.is_null()) return -1;
|
|
|
|
MaybeHandle<WasmInstanceObject> instance = wasm::SyncInstantiate(
|
|
isolate, &thrower, module.ToHandleChecked(), Handle<JSReceiver>::null(),
|
|
Handle<JSArrayBuffer>::null());
|
|
DCHECK_EQ(thrower.error(), instance.is_null());
|
|
if (instance.is_null()) return -1;
|
|
|
|
return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
|
|
nullptr);
|
|
}
|
|
int32_t InterpretWasmModule(Isolate* isolate,
|
|
Handle<WasmInstanceObject> instance,
|
|
ErrorThrower* thrower, int32_t function_index,
|
|
WasmValue* args, bool* possible_nondeterminism) {
|
|
// Don't execute more than 16k steps.
|
|
constexpr int kMaxNumSteps = 16 * 1024;
|
|
|
|
Zone zone(isolate->allocator(), ZONE_NAME);
|
|
v8::internal::HandleScope scope(isolate);
|
|
|
|
WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
|
|
WasmInterpreter::HeapObjectsScope heap_objects_scope(interpreter, instance);
|
|
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
|
|
thread->Reset();
|
|
thread->InitFrame(&(instance->module()->functions[function_index]), args);
|
|
WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
|
|
|
|
*possible_nondeterminism = thread->PossibleNondeterminism();
|
|
if (interpreter_result == WasmInterpreter::FINISHED) {
|
|
WasmValue val = thread->GetReturnValue();
|
|
return val.to<int32_t>();
|
|
} else if (thread->state() == WasmInterpreter::TRAPPED) {
|
|
return 0xdeadbeef;
|
|
} else {
|
|
thrower->RangeError(
|
|
"Interpreter did not finish execution within its step bound");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
MaybeHandle<WasmExportedFunction> GetExportedFunction(Isolate* isolate,
|
|
Handle<JSObject> instance,
|
|
const char* name) {
|
|
Handle<JSObject> exports_object;
|
|
Handle<Name> exports = isolate->factory()->InternalizeUtf8String("exports");
|
|
exports_object = Handle<JSObject>::cast(
|
|
JSObject::GetProperty(instance, exports).ToHandleChecked());
|
|
|
|
Handle<Name> main_name = isolate->factory()->NewStringFromAsciiChecked(name);
|
|
PropertyDescriptor desc;
|
|
Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
|
|
isolate, exports_object, main_name, &desc);
|
|
if (!property_found.FromMaybe(false)) return {};
|
|
|
|
return Handle<WasmExportedFunction>::cast(desc.value());
|
|
}
|
|
|
|
int32_t CallWasmFunctionForTesting(Isolate* isolate, Handle<JSObject> instance,
|
|
ErrorThrower* thrower, const char* name,
|
|
int argc, Handle<Object> argv[]) {
|
|
MaybeHandle<WasmExportedFunction> maybe_export =
|
|
GetExportedFunction(isolate, instance, name);
|
|
Handle<WasmExportedFunction> main_export;
|
|
if (!maybe_export.ToHandle(&main_export)) {
|
|
return -1;
|
|
}
|
|
|
|
// Call the JS function.
|
|
Handle<Object> undefined = isolate->factory()->undefined_value();
|
|
MaybeHandle<Object> retval =
|
|
Execution::Call(isolate, main_export, undefined, argc, argv);
|
|
|
|
// The result should be a number.
|
|
if (retval.is_null()) {
|
|
DCHECK(isolate->has_pending_exception());
|
|
isolate->clear_pending_exception();
|
|
thrower->RuntimeError("Calling exported wasm function failed.");
|
|
return -1;
|
|
}
|
|
Handle<Object> result = retval.ToHandleChecked();
|
|
if (result->IsSmi()) {
|
|
return Smi::ToInt(*result);
|
|
}
|
|
if (result->IsHeapNumber()) {
|
|
return static_cast<int32_t>(HeapNumber::cast(*result)->value());
|
|
}
|
|
thrower->RuntimeError(
|
|
"Calling exported wasm function failed: Return value should be number");
|
|
return -1;
|
|
}
|
|
|
|
void SetupIsolateForWasmModule(Isolate* isolate) {
|
|
WasmJs::Install(isolate, true);
|
|
}
|
|
|
|
} // namespace testing
|
|
} // namespace wasm
|
|
} // namespace internal
|
|
} // namespace v8
|