[wasm] Avoid executing infinite loops in the wasm fuzzers
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}
This commit is contained in:
parent
1db428964a
commit
7b53a0e010
@ -223,9 +223,9 @@ class WasmInstanceObject : public JSObject {
|
||||
class WasmExportedFunction : public JSFunction {
|
||||
public:
|
||||
WasmInstanceObject* instance();
|
||||
int function_index();
|
||||
V8_EXPORT_PRIVATE int function_index();
|
||||
|
||||
static WasmExportedFunction* cast(Object* object);
|
||||
V8_EXPORT_PRIVATE static WasmExportedFunction* cast(Object* object);
|
||||
static bool IsWasmExportedFunction(Object* object);
|
||||
|
||||
static Handle<WasmExportedFunction> New(Isolate* isolate,
|
||||
|
@ -42,6 +42,58 @@ std::unique_ptr<WasmModule> DecodeWasmModuleForTesting(
|
||||
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");
|
||||
@ -111,9 +163,9 @@ int32_t InterpretWasmModule(Isolate* isolate,
|
||||
}
|
||||
}
|
||||
|
||||
int32_t CallWasmFunctionForTesting(Isolate* isolate, Handle<JSObject> instance,
|
||||
ErrorThrower* thrower, const char* name,
|
||||
int argc, Handle<Object> argv[]) {
|
||||
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(
|
||||
@ -123,9 +175,20 @@ int32_t CallWasmFunctionForTesting(Isolate* isolate, Handle<JSObject> instance,
|
||||
PropertyDescriptor desc;
|
||||
Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
|
||||
isolate, exports_object, main_name, &desc);
|
||||
if (!property_found.FromMaybe(false)) return -1;
|
||||
if (!property_found.FromMaybe(false)) return {};
|
||||
|
||||
Handle<JSFunction> main_export = Handle<JSFunction>::cast(desc.value());
|
||||
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();
|
||||
|
@ -27,6 +27,12 @@ std::unique_ptr<WasmModule> DecodeWasmModuleForTesting(
|
||||
Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
|
||||
const byte* module_end, ModuleOrigin origin, bool verify_functions = false);
|
||||
|
||||
// Returns a MaybeHandle to the JsToWasm wrapper of the wasm function exported
|
||||
// with the given name by the provided instance.
|
||||
MaybeHandle<WasmExportedFunction> GetExportedFunction(Isolate* isolate,
|
||||
Handle<JSObject> instance,
|
||||
const char* name);
|
||||
|
||||
// Call an exported wasm function by name. Returns -1 if the export does not
|
||||
// exist or throws an error. Errors are cleared from the isolate before
|
||||
// returning.
|
||||
@ -34,6 +40,15 @@ int32_t CallWasmFunctionForTesting(Isolate* isolate, Handle<JSObject> instance,
|
||||
ErrorThrower* thrower, const char* name,
|
||||
int argc, Handle<Object> argv[]);
|
||||
|
||||
// Interprets an exported wasm function by name. 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,
|
||||
const char* name, size_t argc,
|
||||
WasmValue* args);
|
||||
|
||||
// Decode, verify, and run the function labeled "main" in the
|
||||
// given encoded module. The module should have no imports.
|
||||
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
|
||||
|
@ -116,6 +116,8 @@
|
||||
'wasm.cc',
|
||||
'../common/wasm/wasm-module-runner.cc',
|
||||
'../common/wasm/wasm-module-runner.h',
|
||||
'wasm-fuzzer-common.cc',
|
||||
'wasm-fuzzer-common.h',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -145,6 +147,8 @@
|
||||
'wasm-async.cc',
|
||||
'../common/wasm/wasm-module-runner.cc',
|
||||
'../common/wasm/wasm-module-runner.h',
|
||||
'wasm-fuzzer-common.cc',
|
||||
'wasm-fuzzer-common.h',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "test/common/wasm/flag-utils.h"
|
||||
#include "test/common/wasm/wasm-module-runner.h"
|
||||
#include "test/fuzzer/fuzzer-support.h"
|
||||
#include "test/fuzzer/wasm-fuzzer-common.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -57,19 +58,9 @@ void InstantiateCallback(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
|
||||
ScheduledErrorThrower thrower(i_isolate, "WebAssembly Instantiation");
|
||||
|
||||
Handle<WasmModuleObject> module_obj =
|
||||
ToWasmModuleObjectUnchecked(Utils::OpenHandle(v8::Object::Cast(*module)));
|
||||
MaybeHandle<WasmInstanceObject> maybe_instance =
|
||||
SyncInstantiate(i_isolate, &thrower, module_obj,
|
||||
Handle<JSReceiver>::null(), // imports
|
||||
MaybeHandle<JSArrayBuffer>()); // memory
|
||||
Handle<WasmInstanceObject> instance;
|
||||
if (!maybe_instance.ToHandle(&instance)) {
|
||||
return;
|
||||
}
|
||||
testing::RunWasmModuleForTesting(i_isolate, instance, 0, nullptr);
|
||||
InterpretAndExecuteModule(i_isolate, module_obj);
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "include/v8.h"
|
||||
#include "src/isolate.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/wasm/wasm-api.h"
|
||||
#include "src/wasm/wasm-module-builder.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/zone/accounting-allocator.h"
|
||||
@ -66,6 +67,30 @@ int FuzzWasmSection(SectionCode section, const uint8_t* data, size_t size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InterpretAndExecuteModule(i::Isolate* isolate,
|
||||
Handle<WasmModuleObject> module_object) {
|
||||
ScheduledErrorThrower thrower(isolate, "WebAssembly Instantiation");
|
||||
// Try to instantiate and interpret the module_object.
|
||||
MaybeHandle<WasmInstanceObject> maybe_instance =
|
||||
SyncInstantiate(isolate, &thrower, module_object,
|
||||
Handle<JSReceiver>::null(), // imports
|
||||
MaybeHandle<JSArrayBuffer>()); // memory
|
||||
Handle<WasmInstanceObject> instance;
|
||||
if (!maybe_instance.ToHandle(&instance)) return;
|
||||
if (!testing::InterpretWasmModuleForTesting(isolate, instance, "main", 0,
|
||||
nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Instantiate and execute the module_object.
|
||||
maybe_instance = SyncInstantiate(isolate, &thrower, module_object,
|
||||
Handle<JSReceiver>::null(), // imports
|
||||
MaybeHandle<JSArrayBuffer>()); // memory
|
||||
if (!maybe_instance.ToHandle(&instance)) return;
|
||||
|
||||
testing::RunWasmModuleForTesting(isolate, instance, 0, nullptr);
|
||||
}
|
||||
|
||||
int WasmExecutionFuzzer::FuzzWasmModule(
|
||||
const uint8_t* data, size_t size) {
|
||||
// Save the flag so that we can change it and restore it later.
|
||||
|
@ -17,8 +17,14 @@ namespace internal {
|
||||
namespace wasm {
|
||||
namespace fuzzer {
|
||||
|
||||
int FuzzWasmSection(v8::internal::wasm::SectionCode section,
|
||||
const uint8_t* data, size_t size);
|
||||
int FuzzWasmSection(SectionCode section, const uint8_t* data, size_t size);
|
||||
|
||||
// First instantiates and interprets the "main" function within module_object if
|
||||
// possible. If the interpretation finishes within kMaxSteps steps,
|
||||
// module_object is instantiated again and the compiled "main" function is
|
||||
// executed.
|
||||
void InterpretAndExecuteModule(Isolate* isolate,
|
||||
Handle<WasmModuleObject> module_object);
|
||||
|
||||
class WasmExecutionFuzzer {
|
||||
public:
|
||||
|
@ -16,16 +16,17 @@
|
||||
#include "test/common/wasm/flag-utils.h"
|
||||
#include "test/common/wasm/wasm-module-runner.h"
|
||||
#include "test/fuzzer/fuzzer-support.h"
|
||||
#include "test/fuzzer/wasm-fuzzer-common.h"
|
||||
|
||||
namespace i = v8::internal;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
v8::internal::FlagScope<uint32_t> max_mem_flag_scope(
|
||||
&v8::internal::FLAG_wasm_max_mem_pages, 32);
|
||||
v8::internal::FlagScope<uint32_t> max_table_size_scope(
|
||||
&v8::internal::FLAG_wasm_max_table_size, 100);
|
||||
i::FlagScope<uint32_t> max_mem_flag_scope(&i::FLAG_wasm_max_mem_pages, 32);
|
||||
i::FlagScope<uint32_t> max_table_size_scope(&i::FLAG_wasm_max_table_size,
|
||||
100);
|
||||
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
|
||||
v8::Isolate* isolate = support->GetIsolate();
|
||||
v8::internal::Isolate* i_isolate =
|
||||
reinterpret_cast<v8::internal::Isolate*>(isolate);
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
|
||||
// Clear any pending exceptions from a prior run.
|
||||
if (i_isolate->has_pending_exception()) {
|
||||
@ -36,8 +37,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Context::Scope context_scope(support->GetContext());
|
||||
v8::TryCatch try_catch(isolate);
|
||||
v8::internal::wasm::testing::SetupIsolateForWasmModule(i_isolate);
|
||||
v8::internal::wasm::testing::CompileAndRunWasmModule(i_isolate, data,
|
||||
data + size);
|
||||
i::wasm::testing::SetupIsolateForWasmModule(i_isolate);
|
||||
|
||||
i::HandleScope scope(i_isolate);
|
||||
i::wasm::ErrorThrower thrower(i_isolate, "wasm fuzzer");
|
||||
i::MaybeHandle<i::WasmModuleObject> maybe_object = SyncCompile(
|
||||
i_isolate, &thrower, i::wasm::ModuleWireBytes(data, data + size));
|
||||
i::Handle<i::WasmModuleObject> module_object;
|
||||
if (maybe_object.ToHandle(&module_object)) {
|
||||
i::wasm::fuzzer::InterpretAndExecuteModule(i_isolate, module_object);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
BIN
test/fuzzer/wasm_async/regression-761784.wasm
Normal file
BIN
test/fuzzer/wasm_async/regression-761784.wasm
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user