diff options
Diffstat (limited to 'deps/v8/test/fuzzer/wasm-fuzzer-common.cc')
-rw-r--r-- | deps/v8/test/fuzzer/wasm-fuzzer-common.cc | 272 |
1 files changed, 133 insertions, 139 deletions
diff --git a/deps/v8/test/fuzzer/wasm-fuzzer-common.cc b/deps/v8/test/fuzzer/wasm-fuzzer-common.cc index c44d011111..a85a74a2ff 100644 --- a/deps/v8/test/fuzzer/wasm-fuzzer-common.cc +++ b/deps/v8/test/fuzzer/wasm-fuzzer-common.cc @@ -15,6 +15,7 @@ #include "src/utils/ostreams.h" #include "src/wasm/baseline/liftoff-compiler.h" #include "src/wasm/function-body-decoder-impl.h" +#include "src/wasm/module-decoder-impl.h" #include "src/wasm/module-instantiate.h" #include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-feature-flags.h" @@ -28,42 +29,39 @@ #include "test/common/wasm/wasm-module-runner.h" #include "test/fuzzer/fuzzer-support.h" -namespace v8 { -namespace internal { -namespace wasm { -namespace fuzzer { +namespace v8::internal::wasm::fuzzer { // Compile a baseline module. We pass a pointer to a max step counter and a // nondeterminsm flag that are updated during execution by Liftoff. -Handle<WasmModuleObject> CompileReferenceModule(Zone* zone, Isolate* isolate, - ModuleWireBytes wire_bytes, - ErrorThrower* thrower, - int32_t* max_steps, - int32_t* nondeterminism) { +Handle<WasmModuleObject> CompileReferenceModule( + Isolate* isolate, base::Vector<const uint8_t> wire_bytes, + int32_t* max_steps, int32_t* nondeterminism) { // Create the native module. std::shared_ptr<NativeModule> native_module; constexpr bool kNoVerifyFunctions = false; - auto enabled_features = i::wasm::WasmFeatures::FromIsolate(isolate); - ModuleResult module_res = DecodeWasmModule( - enabled_features, wire_bytes.start(), wire_bytes.end(), - kNoVerifyFunctions, ModuleOrigin::kWasmOrigin, isolate->counters(), - isolate->metrics_recorder(), v8::metrics::Recorder::ContextId::Empty(), - DecodingMethod::kSync, GetWasmEngine()->allocator()); + auto enabled_features = WasmFeatures::FromIsolate(isolate); + ModuleResult module_res = + DecodeWasmModule(enabled_features, wire_bytes, kNoVerifyFunctions, + ModuleOrigin::kWasmOrigin); CHECK(module_res.ok()); std::shared_ptr<WasmModule> module = module_res.value(); CHECK_NOT_NULL(module); native_module = GetWasmEngine()->NewNativeModule(isolate, enabled_features, module, 0); - native_module->SetWireBytes( - base::OwnedVector<uint8_t>::Of(wire_bytes.module_bytes())); + native_module->SetWireBytes(base::OwnedVector<uint8_t>::Of(wire_bytes)); + // The module is known to be valid as this point (it was compiled by the + // caller before). + module->set_all_functions_validated(); // Compile all functions with Liftoff. WasmCodeRefScope code_ref_scope; auto env = native_module->CreateCompilationEnv(); + ModuleWireBytes wire_bytes_accessor{wire_bytes}; for (size_t i = module->num_imported_functions; i < module->functions.size(); ++i) { auto& func = module->functions[i]; - base::Vector<const uint8_t> func_code = wire_bytes.GetFunctionBytes(&func); + base::Vector<const uint8_t> func_code = + wire_bytes_accessor.GetFunctionBytes(&func); FunctionBody func_body(func.sig, func.code.offset(), func_code.begin(), func_code.end()); auto result = @@ -73,6 +71,11 @@ Handle<WasmModuleObject> CompileReferenceModule(Zone* zone, Isolate* isolate, .set_for_debugging(kForDebugging) .set_max_steps(max_steps) .set_nondeterminism(nondeterminism)); + if (!result.succeeded()) { + FATAL( + "Liftoff compilation failed on a valid module. Run with " + "--trace-wasm-decoder (in a debug build) to see why."); + } native_module->PublishCode( native_module->AddCompiledCode(std::move(result))); } @@ -86,27 +89,29 @@ Handle<WasmModuleObject> CompileReferenceModule(Zone* zone, Isolate* isolate, return WasmModuleObject::New(isolate, std::move(native_module), script); } -void InterpretAndExecuteModule(i::Isolate* isolate, - Handle<WasmModuleObject> module_object, - Handle<WasmModuleObject> module_ref, - int32_t* max_steps, int32_t* nondeterminism) { +void ExecuteAgainstReference(Isolate* isolate, + Handle<WasmModuleObject> module_object, + int32_t max_executed_instructions) { // We do not instantiate the module if there is a start function, because a // start function can contain an infinite loop which we cannot handle. if (module_object->module()->start_function_index >= 0) return; + int32_t max_steps = max_executed_instructions; + int32_t nondeterminism = 0; + HandleScope handle_scope(isolate); // Avoid leaking handles. + Zone reference_module_zone(isolate->allocator(), "wasm reference module"); + Handle<WasmModuleObject> module_ref = CompileReferenceModule( + isolate, module_object->native_module()->wire_bytes(), &max_steps, + &nondeterminism); Handle<WasmInstanceObject> instance_ref; - // Try to instantiate the reference instance, return if it fails. Use - // {module_ref} if provided (for "Liftoff as reference"), {module_object} - // otherwise (for "interpreter as reference"). + // Try to instantiate the reference instance, return if it fails. { - ErrorThrower thrower(isolate, "InterpretAndExecuteModule"); + ErrorThrower thrower(isolate, "ExecuteAgainstReference"); if (!GetWasmEngine() - ->SyncInstantiate( - isolate, &thrower, - module_ref.is_null() ? module_object : module_ref, {}, - {}) // no imports & memory + ->SyncInstantiate(isolate, &thrower, module_ref, {}, + {}) // no imports & memory .ToHandle(&instance_ref)) { isolate->clear_pending_exception(); thrower.Reset(); // Ignore errors. @@ -123,50 +128,20 @@ void InterpretAndExecuteModule(i::Isolate* isolate, base::OwnedVector<Handle<Object>> compiled_args = testing::MakeDefaultArguments(isolate, main_function->sig()); - bool exception_ref = false; - int32_t result_ref = 0; - - if (module_ref.is_null()) { - // Use the interpreter as reference. - base::OwnedVector<WasmValue> arguments = - testing::MakeDefaultInterpreterArguments(isolate, main_function->sig()); - - testing::WasmInterpretationResult interpreter_result = - testing::InterpretWasmModule(isolate, instance_ref, - main_function->function_index(), - arguments.begin()); - if (interpreter_result.failed()) return; - - // 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 - // terminating code. With possible non-determinism we cannot guarantee that - // 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 may be non-deterministic. - if (interpreter_result.possible_nondeterminism()) return; - if (interpreter_result.finished()) { - result_ref = interpreter_result.result(); - } else { - DCHECK(interpreter_result.trapped()); - exception_ref = true; - } - } else { - // Use Liftoff code as reference. - result_ref = testing::CallWasmFunctionForTesting( - isolate, instance_ref, "main", static_cast<int>(compiled_args.size()), - compiled_args.begin(), &exception_ref); - // Reached max steps, do not try to execute the test module as it might - // never terminate. - if (*max_steps == 0) return; - // If there is nondeterminism, we cannot guarantee the behavior of the test - // module, and in particular it may not terminate. - if (*nondeterminism != 0) return; - } + std::unique_ptr<const char[]> exception_ref; + int32_t result_ref = testing::CallWasmFunctionForTesting( + isolate, instance_ref, "main", compiled_args.as_vector(), &exception_ref); + // Reached max steps, do not try to execute the test module as it might + // never terminate. + if (max_steps < 0) return; + // If there is nondeterminism, we cannot guarantee the behavior of the test + // module, and in particular it may not terminate. + if (nondeterminism != 0) return; // Instantiate a fresh instance for the actual (non-ref) execution. Handle<WasmInstanceObject> instance; { - ErrorThrower thrower(isolate, "InterpretAndExecuteModule (second)"); + ErrorThrower thrower(isolate, "ExecuteAgainstReference (second)"); // We instantiated before, so the second instantiation must also succeed. if (!GetWasmEngine() ->SyncInstantiate(isolate, &thrower, module_object, {}, @@ -185,15 +160,14 @@ void InterpretAndExecuteModule(i::Isolate* isolate, DCHECK(!thrower.error()); } - bool exception = false; + std::unique_ptr<const char[]> exception; int32_t result = testing::CallWasmFunctionForTesting( - isolate, instance, "main", static_cast<int>(compiled_args.size()), - compiled_args.begin(), &exception); + isolate, instance, "main", compiled_args.as_vector(), &exception); - if (exception_ref != exception) { - const char* exception_text[] = {"no exception", "exception"}; - FATAL("expected: %s; got: %s", exception_text[exception_ref], - exception_text[exception]); + if ((exception_ref != nullptr) != (exception != nullptr)) { + FATAL("Exception mismatch! Expected: <%s>; got: <%s>", + exception_ref ? exception_ref.get() : "<no exception>", + exception ? exception.get() : "<no exception>"); } if (!exception) { @@ -396,6 +370,32 @@ class InitExprInterface { void BinOp(FullDecoder* decoder, WasmOpcode opcode, const Value& lhs, const Value& rhs, Value* result) { + switch (opcode) { + case kExprI32Add: + os_ << "kExprI32Add, "; + break; + case kExprI32Sub: + os_ << "kExprI32Sub, "; + break; + case kExprI32Mul: + os_ << "kExprI32Mul, "; + break; + case kExprI64Add: + os_ << "kExprI64Add, "; + break; + case kExprI64Sub: + os_ << "kExprI64Sub, "; + break; + case kExprI64Mul: + os_ << "kExprI64Mul, "; + break; + default: + UNREACHABLE(); + } + } + + void UnOp(FullDecoder* decoder, WasmOpcode opcode, const Value& value, + Value* result) { // TODO(12089): Implement. UNIMPLEMENTED(); } @@ -506,12 +506,10 @@ void DecodeAndAppendInitExpr(StdoutStream& os, Zone* zone, void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes, bool compiles) { constexpr bool kVerifyFunctions = false; - auto enabled_features = i::wasm::WasmFeatures::FromIsolate(isolate); - ModuleResult module_res = DecodeWasmModule( - enabled_features, wire_bytes.start(), wire_bytes.end(), kVerifyFunctions, - ModuleOrigin::kWasmOrigin, isolate->counters(), - isolate->metrics_recorder(), v8::metrics::Recorder::ContextId::Empty(), - DecodingMethod::kSync, GetWasmEngine()->allocator()); + auto enabled_features = WasmFeatures::FromIsolate(isolate); + ModuleResult module_res = + DecodeWasmModule(enabled_features, wire_bytes.module_bytes(), + kVerifyFunctions, ModuleOrigin::kWasmOrigin); CHECK_WITH_MSG(module_res.ok(), module_res.error().message().c_str()); WasmModule* module = module_res.value().get(); CHECK_NOT_NULL(module); @@ -538,6 +536,7 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes, "// found in the LICENSE file.\n" "\n" "// Flags: --wasm-staging --experimental-wasm-gc\n" + "// Flags: --experimental-wasm-relaxed-simd\n" "\n" "d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');\n" "\n" @@ -643,14 +642,20 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes, os << ", "; } os << "["; - for (uint32_t i = 0; i < elem_segment.entries.size(); i++) { + ModuleDecoderImpl decoder(WasmFeatures::All(), + wire_bytes.module_bytes().SubVectorFrom( + elem_segment.elements_wire_bytes_offset), + ModuleOrigin::kWasmOrigin); + for (uint32_t i = 0; i < elem_segment.element_count; i++) { + ConstantExpression expr = + decoder.consume_element_segment_entry(module, elem_segment); if (elem_segment.element_type == WasmElemSegment::kExpressionElements) { - DecodeAndAppendInitExpr(os, &zone, module, wire_bytes, - elem_segment.entries[i], elem_segment.type); + DecodeAndAppendInitExpr(os, &zone, module, wire_bytes, expr, + elem_segment.type); } else { - os << elem_segment.entries[i].index(); + os << expr.index(); } - if (i < elem_segment.entries.size() - 1) os << ", "; + if (i < elem_segment.element_count - 1) os << ", "; } os << "], " << (elem_segment.element_type == WasmElemSegment::kExpressionElements @@ -717,18 +722,29 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes, } } -void OneTimeEnableStagedWasmFeatures(v8::Isolate* isolate) { - struct EnableStagedWasmFeatures { - explicit EnableStagedWasmFeatures(v8::Isolate* isolate) { -#define ENABLE_STAGED_FEATURES(feat, desc, val) \ +void EnableExperimentalWasmFeatures(v8::Isolate* isolate) { + struct EnableExperimentalWasmFeatures { + explicit EnableExperimentalWasmFeatures(v8::Isolate* isolate) { + // Enable all staged features. +#define ENABLE_STAGED_FEATURES(feat, ...) \ v8_flags.experimental_wasm_##feat = true; FOREACH_WASM_STAGING_FEATURE_FLAG(ENABLE_STAGED_FEATURES) #undef ENABLE_STAGED_FEATURES + + // Enable non-staged experimental features that we also want to fuzz. + v8_flags.experimental_wasm_gc = true; + + // Enforce implications from enabling features. + FlagList::EnforceFlagImplications(); + + // Last, install any conditional features. Implications are handled + // implicitly. isolate->InstallConditionalFeatures(isolate->GetCurrentContext()); } }; // The compiler will properly synchronize the constructor call. - static EnableStagedWasmFeatures one_time_enable_staged_features(isolate); + static EnableExperimentalWasmFeatures one_time_enable_experimental_features( + isolate); } void WasmExecutionFuzzer::FuzzWasmModule(base::Vector<const uint8_t> data, @@ -741,7 +757,7 @@ void WasmExecutionFuzzer::FuzzWasmModule(base::Vector<const uint8_t> data, // respect that limit. if (data.size() > max_input_size()) return; - i::Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate); + Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate); // Clear any pending exceptions from a prior run. i_isolate->clear_pending_exception(); @@ -753,7 +769,7 @@ void WasmExecutionFuzzer::FuzzWasmModule(base::Vector<const uint8_t> data, // We explicitly enable staged WebAssembly features here to increase fuzzer // coverage. For libfuzzer fuzzers it is not possible that the fuzzer enables // the flag by itself. - OneTimeEnableStagedWasmFeatures(isolate); + EnableExperimentalWasmFeatures(isolate); v8::TryCatch try_catch(isolate); HandleScope scope(i_isolate); @@ -782,32 +798,26 @@ void WasmExecutionFuzzer::FuzzWasmModule(base::Vector<const uint8_t> data, } // Note: After dividing by 3 for 4 times, configuration_byte is within [0, 3]. -// Control whether Liftoff or the interpreter will be used as the reference -// tier. -// TODO(thibaudm): Port nondeterminism detection to arm. -#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_X86) || \ - defined(V8_TARGET_ARCH_ARM64) || defined(V8_TARGET_ARCH_ARM) - bool liftoff_as_reference = configuration_byte & 1; -#else - bool liftoff_as_reference = false; -#endif FlagScope<bool> turbo_mid_tier_regalloc( &v8_flags.turbo_force_mid_tier_regalloc, configuration_byte == 0); - if (!GenerateModule(i_isolate, &zone, data, &buffer, liftoff_as_reference)) { + if (!GenerateModule(i_isolate, &zone, data, &buffer)) { return; } testing::SetupIsolateForWasmModule(i_isolate); - ErrorThrower interpreter_thrower(i_isolate, "Interpreter"); ModuleWireBytes wire_bytes(buffer.begin(), buffer.end()); - if (require_valid && v8_flags.wasm_fuzzer_gen_test) { - GenerateTestCase(i_isolate, wire_bytes, true); + auto enabled_features = WasmFeatures::FromIsolate(i_isolate); + + bool valid = + GetWasmEngine()->SyncValidate(i_isolate, enabled_features, wire_bytes); + + if (v8_flags.wasm_fuzzer_gen_test) { + GenerateTestCase(i_isolate, wire_bytes, valid); } - auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate); MaybeHandle<WasmModuleObject> compiled_module; { // Explicitly enable Liftoff, disable tiering and set the tier_mask. This @@ -818,38 +828,22 @@ void WasmExecutionFuzzer::FuzzWasmModule(base::Vector<const uint8_t> data, tier_mask); FlagScope<int> debug_mask_scope(&v8_flags.wasm_debug_mask_for_testing, debug_mask); - compiled_module = GetWasmEngine()->SyncCompile( - i_isolate, enabled_features, &interpreter_thrower, wire_bytes); - } - bool compiles = !compiled_module.is_null(); - if (!require_valid && v8_flags.wasm_fuzzer_gen_test) { - GenerateTestCase(i_isolate, wire_bytes, compiles); + ErrorThrower thrower(i_isolate, "WasmFuzzerSyncCompile"); + compiled_module = GetWasmEngine()->SyncCompile(i_isolate, enabled_features, + &thrower, wire_bytes); + CHECK_EQ(valid, !compiled_module.is_null()); + CHECK_EQ(!valid, thrower.error()); + if (require_valid && !valid) { + FATAL("Generated module should validate, but got: %s", + thrower.error_msg()); + } + thrower.Reset(); } - std::string error_message; - bool result = GetWasmEngine()->SyncValidate(i_isolate, enabled_features, - wire_bytes, &error_message); - - CHECK_EQ(compiles, result); - CHECK_WITH_MSG( - !require_valid || result, - ("Generated module should validate, but got: " + error_message).c_str()); - - if (!compiles) return; - - int32_t max_steps = 16 * 1024; - int32_t nondeterminism = false; - Handle<WasmModuleObject> module_ref; - if (liftoff_as_reference) { - module_ref = CompileReferenceModule(&zone, i_isolate, wire_bytes, - &interpreter_thrower, &max_steps, - &nondeterminism); + if (valid) { + ExecuteAgainstReference(i_isolate, compiled_module.ToHandleChecked(), + kDefaultMaxFuzzerExecutedInstructions); } - InterpretAndExecuteModule(i_isolate, compiled_module.ToHandleChecked(), - module_ref, &max_steps, &nondeterminism); } -} // namespace fuzzer -} // namespace wasm -} // namespace internal -} // namespace v8 +} // namespace v8::internal::wasm::fuzzer |