2017-05-08 09:22:54 +00:00
|
|
|
// 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/fuzzer/wasm-fuzzer-common.h"
|
|
|
|
|
2019-01-10 14:48:12 +00:00
|
|
|
#include <ctime>
|
|
|
|
|
2021-08-23 13:01:06 +00:00
|
|
|
#include "include/v8-context.h"
|
|
|
|
#include "include/v8-exception.h"
|
|
|
|
#include "include/v8-isolate.h"
|
|
|
|
#include "include/v8-local-handle.h"
|
|
|
|
#include "include/v8-metrics.h"
|
2019-05-22 07:55:37 +00:00
|
|
|
#include "src/execution/isolate.h"
|
2019-05-23 08:51:46 +00:00
|
|
|
#include "src/objects/objects-inl.h"
|
2019-09-03 07:56:24 +00:00
|
|
|
#include "src/utils/ostreams.h"
|
2021-07-08 10:24:30 +00:00
|
|
|
#include "src/wasm/baseline/liftoff-compiler.h"
|
2021-08-06 10:45:15 +00:00
|
|
|
#include "src/wasm/function-body-decoder-impl.h"
|
2021-07-08 10:24:30 +00:00
|
|
|
#include "src/wasm/module-instantiate.h"
|
2018-01-17 14:46:27 +00:00
|
|
|
#include "src/wasm/wasm-engine.h"
|
2019-09-03 07:56:24 +00:00
|
|
|
#include "src/wasm/wasm-feature-flags.h"
|
2017-05-08 09:22:54 +00:00
|
|
|
#include "src/wasm/wasm-module-builder.h"
|
|
|
|
#include "src/wasm/wasm-module.h"
|
2018-07-25 14:11:56 +00:00
|
|
|
#include "src/wasm/wasm-objects-inl.h"
|
2021-11-05 06:10:04 +00:00
|
|
|
#include "src/wasm/wasm-opcodes-inl.h"
|
2017-05-08 09:22:54 +00:00
|
|
|
#include "src/zone/accounting-allocator.h"
|
|
|
|
#include "src/zone/zone.h"
|
2017-11-06 12:21:06 +00:00
|
|
|
#include "test/common/wasm/flag-utils.h"
|
2017-05-08 09:22:54 +00:00
|
|
|
#include "test/common/wasm/wasm-module-runner.h"
|
|
|
|
#include "test/fuzzer/fuzzer-support.h"
|
|
|
|
|
2017-09-01 13:20:46 +00:00
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace wasm {
|
|
|
|
namespace fuzzer {
|
2017-08-09 08:11:21 +00:00
|
|
|
|
2021-07-08 10:24:30 +00:00
|
|
|
// 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) {
|
|
|
|
// 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());
|
|
|
|
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()));
|
|
|
|
|
|
|
|
// Compile all functions with Liftoff.
|
|
|
|
WasmCodeRefScope code_ref_scope;
|
|
|
|
auto env = native_module->CreateCompilationEnv();
|
|
|
|
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);
|
|
|
|
FunctionBody func_body(func.sig, func.code.offset(), func_code.begin(),
|
|
|
|
func_code.end());
|
|
|
|
auto result = ExecuteLiftoffCompilation(
|
2021-08-04 16:09:26 +00:00
|
|
|
&env, func_body, func.func_index, kForDebugging,
|
|
|
|
LiftoffOptions{}.set_max_steps(max_steps).set_nondeterminism(
|
|
|
|
nondeterminism));
|
2021-07-08 10:24:30 +00:00
|
|
|
native_module->PublishCode(
|
|
|
|
native_module->AddCompiledCode(std::move(result)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the module object.
|
|
|
|
constexpr base::Vector<const char> kNoSourceUrl;
|
|
|
|
Handle<Script> script =
|
|
|
|
GetWasmEngine()->GetOrCreateScript(isolate, native_module, kNoSourceUrl);
|
|
|
|
Handle<FixedArray> export_wrappers = isolate->factory()->NewFixedArray(
|
2021-08-27 12:30:43 +00:00
|
|
|
static_cast<int>(module->functions.size()));
|
2021-07-08 10:24:30 +00:00
|
|
|
return WasmModuleObject::New(isolate, std::move(native_module), script,
|
|
|
|
export_wrappers);
|
|
|
|
}
|
|
|
|
|
2017-09-07 09:48:34 +00:00
|
|
|
void InterpretAndExecuteModule(i::Isolate* isolate,
|
2021-07-08 10:24:30 +00:00
|
|
|
Handle<WasmModuleObject> module_object,
|
|
|
|
Handle<WasmModuleObject> module_ref,
|
|
|
|
int32_t* max_steps, int32_t* nondeterminism) {
|
2018-07-05 08:43:57 +00:00
|
|
|
// 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;
|
|
|
|
|
2020-08-11 10:05:04 +00:00
|
|
|
HandleScope handle_scope(isolate); // Avoid leaking handles.
|
2017-09-07 09:48:34 +00:00
|
|
|
Handle<WasmInstanceObject> instance;
|
2018-01-30 10:18:24 +00:00
|
|
|
|
2020-08-11 10:05:04 +00:00
|
|
|
// Try to instantiate, return if it fails.
|
|
|
|
{
|
|
|
|
ErrorThrower thrower(isolate, "WebAssembly Instantiation");
|
2021-06-18 14:29:39 +00:00
|
|
|
if (!GetWasmEngine()
|
2020-08-11 10:19:36 +00:00
|
|
|
->SyncInstantiate(isolate, &thrower, module_object, {},
|
|
|
|
{}) // no imports & memory
|
|
|
|
.ToHandle(&instance)) {
|
2020-08-11 10:05:04 +00:00
|
|
|
isolate->clear_pending_exception();
|
|
|
|
thrower.Reset(); // Ignore errors.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the "main" exported function. Do nothing if it does not exist.
|
|
|
|
Handle<WasmExportedFunction> main_function;
|
|
|
|
if (!testing::GetExportedFunction(isolate, instance, "main")
|
|
|
|
.ToHandle(&main_function)) {
|
2018-01-30 10:18:24 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-08-11 10:05:04 +00:00
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
base::OwnedVector<Handle<Object>> compiled_args =
|
2020-08-26 12:53:24 +00:00
|
|
|
testing::MakeDefaultArguments(isolate, main_function->sig());
|
2021-07-08 10:24:30 +00:00
|
|
|
bool exception_ref = false;
|
2020-08-18 08:14:25 +00:00
|
|
|
bool exception = false;
|
2021-07-08 10:24:30 +00:00
|
|
|
int32_t result_ref = 0;
|
|
|
|
int32_t result = 0;
|
|
|
|
|
|
|
|
if (module_ref.is_null()) {
|
|
|
|
base::OwnedVector<WasmValue> arguments =
|
|
|
|
testing::MakeDefaultInterpreterArguments(isolate, main_function->sig());
|
|
|
|
|
|
|
|
// Now interpret.
|
|
|
|
testing::WasmInterpretationResult interpreter_result =
|
|
|
|
testing::InterpretWasmModule(isolate, instance,
|
|
|
|
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;
|
|
|
|
}
|
2021-07-09 10:45:30 +00:00
|
|
|
// Reset the instance before the test run.
|
|
|
|
{
|
|
|
|
ErrorThrower thrower(isolate, "Second Instantiation");
|
|
|
|
// We instantiated before, so the second instantiation must also succeed:
|
|
|
|
CHECK(GetWasmEngine()
|
|
|
|
->SyncInstantiate(isolate, &thrower, module_object, {},
|
|
|
|
{}) // no imports & memory
|
|
|
|
.ToHandle(&instance));
|
|
|
|
}
|
2021-07-08 10:24:30 +00:00
|
|
|
} else {
|
|
|
|
Handle<WasmInstanceObject> instance_ref;
|
|
|
|
{
|
|
|
|
ErrorThrower thrower(isolate, "WebAssembly Instantiation");
|
2021-07-09 15:53:05 +00:00
|
|
|
// We instantiated before, so the second instantiation must also succeed:
|
|
|
|
CHECK(GetWasmEngine()
|
|
|
|
->SyncInstantiate(isolate, &thrower, module_ref, {},
|
|
|
|
{}) // no imports & memory
|
|
|
|
.ToHandle(&instance_ref));
|
2021-07-08 10:24:30 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = testing::CallWasmFunctionForTesting(
|
2020-08-26 12:53:24 +00:00
|
|
|
isolate, instance, "main", static_cast<int>(compiled_args.size()),
|
|
|
|
compiled_args.begin(), &exception);
|
2021-07-08 10:24:30 +00:00
|
|
|
|
|
|
|
if (exception_ref != exception) {
|
2020-08-10 14:20:01 +00:00
|
|
|
const char* exception_text[] = {"no exception", "exception"};
|
2021-09-22 19:55:42 +00:00
|
|
|
FATAL("expected: %s; got: %s", exception_text[exception_ref],
|
2020-08-18 08:14:25 +00:00
|
|
|
exception_text[exception]);
|
2020-08-10 14:20:01 +00:00
|
|
|
}
|
|
|
|
|
2021-07-08 10:24:30 +00:00
|
|
|
if (!exception) {
|
|
|
|
CHECK_EQ(result_ref, result);
|
2018-01-31 12:11:14 +00:00
|
|
|
}
|
2017-09-07 09:48:34 +00:00
|
|
|
}
|
|
|
|
|
2018-01-08 11:02:45 +00:00
|
|
|
namespace {
|
2022-02-11 16:15:23 +00:00
|
|
|
|
2018-01-08 11:02:45 +00:00
|
|
|
struct PrintSig {
|
|
|
|
const size_t num;
|
|
|
|
const std::function<ValueType(size_t)> getter;
|
|
|
|
};
|
2022-02-11 16:15:23 +00:00
|
|
|
|
2018-01-08 11:02:45 +00:00
|
|
|
PrintSig PrintParameters(const FunctionSig* sig) {
|
|
|
|
return {sig->parameter_count(), [=](size_t i) { return sig->GetParam(i); }};
|
|
|
|
}
|
2022-02-11 16:15:23 +00:00
|
|
|
|
2018-01-08 11:02:45 +00:00
|
|
|
PrintSig PrintReturns(const FunctionSig* sig) {
|
|
|
|
return {sig->return_count(), [=](size_t i) { return sig->GetReturn(i); }};
|
|
|
|
}
|
2022-02-11 16:15:23 +00:00
|
|
|
|
|
|
|
std::string HeapTypeToConstantName(HeapType heap_type) {
|
|
|
|
switch (heap_type.representation()) {
|
|
|
|
case HeapType::kFunc:
|
|
|
|
return "kWasmFuncRef";
|
|
|
|
case HeapType::kExtern:
|
|
|
|
return "kWasmExternRef";
|
|
|
|
case HeapType::kEq:
|
|
|
|
return "kWasmEqRef";
|
|
|
|
case HeapType::kI31:
|
|
|
|
return "kWasmI31Ref";
|
|
|
|
case HeapType::kData:
|
|
|
|
return "kWasmDataRef";
|
|
|
|
case HeapType::kArray:
|
|
|
|
return "kWasmArrayRef";
|
|
|
|
case HeapType::kAny:
|
|
|
|
return "kWasmAnyRef";
|
|
|
|
case HeapType::kBottom:
|
|
|
|
UNREACHABLE();
|
|
|
|
default:
|
|
|
|
return std::to_string(heap_type.ref_index());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-06 10:45:15 +00:00
|
|
|
std::string ValueTypeToConstantName(ValueType type) {
|
2020-03-12 14:29:51 +00:00
|
|
|
switch (type.kind()) {
|
2021-10-07 15:10:58 +00:00
|
|
|
case kI8:
|
|
|
|
return "kWasmI8";
|
|
|
|
case kI16:
|
|
|
|
return "kWasmI16";
|
2021-02-22 09:28:44 +00:00
|
|
|
case kI32:
|
2018-01-08 11:02:45 +00:00
|
|
|
return "kWasmI32";
|
2021-02-22 09:28:44 +00:00
|
|
|
case kI64:
|
2018-01-08 11:02:45 +00:00
|
|
|
return "kWasmI64";
|
2021-02-22 09:28:44 +00:00
|
|
|
case kF32:
|
2018-01-08 11:02:45 +00:00
|
|
|
return "kWasmF32";
|
2021-02-22 09:28:44 +00:00
|
|
|
case kF64:
|
2018-01-08 11:02:45 +00:00
|
|
|
return "kWasmF64";
|
2021-02-22 09:28:44 +00:00
|
|
|
case kS128:
|
2020-09-21 20:31:37 +00:00
|
|
|
return "kWasmS128";
|
2021-02-22 09:28:44 +00:00
|
|
|
case kOptRef:
|
2020-07-01 06:35:04 +00:00
|
|
|
switch (type.heap_representation()) {
|
2020-06-29 09:24:51 +00:00
|
|
|
case HeapType::kExtern:
|
[wasm-gc] Change ValueType representation to account for new types
Motivation:
Changes to the typed function references and gc proposals solidified
the notion of heap type, clarified nullable vs. non-nullable reference
types, and introduced rtts, which contain an integer depth field in
addition to a heap type. This required us to overhaul our ValueType
representation, which results in extensive changes.
To keep this CL "small", we do not try to implement the binary encoding
as described in the proposals, but rather devise a simpler one of our
own (see below). Also, we do not try to implement additional
functionality for the new types.
Changes:
- Introduce HeapType. Move heap types from ValueType to HeapType.
- Introduce Nullability for reference types.
- Rework ValueType helper methods.
- Introduce rtts in ValueType with an integer depth field. Include depth
in the ValueType encoding.
- Make the constructor of ValueType private, instead expose static
functions which explicitly state what they create.
- Change every switch statement on ValueType::Kind. Sometimes, we need
nested switches.
- Introduce temporary constants in ValueTypeCode for nullable types,
use them for decoding.
- In WasmGlobalObject, split 'flags' into 'raw_type' and 'is_mutable'.
- Change IsSubtypeOfRef to IsSubtypeOfHeap and implement changes in
subtyping.
- kWasmFuncRef initializers are now non-nullable. Initializers are
only required to be subtypes of the declared global type.
- Change tests and fuzzers as needed.
Bug: v8:7748
Change-Id: If41f783bd4128443b07e94188cea7dd53ab0bfa5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2247657
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68408}
2020-06-18 11:24:07 +00:00
|
|
|
return "kWasmExternRef";
|
2020-06-29 09:24:51 +00:00
|
|
|
case HeapType::kFunc:
|
[wasm-gc] Change ValueType representation to account for new types
Motivation:
Changes to the typed function references and gc proposals solidified
the notion of heap type, clarified nullable vs. non-nullable reference
types, and introduced rtts, which contain an integer depth field in
addition to a heap type. This required us to overhaul our ValueType
representation, which results in extensive changes.
To keep this CL "small", we do not try to implement the binary encoding
as described in the proposals, but rather devise a simpler one of our
own (see below). Also, we do not try to implement additional
functionality for the new types.
Changes:
- Introduce HeapType. Move heap types from ValueType to HeapType.
- Introduce Nullability for reference types.
- Rework ValueType helper methods.
- Introduce rtts in ValueType with an integer depth field. Include depth
in the ValueType encoding.
- Make the constructor of ValueType private, instead expose static
functions which explicitly state what they create.
- Change every switch statement on ValueType::Kind. Sometimes, we need
nested switches.
- Introduce temporary constants in ValueTypeCode for nullable types,
use them for decoding.
- In WasmGlobalObject, split 'flags' into 'raw_type' and 'is_mutable'.
- Change IsSubtypeOfRef to IsSubtypeOfHeap and implement changes in
subtyping.
- kWasmFuncRef initializers are now non-nullable. Initializers are
only required to be subtypes of the declared global type.
- Change tests and fuzzers as needed.
Bug: v8:7748
Change-Id: If41f783bd4128443b07e94188cea7dd53ab0bfa5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2247657
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68408}
2020-06-18 11:24:07 +00:00
|
|
|
return "kWasmFuncRef";
|
2021-08-06 10:45:15 +00:00
|
|
|
case HeapType::kEq:
|
|
|
|
return "kWasmEqRef";
|
2020-11-19 14:51:14 +00:00
|
|
|
case HeapType::kAny:
|
2021-08-06 10:45:15 +00:00
|
|
|
return "kWasmAnyRef";
|
2020-11-19 14:51:14 +00:00
|
|
|
case HeapType::kBottom:
|
2022-02-11 16:15:23 +00:00
|
|
|
UNREACHABLE();
|
2021-11-02 14:43:10 +00:00
|
|
|
case HeapType::kData:
|
2022-01-11 12:55:55 +00:00
|
|
|
case HeapType::kArray:
|
2021-11-02 14:43:10 +00:00
|
|
|
case HeapType::kI31:
|
|
|
|
default:
|
2022-02-11 16:15:23 +00:00
|
|
|
return "wasmOptRefType(" + HeapTypeToConstantName(type.heap_type()) +
|
|
|
|
")";
|
2021-11-02 14:43:10 +00:00
|
|
|
}
|
2022-02-11 16:15:23 +00:00
|
|
|
case kRef:
|
|
|
|
return "wasmRefType(" + HeapTypeToConstantName(type.heap_type()) + ")";
|
|
|
|
case kRtt:
|
|
|
|
case kVoid:
|
|
|
|
case kBottom:
|
2021-08-06 10:45:15 +00:00
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-08 11:02:45 +00:00
|
|
|
std::ostream& operator<<(std::ostream& os, const PrintSig& print) {
|
|
|
|
os << "[";
|
|
|
|
for (size_t i = 0; i < print.num; ++i) {
|
|
|
|
os << (i == 0 ? "" : ", ") << ValueTypeToConstantName(print.getter(i));
|
|
|
|
}
|
|
|
|
return os << "]";
|
|
|
|
}
|
|
|
|
|
2018-02-01 15:18:05 +00:00
|
|
|
struct PrintName {
|
|
|
|
WasmName name;
|
|
|
|
PrintName(ModuleWireBytes wire_bytes, WireBytesRef ref)
|
|
|
|
: name(wire_bytes.GetNameOrNull(ref)) {}
|
|
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const PrintName& name) {
|
2022-02-14 18:04:55 +00:00
|
|
|
return os.put('\'').write(name.name.begin(), name.name.size()).put('\'');
|
2018-02-01 15:18:05 +00:00
|
|
|
}
|
2021-05-19 11:01:15 +00:00
|
|
|
|
2021-11-05 06:10:04 +00:00
|
|
|
// An interface for WasmFullDecoder used to decode initializer expressions. As
|
|
|
|
// opposed to the one in src/wasm/, this emits {WasmInitExpr} as opposed to a
|
|
|
|
// {WasmValue}.
|
|
|
|
class InitExprInterface {
|
|
|
|
public:
|
|
|
|
static constexpr Decoder::ValidateFlag validate = Decoder::kFullValidation;
|
|
|
|
static constexpr DecodingMode decoding_mode = kInitExpression;
|
|
|
|
|
|
|
|
struct Value : public ValueBase<validate> {
|
|
|
|
WasmInitExpr init_expr;
|
|
|
|
|
|
|
|
template <typename... Args>
|
|
|
|
explicit Value(Args&&... args) V8_NOEXCEPT
|
|
|
|
: ValueBase(std::forward<Args>(args)...) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
using Control = ControlBase<Value, validate>;
|
|
|
|
using FullDecoder =
|
|
|
|
WasmFullDecoder<validate, InitExprInterface, decoding_mode>;
|
|
|
|
|
|
|
|
explicit InitExprInterface(Zone* zone) : zone_(zone) {}
|
|
|
|
|
|
|
|
#define EMPTY_INTERFACE_FUNCTION(name, ...) \
|
|
|
|
V8_INLINE void name(FullDecoder* decoder, ##__VA_ARGS__) {}
|
|
|
|
INTERFACE_META_FUNCTIONS(EMPTY_INTERFACE_FUNCTION)
|
|
|
|
#undef EMPTY_INTERFACE_FUNCTION
|
|
|
|
#define UNREACHABLE_INTERFACE_FUNCTION(name, ...) \
|
|
|
|
V8_INLINE void name(FullDecoder* decoder, ##__VA_ARGS__) { UNREACHABLE(); }
|
|
|
|
INTERFACE_NON_CONSTANT_FUNCTIONS(UNREACHABLE_INTERFACE_FUNCTION)
|
|
|
|
#undef UNREACHABLE_INTERFACE_FUNCTION
|
|
|
|
|
|
|
|
void I32Const(FullDecoder* decoder, Value* result, int32_t value) {
|
|
|
|
result->init_expr = WasmInitExpr(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void I64Const(FullDecoder* decoder, Value* result, int64_t value) {
|
|
|
|
result->init_expr = WasmInitExpr(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void F32Const(FullDecoder* decoder, Value* result, float value) {
|
|
|
|
result->init_expr = WasmInitExpr(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void F64Const(FullDecoder* decoder, Value* result, double value) {
|
|
|
|
result->init_expr = WasmInitExpr(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void S128Const(FullDecoder* decoder, Simd128Immediate<validate>& imm,
|
|
|
|
Value* result) {
|
|
|
|
result->init_expr = WasmInitExpr(imm.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RefNull(FullDecoder* decoder, ValueType type, Value* result) {
|
|
|
|
result->init_expr = WasmInitExpr::RefNullConst(type.heap_representation());
|
|
|
|
}
|
|
|
|
|
|
|
|
void RefFunc(FullDecoder* decoder, uint32_t function_index, Value* result) {
|
|
|
|
result->init_expr = WasmInitExpr::RefFuncConst(function_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GlobalGet(FullDecoder* decoder, Value* result,
|
|
|
|
const GlobalIndexImmediate<validate>& imm) {
|
|
|
|
result->init_expr = WasmInitExpr::GlobalGet(imm.index);
|
|
|
|
}
|
|
|
|
|
|
|
|
void StructNewWithRtt(FullDecoder* decoder,
|
|
|
|
const StructIndexImmediate<validate>& imm,
|
|
|
|
const Value& rtt, const Value args[], Value* result) {
|
|
|
|
ZoneVector<WasmInitExpr>* elements =
|
|
|
|
zone_->New<ZoneVector<WasmInitExpr>>(zone_);
|
|
|
|
for (size_t i = 0; i < imm.struct_type->field_count(); i++) {
|
|
|
|
elements->push_back(args[i].init_expr);
|
|
|
|
}
|
|
|
|
bool nominal = decoder->module_->has_supertype(imm.index);
|
|
|
|
|
|
|
|
if (!nominal) elements->push_back(rtt.init_expr);
|
|
|
|
|
|
|
|
result->init_expr =
|
|
|
|
nominal ? WasmInitExpr::StructNew(imm.index, elements)
|
|
|
|
: WasmInitExpr::StructNewWithRtt(imm.index, elements);
|
|
|
|
}
|
|
|
|
|
|
|
|
void StructNewDefault(FullDecoder* decoder,
|
|
|
|
const StructIndexImmediate<validate>& imm,
|
|
|
|
const Value& rtt, Value* result) {
|
|
|
|
bool nominal = decoder->module_->has_supertype(imm.index);
|
|
|
|
result->init_expr = nominal ? WasmInitExpr::StructNewDefault(imm.index)
|
|
|
|
: WasmInitExpr::StructNewDefaultWithRtt(
|
|
|
|
zone_, imm.index, rtt.init_expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ArrayInit(FullDecoder* decoder, const ArrayIndexImmediate<validate>& imm,
|
|
|
|
const base::Vector<Value>& elements, const Value& rtt,
|
|
|
|
Value* result) {
|
|
|
|
ZoneVector<WasmInitExpr>* args =
|
|
|
|
zone_->New<ZoneVector<WasmInitExpr>>(zone_);
|
|
|
|
for (Value expr : elements) args->push_back(expr.init_expr);
|
|
|
|
bool nominal = decoder->module_->has_supertype(imm.index);
|
|
|
|
|
|
|
|
if (!nominal) args->push_back(rtt.init_expr);
|
|
|
|
result->init_expr = nominal ? WasmInitExpr::ArrayInitStatic(imm.index, args)
|
|
|
|
: WasmInitExpr::ArrayInit(imm.index, args);
|
|
|
|
}
|
|
|
|
|
2022-01-26 08:27:39 +00:00
|
|
|
void ArrayInitFromData(FullDecoder* decoder,
|
|
|
|
const ArrayIndexImmediate<validate>& array_imm,
|
|
|
|
const IndexImmediate<validate>& data_segment_imm,
|
|
|
|
const Value& offset_value, const Value& length_value,
|
|
|
|
const Value& rtt, Value* result) {
|
|
|
|
// TODO(7748): Implement.
|
|
|
|
UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
2021-11-05 06:10:04 +00:00
|
|
|
void RttCanon(FullDecoder* decoder, uint32_t type_index, Value* result) {
|
|
|
|
result->init_expr = WasmInitExpr::RttCanon(type_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DoReturn(FullDecoder* decoder, uint32_t /*drop_values*/) {
|
|
|
|
// End decoding on "end".
|
|
|
|
decoder->set_end(decoder->pc() + 1);
|
|
|
|
result_ = decoder->stack_value(1)->init_expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
WasmInitExpr result() { return result_; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
WasmInitExpr result_;
|
|
|
|
Zone* zone_;
|
|
|
|
};
|
|
|
|
|
2021-06-30 20:35:23 +00:00
|
|
|
// Appends an initializer expression encoded in {wire_bytes}, in the offset
|
|
|
|
// contained in {expr}.
|
2021-11-05 06:10:04 +00:00
|
|
|
void AppendInitExpr(std::ostream& os, const WasmInitExpr& expr) {
|
2021-06-30 20:35:23 +00:00
|
|
|
os << "WasmInitExpr.";
|
2021-11-05 06:10:04 +00:00
|
|
|
bool append_operands = false;
|
|
|
|
switch (expr.kind()) {
|
|
|
|
case WasmInitExpr::kNone:
|
|
|
|
UNREACHABLE();
|
|
|
|
case WasmInitExpr::kGlobalGet:
|
|
|
|
os << "GlobalGet(" << expr.immediate().index;
|
2021-06-30 20:35:23 +00:00
|
|
|
break;
|
2021-11-05 06:10:04 +00:00
|
|
|
case WasmInitExpr::kI32Const:
|
|
|
|
os << "I32Const(" << expr.immediate().i32_const;
|
2021-06-30 20:35:23 +00:00
|
|
|
break;
|
2021-11-05 06:10:04 +00:00
|
|
|
case WasmInitExpr::kI64Const:
|
|
|
|
os << "I64Const(" << expr.immediate().i64_const;
|
2021-06-30 20:35:23 +00:00
|
|
|
break;
|
2021-11-05 06:10:04 +00:00
|
|
|
case WasmInitExpr::kF32Const:
|
|
|
|
os << "F32Const(" << expr.immediate().f32_const;
|
2021-06-30 20:35:23 +00:00
|
|
|
break;
|
2021-11-05 06:10:04 +00:00
|
|
|
case WasmInitExpr::kF64Const:
|
|
|
|
os << "F64Const(" << expr.immediate().f64_const;
|
2021-06-30 20:35:23 +00:00
|
|
|
break;
|
2021-11-05 06:10:04 +00:00
|
|
|
case WasmInitExpr::kS128Const:
|
2021-08-06 10:45:15 +00:00
|
|
|
os << "S128Const([";
|
|
|
|
for (int i = 0; i < kSimd128Size; i++) {
|
2021-11-05 06:10:04 +00:00
|
|
|
os << static_cast<int>(expr.immediate().s128_const[i]);
|
|
|
|
if (i < kSimd128Size - 1) os << ", ";
|
2021-08-06 10:45:15 +00:00
|
|
|
}
|
|
|
|
os << "]";
|
|
|
|
break;
|
2021-11-05 06:10:04 +00:00
|
|
|
case WasmInitExpr::kRefNullConst:
|
|
|
|
os << "RefNull("
|
|
|
|
<< HeapTypeToConstantName(HeapType(expr.immediate().heap_type));
|
|
|
|
break;
|
|
|
|
case WasmInitExpr::kRefFuncConst:
|
|
|
|
os << "RefFunc(" << expr.immediate().index;
|
|
|
|
break;
|
|
|
|
case WasmInitExpr::kStructNewWithRtt:
|
|
|
|
os << "StructNewWithRtt(" << expr.immediate().index;
|
|
|
|
append_operands = true;
|
|
|
|
break;
|
|
|
|
case WasmInitExpr::kStructNew:
|
|
|
|
os << "StructNew(" << expr.immediate().index;
|
|
|
|
append_operands = true;
|
|
|
|
break;
|
|
|
|
case WasmInitExpr::kStructNewDefaultWithRtt:
|
|
|
|
os << "StructNewDefaultWithRtt(" << expr.immediate().index << ", ";
|
|
|
|
AppendInitExpr(os, (*expr.operands())[0]);
|
|
|
|
break;
|
|
|
|
case WasmInitExpr::kStructNewDefault:
|
|
|
|
os << "StructNewDefault(" << expr.immediate().index;
|
2021-06-30 20:35:23 +00:00
|
|
|
break;
|
2021-11-05 06:10:04 +00:00
|
|
|
case WasmInitExpr::kArrayInit:
|
|
|
|
os << "ArrayInit(" << expr.immediate().index;
|
|
|
|
append_operands = true;
|
2021-08-06 10:45:15 +00:00
|
|
|
break;
|
2021-11-05 06:10:04 +00:00
|
|
|
case WasmInitExpr::kArrayInitStatic:
|
|
|
|
os << "ArrayInitStatic(" << expr.immediate().index;
|
|
|
|
append_operands = true;
|
|
|
|
break;
|
|
|
|
case WasmInitExpr::kRttCanon:
|
|
|
|
os << "RttCanon(" << expr.immediate().index;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (append_operands) {
|
|
|
|
os << ", [";
|
|
|
|
for (size_t i = 0; i < expr.operands()->size(); i++) {
|
|
|
|
AppendInitExpr(os, (*expr.operands())[i]);
|
|
|
|
if (i < expr.operands()->size() - 1) os << ", ";
|
2021-08-06 10:45:15 +00:00
|
|
|
}
|
2021-11-05 06:10:04 +00:00
|
|
|
os << "]";
|
2021-06-30 20:35:23 +00:00
|
|
|
}
|
2021-11-05 06:10:04 +00:00
|
|
|
|
2021-06-30 20:35:23 +00:00
|
|
|
os << ")";
|
|
|
|
}
|
2021-11-05 06:10:04 +00:00
|
|
|
|
|
|
|
void DecodeAndAppendInitExpr(StdoutStream& os, Zone* zone,
|
|
|
|
const WasmModule* module,
|
2022-01-11 17:53:56 +00:00
|
|
|
ModuleWireBytes module_bytes,
|
|
|
|
ConstantExpression init, ValueType expected) {
|
|
|
|
switch (init.kind()) {
|
|
|
|
case ConstantExpression::kEmpty:
|
|
|
|
UNREACHABLE();
|
|
|
|
case ConstantExpression::kI32Const:
|
|
|
|
AppendInitExpr(os, WasmInitExpr(init.i32_value()));
|
|
|
|
break;
|
|
|
|
case ConstantExpression::kRefNull:
|
|
|
|
AppendInitExpr(os, WasmInitExpr::RefNullConst(init.repr()));
|
|
|
|
break;
|
|
|
|
case ConstantExpression::kRefFunc:
|
|
|
|
AppendInitExpr(os, WasmInitExpr::RefFuncConst(init.index()));
|
|
|
|
break;
|
|
|
|
case ConstantExpression::kWireBytesRef: {
|
|
|
|
WireBytesRef ref = init.wire_bytes_ref();
|
|
|
|
auto sig = FixedSizeSignature<ValueType>::Returns(expected);
|
|
|
|
FunctionBody body(&sig, ref.offset(), module_bytes.start() + ref.offset(),
|
|
|
|
module_bytes.start() + ref.end_offset());
|
|
|
|
WasmFeatures detected;
|
|
|
|
WasmFullDecoder<Decoder::kFullValidation, InitExprInterface,
|
|
|
|
kInitExpression>
|
|
|
|
decoder(zone, module, WasmFeatures::All(), &detected, body, zone);
|
|
|
|
|
|
|
|
decoder.DecodeFunctionBody();
|
|
|
|
|
|
|
|
AppendInitExpr(os, decoder.interface().result());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-11-05 06:10:04 +00:00
|
|
|
}
|
2018-06-21 12:09:36 +00:00
|
|
|
} // namespace
|
2018-02-01 15:18:05 +00:00
|
|
|
|
2018-01-08 11:02:45 +00:00
|
|
|
void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
|
|
|
|
bool compiles) {
|
|
|
|
constexpr bool kVerifyFunctions = false;
|
2019-11-26 16:25:14 +00:00
|
|
|
auto enabled_features = i::wasm::WasmFeatures::FromIsolate(isolate);
|
2018-07-20 12:55:40 +00:00
|
|
|
ModuleResult module_res = DecodeWasmModule(
|
2018-08-08 14:54:44 +00:00
|
|
|
enabled_features, wire_bytes.start(), wire_bytes.end(), kVerifyFunctions,
|
2019-04-03 13:54:08 +00:00
|
|
|
ModuleOrigin::kWasmOrigin, isolate->counters(),
|
2020-08-13 15:04:08 +00:00
|
|
|
isolate->metrics_recorder(), v8::metrics::Recorder::ContextId::Empty(),
|
2021-06-18 14:29:39 +00:00
|
|
|
DecodingMethod::kSync, GetWasmEngine()->allocator());
|
2021-08-27 12:53:28 +00:00
|
|
|
CHECK_WITH_MSG(module_res.ok(), module_res.error().message().c_str());
|
2018-10-19 12:35:56 +00:00
|
|
|
WasmModule* module = module_res.value().get();
|
2018-01-08 11:02:45 +00:00
|
|
|
CHECK_NOT_NULL(module);
|
|
|
|
|
2021-11-05 06:10:04 +00:00
|
|
|
AccountingAllocator allocator;
|
|
|
|
Zone zone(&allocator, "init. expression zone");
|
|
|
|
|
2018-06-14 12:46:07 +00:00
|
|
|
StdoutStream os;
|
2018-01-08 11:02:45 +00:00
|
|
|
|
2019-01-10 14:48:12 +00:00
|
|
|
tzset();
|
|
|
|
time_t current_time = time(nullptr);
|
|
|
|
struct tm current_localtime;
|
|
|
|
#ifdef V8_OS_WIN
|
|
|
|
localtime_s(¤t_localtime, ¤t_time);
|
|
|
|
#else
|
|
|
|
localtime_r(¤t_time, ¤t_localtime);
|
|
|
|
#endif
|
|
|
|
int year = 1900 + current_localtime.tm_year;
|
|
|
|
|
|
|
|
os << "// Copyright " << year
|
|
|
|
<< " the V8 project authors. All rights reserved.\n"
|
2018-01-08 11:02:45 +00:00
|
|
|
"// Use of this source code is governed by a BSD-style license that "
|
|
|
|
"can be\n"
|
|
|
|
"// found in the LICENSE file.\n"
|
|
|
|
"\n"
|
2021-08-06 10:45:15 +00:00
|
|
|
"// Flags: --wasm-staging --experimental-wasm-gc\n"
|
2019-08-23 15:32:23 +00:00
|
|
|
"\n"
|
2021-08-24 22:51:54 +00:00
|
|
|
"d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');\n"
|
2018-01-08 11:02:45 +00:00
|
|
|
"\n"
|
2020-02-04 10:31:29 +00:00
|
|
|
"const builder = new WasmModuleBuilder();\n";
|
2018-01-08 11:02:45 +00:00
|
|
|
|
2021-08-06 10:45:15 +00:00
|
|
|
for (int i = 0; i < static_cast<int>(module->types.size()); i++) {
|
|
|
|
if (module->has_struct(i)) {
|
|
|
|
const StructType* struct_type = module->types[i].struct_type;
|
|
|
|
os << "builder.addStruct([";
|
|
|
|
int field_count = struct_type->field_count();
|
|
|
|
for (int index = 0; index < field_count; index++) {
|
|
|
|
os << "makeField(" << ValueTypeToConstantName(struct_type->field(index))
|
|
|
|
<< ", " << (struct_type->mutability(index) ? "true" : "false")
|
|
|
|
<< ")";
|
2021-11-05 06:10:04 +00:00
|
|
|
if (index + 1 < field_count) os << ", ";
|
2021-08-06 10:45:15 +00:00
|
|
|
}
|
2021-11-05 06:10:04 +00:00
|
|
|
os << "]);\n";
|
2021-08-06 10:45:15 +00:00
|
|
|
} else if (module->has_array(i)) {
|
|
|
|
const ArrayType* array_type = module->types[i].array_type;
|
|
|
|
os << "builder.addArray("
|
2021-11-05 06:10:04 +00:00
|
|
|
<< ValueTypeToConstantName(array_type->element_type()) << ", "
|
2021-08-06 10:45:15 +00:00
|
|
|
<< (array_type->mutability() ? "true" : "false") << ");\n";
|
|
|
|
} else {
|
|
|
|
DCHECK(module->has_signature(i));
|
|
|
|
const FunctionSig* sig = module->types[i].function_sig;
|
|
|
|
os << "builder.addType(makeSig(" << PrintParameters(sig) << ", "
|
|
|
|
<< PrintReturns(sig) << "));\n";
|
|
|
|
}
|
2018-11-26 13:05:51 +00:00
|
|
|
}
|
|
|
|
|
2022-02-14 18:04:55 +00:00
|
|
|
for (WasmImport imported : module->import_table) {
|
|
|
|
// TODO(wasm): Support other imports when needed.
|
|
|
|
CHECK_EQ(kExternalFunction, imported.kind);
|
|
|
|
auto module_name = PrintName(wire_bytes, imported.module_name);
|
|
|
|
auto field_name = PrintName(wire_bytes, imported.field_name);
|
|
|
|
int sig_index = module->functions[imported.index].sig_index;
|
|
|
|
os << "builder.addImport(" << module_name << ", " << field_name << ", "
|
|
|
|
<< sig_index << " /* sig */);\n";
|
|
|
|
}
|
|
|
|
|
2022-02-11 16:15:23 +00:00
|
|
|
if (module->has_memory) {
|
|
|
|
os << "builder.addMemory(" << module->initial_pages;
|
|
|
|
if (module->has_maximum_pages) {
|
|
|
|
os << ", " << module->maximum_pages;
|
|
|
|
} else {
|
|
|
|
os << ", undefined";
|
|
|
|
}
|
|
|
|
os << ", " << (module->mem_export ? "true" : "false");
|
|
|
|
if (module->has_shared_memory) {
|
|
|
|
os << ", true";
|
|
|
|
}
|
|
|
|
os << ");\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
for (WasmGlobal& global : module->globals) {
|
|
|
|
os << "builder.addGlobal(" << ValueTypeToConstantName(global.type) << ", "
|
|
|
|
<< global.mutability << ", ";
|
|
|
|
DecodeAndAppendInitExpr(os, &zone, module, wire_bytes, global.init,
|
|
|
|
global.type);
|
|
|
|
os << ");\n";
|
|
|
|
}
|
|
|
|
|
2018-01-08 11:02:45 +00:00
|
|
|
Zone tmp_zone(isolate->allocator(), ZONE_NAME);
|
|
|
|
|
2021-08-23 11:02:30 +00:00
|
|
|
// TODO(9495): Add support for tables with explicit initializers.
|
2018-11-26 13:08:20 +00:00
|
|
|
for (const WasmTable& table : module->tables) {
|
2021-08-23 11:02:30 +00:00
|
|
|
os << "builder.addTable(" << ValueTypeToConstantName(table.type) << ", "
|
|
|
|
<< table.initial_size << ", "
|
|
|
|
<< (table.has_maximum_size ? std::to_string(table.maximum_size)
|
|
|
|
: "undefined")
|
|
|
|
<< ", undefined)\n";
|
2018-11-26 13:08:20 +00:00
|
|
|
}
|
2019-01-14 11:55:10 +00:00
|
|
|
for (const WasmElemSegment& elem_segment : module->elem_segments) {
|
2021-05-19 11:01:15 +00:00
|
|
|
const char* status_str =
|
|
|
|
elem_segment.status == WasmElemSegment::kStatusActive
|
|
|
|
? "Active"
|
|
|
|
: elem_segment.status == WasmElemSegment::kStatusPassive
|
|
|
|
? "Passive"
|
|
|
|
: "Declarative";
|
|
|
|
os << "builder.add" << status_str << "ElementSegment(";
|
|
|
|
if (elem_segment.status == WasmElemSegment::kStatusActive) {
|
2021-06-30 20:35:23 +00:00
|
|
|
os << elem_segment.table_index << ", ";
|
2021-11-05 06:10:04 +00:00
|
|
|
DecodeAndAppendInitExpr(os, &zone, module, wire_bytes,
|
|
|
|
elem_segment.offset, kWasmI32);
|
2021-06-30 20:35:23 +00:00
|
|
|
os << ", ";
|
2021-05-19 11:01:15 +00:00
|
|
|
}
|
|
|
|
os << "[";
|
|
|
|
for (uint32_t i = 0; i < elem_segment.entries.size(); i++) {
|
2022-01-04 10:23:23 +00:00
|
|
|
if (elem_segment.element_type == WasmElemSegment::kExpressionElements) {
|
|
|
|
DecodeAndAppendInitExpr(os, &zone, module, wire_bytes,
|
2022-01-11 17:53:56 +00:00
|
|
|
elem_segment.entries[i], elem_segment.type);
|
2022-01-04 10:23:23 +00:00
|
|
|
} else {
|
2022-01-11 17:53:56 +00:00
|
|
|
os << elem_segment.entries[i].index();
|
2022-01-04 10:23:23 +00:00
|
|
|
}
|
2021-05-19 11:01:15 +00:00
|
|
|
if (i < elem_segment.entries.size() - 1) os << ", ";
|
2018-11-26 13:08:20 +00:00
|
|
|
}
|
2022-01-04 10:23:23 +00:00
|
|
|
os << "], "
|
|
|
|
<< (elem_segment.element_type == WasmElemSegment::kExpressionElements
|
|
|
|
? ValueTypeToConstantName(elem_segment.type)
|
|
|
|
: "undefined")
|
|
|
|
<< ");\n";
|
2018-11-26 13:08:20 +00:00
|
|
|
}
|
|
|
|
|
2021-09-06 10:10:00 +00:00
|
|
|
for (const WasmTag& tag : module->tags) {
|
|
|
|
os << "builder.addTag(makeSig(" << PrintParameters(tag.ToFunctionSig())
|
|
|
|
<< ", []));\n";
|
|
|
|
}
|
|
|
|
|
2018-01-08 11:02:45 +00:00
|
|
|
for (const WasmFunction& func : module->functions) {
|
2022-02-14 18:04:55 +00:00
|
|
|
if (func.imported) continue;
|
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const uint8_t> func_code = wire_bytes.GetFunctionBytes(&func);
|
2020-02-04 10:31:29 +00:00
|
|
|
os << "// Generate function " << (func.func_index + 1) << " (out of "
|
2018-02-01 15:18:05 +00:00
|
|
|
<< module->functions.size() << ").\n";
|
2018-01-08 11:02:45 +00:00
|
|
|
|
|
|
|
// Add function.
|
2020-02-04 10:31:29 +00:00
|
|
|
os << "builder.addFunction(undefined, " << func.sig_index
|
2018-11-26 13:05:51 +00:00
|
|
|
<< " /* sig */)\n";
|
2018-01-08 11:54:54 +00:00
|
|
|
|
|
|
|
// Add locals.
|
|
|
|
BodyLocalDecls decls(&tmp_zone);
|
[wasm-gc] read_heap_type should check if index is in module bounds
read_heap_type did not have knowledge of the module for which the heap
type was being decoded. As a result, callers of read_heap_type (or
read_value_type, which in turn calls read_heap_type) had to check after
the fact that a decoded indexed type (ref, ref null, or rtt) references
a type index within the module's bounds. This was not done consistently,
and was missing (at least) in DecodeLocals.
To avoid such problems in the future, this CL refactors read_heap_type
to accept a module and check the decoded index against it.
Changes:
- Add WasmModule argument to read_heap_type. Do so accordingly to all
its transitive callers (read_value_type, immediate arguments,
DecodeLocalDecls, DecodeValue/HeapType in unittests).
- Add index check to read_heap_type and emit an error for an
out-of-bounds index.
- Remove all other now-redundant index validations. Replace them with
decoder->ok() if needed (since read_heap_type will now emit an error).
- Fix error message in Validate for BlockTypeImmediate.
- In DecodeLocalDecls in unittests, pass an empty module to
DecodeLocalDecls in the main code.
- Add a unit test with an invalid index in local type declarations.
Bug: v8:9495
Change-Id: I4ed1204847db80f78b6ae85fa40d300cd2456295
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2569757
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71572}
2020-12-02 14:29:22 +00:00
|
|
|
DecodeLocalDecls(enabled_features, &decls, module, func_code.begin(),
|
2018-08-08 14:54:44 +00:00
|
|
|
func_code.end());
|
2018-01-08 11:54:54 +00:00
|
|
|
if (!decls.type_list.empty()) {
|
2020-02-04 10:31:29 +00:00
|
|
|
os << " ";
|
2018-01-08 11:54:54 +00:00
|
|
|
for (size_t pos = 0, count = 1, locals = decls.type_list.size();
|
|
|
|
pos < locals; pos += count, count = 1) {
|
|
|
|
ValueType type = decls.type_list[pos];
|
2020-09-15 14:18:47 +00:00
|
|
|
while (pos + count < locals && decls.type_list[pos + count] == type) {
|
2018-01-08 11:54:54 +00:00
|
|
|
++count;
|
2020-09-15 14:18:47 +00:00
|
|
|
}
|
2020-09-21 10:42:09 +00:00
|
|
|
os << ".addLocals(" << ValueTypeToConstantName(type) << ", " << count
|
|
|
|
<< ")";
|
2018-01-08 11:54:54 +00:00
|
|
|
}
|
|
|
|
os << "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add body.
|
2020-02-04 10:31:29 +00:00
|
|
|
os << " .addBodyWithEnd([\n";
|
2018-01-08 11:02:45 +00:00
|
|
|
|
2019-04-29 11:06:49 +00:00
|
|
|
FunctionBody func_body(func.sig, func.code.offset(), func_code.begin(),
|
2018-01-08 11:02:45 +00:00
|
|
|
func_code.end());
|
|
|
|
PrintRawWasmCode(isolate->allocator(), func_body, module, kOmitLocals);
|
2020-02-04 10:31:29 +00:00
|
|
|
os << "]);\n";
|
2018-02-01 15:18:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (WasmExport& exp : module->export_table) {
|
|
|
|
if (exp.kind != kExternalFunction) continue;
|
2022-02-14 18:04:55 +00:00
|
|
|
os << "builder.addExport(" << PrintName(wire_bytes, exp.name) << ", "
|
2018-02-01 15:18:05 +00:00
|
|
|
<< exp.index << ");\n";
|
2018-01-08 11:02:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (compiles) {
|
2020-02-04 10:31:29 +00:00
|
|
|
os << "const instance = builder.instantiate();\n"
|
|
|
|
"print(instance.exports.main(1, 2, 3));\n";
|
2018-01-08 11:02:45 +00:00
|
|
|
} else {
|
2020-02-04 10:31:29 +00:00
|
|
|
os << "assertThrows(function() { builder.instantiate(); }, "
|
2018-02-01 15:18:05 +00:00
|
|
|
"WebAssembly.CompileError);\n";
|
2018-01-08 11:02:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 08:02:32 +00:00
|
|
|
void OneTimeEnableStagedWasmFeatures(v8::Isolate* isolate) {
|
2020-10-05 14:50:59 +00:00
|
|
|
struct EnableStagedWasmFeatures {
|
2021-03-23 08:02:32 +00:00
|
|
|
explicit EnableStagedWasmFeatures(v8::Isolate* isolate) {
|
2020-10-05 14:50:59 +00:00
|
|
|
#define ENABLE_STAGED_FEATURES(feat, desc, val) \
|
|
|
|
FLAG_experimental_wasm_##feat = true;
|
|
|
|
FOREACH_WASM_STAGING_FEATURE_FLAG(ENABLE_STAGED_FEATURES)
|
|
|
|
#undef ENABLE_STAGED_FEATURES
|
2021-03-23 08:02:32 +00:00
|
|
|
isolate->InstallConditionalFeatures(isolate->GetCurrentContext());
|
2020-10-05 14:50:59 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
// The compiler will properly synchronize the constructor call.
|
2021-03-23 08:02:32 +00:00
|
|
|
static EnableStagedWasmFeatures one_time_enable_staged_features(isolate);
|
2020-10-05 14:50:59 +00:00
|
|
|
}
|
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
void WasmExecutionFuzzer::FuzzWasmModule(base::Vector<const uint8_t> data,
|
2018-11-20 14:41:36 +00:00
|
|
|
bool require_valid) {
|
2021-03-23 08:02:32 +00:00
|
|
|
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
|
|
|
|
v8::Isolate* isolate = support->GetIsolate();
|
2019-08-23 15:32:23 +00:00
|
|
|
|
2018-10-25 14:24:01 +00:00
|
|
|
// Strictly enforce the input size limit. Note that setting "max_len" on the
|
|
|
|
// fuzzer target is not enough, since different fuzzers are used and not all
|
|
|
|
// respect that limit.
|
2018-11-20 14:41:36 +00:00
|
|
|
if (data.size() > max_input_size()) return;
|
2018-10-25 14:24:01 +00:00
|
|
|
|
2017-09-01 13:20:46 +00:00
|
|
|
i::Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
|
2017-05-08 09:22:54 +00:00
|
|
|
|
|
|
|
// Clear any pending exceptions from a prior run.
|
2017-11-08 14:23:08 +00:00
|
|
|
i_isolate->clear_pending_exception();
|
2017-05-08 09:22:54 +00:00
|
|
|
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Context::Scope context_scope(support->GetContext());
|
2021-03-23 08:02:32 +00:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2017-05-08 09:22:54 +00:00
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
HandleScope scope(i_isolate);
|
|
|
|
|
|
|
|
AccountingAllocator allocator;
|
|
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
|
|
|
|
|
|
ZoneBuffer buffer(&zone);
|
2021-11-16 09:41:25 +00:00
|
|
|
|
|
|
|
// The first byte specifies some internal configuration, like which function
|
|
|
|
// is compiled with with compiler, and other flags.
|
|
|
|
uint8_t configuration_byte = data.empty() ? 0 : data[0];
|
2021-07-08 10:24:30 +00:00
|
|
|
if (!data.empty()) data += 1;
|
2021-11-16 09:41:25 +00:00
|
|
|
|
|
|
|
// Derive the compiler configuration for the first four functions from the
|
|
|
|
// configuration byte, to choose for each function between:
|
|
|
|
// 0: TurboFan
|
|
|
|
// 1: Liftoff
|
|
|
|
// 2: Liftoff for debugging
|
|
|
|
uint8_t tier_mask = 0;
|
|
|
|
uint8_t debug_mask = 0;
|
|
|
|
for (int i = 0; i < 4; ++i, configuration_byte /= 3) {
|
|
|
|
int compiler_config = configuration_byte % 3;
|
|
|
|
tier_mask |= (compiler_config == 0) << i;
|
|
|
|
debug_mask |= (compiler_config == 2) << i;
|
|
|
|
}
|
|
|
|
// Note: After dividing by 3 for 4 times, configuration_byte is within [0, 3].
|
|
|
|
|
2022-02-09 11:40:45 +00:00
|
|
|
// Control whether Liftoff or the interpreter will be used as the reference
|
|
|
|
// tier.
|
|
|
|
// TODO(thibaudm): Port nondeterminism detection to arm.
|
2022-02-10 10:50:25 +00:00
|
|
|
#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_X86) || \
|
|
|
|
defined(V8_TARGET_ARCH_ARM64) || defined(V8_TARGET_ARCH_ARM)
|
2022-02-09 11:40:45 +00:00
|
|
|
bool liftoff_as_reference = configuration_byte & 1;
|
|
|
|
#else
|
2021-07-08 10:24:30 +00:00
|
|
|
bool liftoff_as_reference = false;
|
2022-02-09 11:40:45 +00:00
|
|
|
#endif
|
2021-11-16 09:41:25 +00:00
|
|
|
FlagScope<bool> turbo_mid_tier_regalloc(&FLAG_turbo_force_mid_tier_regalloc,
|
|
|
|
configuration_byte == 0);
|
|
|
|
|
2021-07-16 14:46:36 +00:00
|
|
|
if (!GenerateModule(i_isolate, &zone, data, &buffer, liftoff_as_reference)) {
|
2018-11-20 14:41:36 +00:00
|
|
|
return;
|
2017-05-08 09:22:54 +00:00
|
|
|
}
|
|
|
|
|
2017-09-01 13:20:46 +00:00
|
|
|
testing::SetupIsolateForWasmModule(i_isolate);
|
2017-05-08 09:22:54 +00:00
|
|
|
|
|
|
|
ErrorThrower interpreter_thrower(i_isolate, "Interpreter");
|
2017-06-12 11:13:15 +00:00
|
|
|
ModuleWireBytes wire_bytes(buffer.begin(), buffer.end());
|
2017-05-08 09:22:54 +00:00
|
|
|
|
2021-08-05 13:58:57 +00:00
|
|
|
if (require_valid && FLAG_wasm_fuzzer_gen_test) {
|
|
|
|
GenerateTestCase(i_isolate, wire_bytes, true);
|
|
|
|
}
|
|
|
|
|
2019-11-26 16:25:14 +00:00
|
|
|
auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
|
2017-11-06 12:21:06 +00:00
|
|
|
MaybeHandle<WasmModuleObject> compiled_module;
|
|
|
|
{
|
2018-07-12 09:15:43 +00:00
|
|
|
// Explicitly enable Liftoff, disable tiering and set the tier_mask. This
|
|
|
|
// way, we deterministically test a combination of Liftoff and Turbofan.
|
|
|
|
FlagScope<bool> liftoff(&FLAG_liftoff, true);
|
|
|
|
FlagScope<bool> no_tier_up(&FLAG_wasm_tier_up, false);
|
|
|
|
FlagScope<int> tier_mask_scope(&FLAG_wasm_tier_mask_for_testing, tier_mask);
|
2021-04-06 15:42:44 +00:00
|
|
|
FlagScope<int> debug_mask_scope(&FLAG_wasm_debug_mask_for_testing,
|
|
|
|
debug_mask);
|
2021-06-18 14:29:39 +00:00
|
|
|
compiled_module = GetWasmEngine()->SyncCompile(
|
2018-08-08 14:54:44 +00:00
|
|
|
i_isolate, enabled_features, &interpreter_thrower, wire_bytes);
|
2017-11-06 12:21:06 +00:00
|
|
|
}
|
2017-07-10 19:24:22 +00:00
|
|
|
bool compiles = !compiled_module.is_null();
|
2021-08-05 13:58:57 +00:00
|
|
|
if (!require_valid && FLAG_wasm_fuzzer_gen_test) {
|
2018-01-08 11:02:45 +00:00
|
|
|
GenerateTestCase(i_isolate, wire_bytes, compiles);
|
2017-05-08 09:22:54 +00:00
|
|
|
}
|
|
|
|
|
2021-10-12 14:49:04 +00:00
|
|
|
std::string error_message;
|
|
|
|
bool result = GetWasmEngine()->SyncValidate(i_isolate, enabled_features,
|
|
|
|
wire_bytes, &error_message);
|
2021-10-07 10:16:35 +00:00
|
|
|
|
2021-10-12 14:49:04 +00:00
|
|
|
CHECK_EQ(compiles, result);
|
2021-10-07 10:16:35 +00:00
|
|
|
CHECK_WITH_MSG(
|
2021-10-12 14:49:04 +00:00
|
|
|
!require_valid || result,
|
|
|
|
("Generated module should validate, but got: " + error_message).c_str());
|
2017-07-10 19:24:22 +00:00
|
|
|
|
2018-11-20 14:41:36 +00:00
|
|
|
if (!compiles) return;
|
2017-07-10 19:24:22 +00:00
|
|
|
|
2021-07-08 10:24:30 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
InterpretAndExecuteModule(i_isolate, compiled_module.ToHandleChecked(),
|
|
|
|
module_ref, &max_steps, &nondeterminism);
|
2017-05-08 09:22:54 +00:00
|
|
|
}
|
2017-09-01 13:20:46 +00:00
|
|
|
|
|
|
|
} // namespace fuzzer
|
|
|
|
} // namespace wasm
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|