Commit 16d9298a authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[api] Add callback to set up conditional features

Origin trials allow webpages to use experimental features even though
the features are not yet enabled by default. These features will then
get enabled per execution context: it is possible that the feature is
enabled in one execution context but disabled in another execution
context. In V8 we check for origin trials by calling a callback provided
by the embedder that takes the context as a parameter and returns
whether a feature is enabled in this context or not.

This approach fails when a feature changes the context itself, e.g. by
extending the global object. In that case the context is not available
yet to check for the origin trial.

To solve the problem this CL adds a new API function that can be called
by the embedder to notify V8 that context with the origin trial
information is finished. After that V8 can read the origin trial
information from the context and extend e.g. the global object with the
origin trial features.

Additionally to the API this CL also adds code to enable the
WebAssembly.Exception constructor conditionally, depending on whether
it has been enabled by an origin trial or not.

The Blink-side change: https://crrev.com/c/2775573

R=ulan@chromium.org, jkummerow@chromium.org

Change-Id: Ic05c4a89eb3e0e31469e49da8767d630c43b2e00
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2773287Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73597}
parent 521c2ff7
......@@ -9493,6 +9493,13 @@ class V8_EXPORT Isolate {
void SetWasmExceptionsEnabledCallback(WasmExceptionsEnabledCallback callback);
/**
* This function can be called by the embedder to signal V8 that the dynamic
* enabling of features has finished. V8 can now set up dynamically added
* features.
*/
void InstallConditionalFeatures(Local<Context> context);
/**
* Check if V8 is dead and therefore unusable. This is the case after
* fatal errors such as out-of-memory situations.
......
......@@ -115,6 +115,7 @@
#include "src/wasm/streaming-decoder.h"
#include "src/wasm/value-type.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-result.h"
#include "src/wasm/wasm-serialization.h"
......@@ -8879,6 +8880,13 @@ CALLBACK_SETTER(WasmSimdEnabledCallback, WasmSimdEnabledCallback,
CALLBACK_SETTER(WasmExceptionsEnabledCallback, WasmExceptionsEnabledCallback,
wasm_exceptions_enabled_callback)
void Isolate::InstallConditionalFeatures(Local<Context> context) {
#if V8_ENABLE_WEBASSEMBLY
i::WasmJs::InstallConditionalFeatures(reinterpret_cast<i::Isolate*>(this),
Utils::OpenHandle(*context));
#endif // V8_ENABLE_WEBASSEMBLY
}
void Isolate::AddNearHeapLimitCallback(v8::NearHeapLimitCallback callback,
void* data) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
......
......@@ -23,11 +23,17 @@ WasmFeatures WasmFeatures::FromFlags() {
// static
WasmFeatures WasmFeatures::FromIsolate(Isolate* isolate) {
return FromContext(isolate, handle(isolate->context(), isolate));
}
// static
WasmFeatures WasmFeatures::FromContext(Isolate* isolate,
Handle<Context> context) {
WasmFeatures features = WasmFeatures::FromFlags();
if (isolate->IsWasmSimdEnabled(handle(isolate->context(), isolate))) {
if (isolate->IsWasmSimdEnabled(context)) {
features.Add(kFeature_simd);
}
if (isolate->AreWasmExceptionsEnabled(handle(isolate->context(), isolate))) {
if (isolate->AreWasmExceptionsEnabled(context)) {
features.Add(kFeature_eh);
}
return features;
......
......@@ -20,6 +20,9 @@
namespace v8 {
namespace internal {
class Context;
template <typename T>
class Handle;
class Isolate;
namespace wasm {
......@@ -57,6 +60,8 @@ class WasmFeatures : public base::EnumSet<WasmFeature> {
static inline constexpr WasmFeatures ForAsmjs();
static WasmFeatures FromFlags();
static V8_EXPORT_PRIVATE WasmFeatures FromIsolate(Isolate*);
static V8_EXPORT_PRIVATE WasmFeatures FromContext(Isolate*,
Handle<Context> context);
};
// static
......
......@@ -2242,26 +2242,26 @@ void WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) {
v8_str(isolate, "WebAssembly.Global"), ro_attributes);
// Setup Exception
Handle<String> exception_name = v8_str(isolate, "Exception");
Handle<JSFunction> exception_constructor =
CreateFunc(isolate, exception_name, WebAssemblyException, true,
SideEffectType::kHasSideEffect);
exception_constructor->shared().set_length(1);
if (enabled_features.has_eh()) {
Handle<String> exception_name = v8_str(isolate, "Exception");
Handle<JSFunction> exception_constructor =
CreateFunc(isolate, exception_name, WebAssemblyException, true,
SideEffectType::kHasSideEffect);
exception_constructor->shared().set_length(1);
JSObject::AddProperty(isolate, webassembly, exception_name,
exception_constructor, DONT_ENUM);
// Install the constructor on the context unconditionally so that it is also
// available when the feature is enabled via the origin trial.
context->set_wasm_exception_constructor(*exception_constructor);
SetDummyInstanceTemplate(isolate, exception_constructor);
JSFunction::EnsureHasInitialMap(exception_constructor);
Handle<JSObject> exception_proto(
JSObject::cast(exception_constructor->instance_prototype()), isolate);
Handle<Map> exception_map = isolate->factory()->NewMap(
i::WASM_EXCEPTION_OBJECT_TYPE, WasmExceptionObject::kHeaderSize);
JSFunction::SetInitialMap(isolate, exception_constructor, exception_map,
exception_proto);
}
// Install the constructor on the context unconditionally so that it is also
// available when the feature is enabled via the origin trial.
context->set_wasm_exception_constructor(*exception_constructor);
SetDummyInstanceTemplate(isolate, exception_constructor);
JSFunction::EnsureHasInitialMap(exception_constructor);
Handle<JSObject> exception_proto(
JSObject::cast(exception_constructor->instance_prototype()), isolate);
Handle<Map> exception_map = isolate->factory()->NewMap(
i::WASM_EXCEPTION_OBJECT_TYPE, WasmExceptionObject::kHeaderSize);
JSFunction::SetInitialMap(isolate, exception_constructor, exception_map,
exception_proto);
// Setup Function
if (enabled_features.has_type_reflection()) {
......@@ -2308,6 +2308,30 @@ void WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) {
runtime_error, DONT_ENUM);
}
// static
void WasmJs::InstallConditionalFeatures(Isolate* isolate,
Handle<Context> context) {
// Exception handling may have been enabled by an origin trial. If so, make
// sure that the {WebAssembly.Exception} constructor is set up.
auto enabled_features = i::wasm::WasmFeatures::FromContext(isolate, context);
if (enabled_features.has_eh()) {
Handle<JSGlobalObject> global = handle(context->global_object(), isolate);
MaybeHandle<Object> maybe_webassembly =
JSObject::GetProperty(isolate, global, "WebAssembly");
Handle<JSObject> webassembly =
Handle<JSObject>::cast(maybe_webassembly.ToHandleChecked());
// Setup Exception
Handle<String> exception_name = v8_str(isolate, "Exception");
if (!JSObject::HasProperty(webassembly, exception_name).FromMaybe(true)) {
Handle<JSFunction> exception_constructor =
CreateFunc(isolate, exception_name, WebAssemblyException, true,
SideEffectType::kHasSideEffect);
exception_constructor->shared().set_length(1);
JSObject::AddProperty(isolate, webassembly, exception_name,
exception_constructor, DONT_ENUM);
}
}
}
#undef ASSIGN
#undef EXTRACT_THIS
......
......@@ -13,7 +13,9 @@
namespace v8 {
namespace internal {
class WasmFrame;
class Context;
template <typename T>
class Handle;
namespace wasm {
class StreamingDecoder;
......@@ -24,6 +26,9 @@ class WasmJs {
public:
V8_EXPORT_PRIVATE static void Install(Isolate* isolate,
bool exposed_on_global_object);
V8_EXPORT_PRIVATE static void InstallConditionalFeatures(
Isolate* isolate, Handle<Context> context);
};
} // namespace internal
......
......@@ -45,18 +45,14 @@ class AsyncFuzzerResolver : public i::wasm::CompilationResultResolver {
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// 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();
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
// Set some more flags.
FLAG_wasm_async_compilation = true;
FLAG_wasm_max_mem_pages = 32;
FLAG_wasm_max_table_size = 100;
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
i::Isolate* i_isolate = reinterpret_cast<v8::internal::Isolate*>(isolate);
// Clear any pending exceptions from a prior run.
......@@ -68,6 +64,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8::HandleScope handle_scope(isolate);
i::HandleScope internal_scope(i_isolate);
v8::Context::Scope context_scope(support->GetContext());
// 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);
TryCatch try_catch(isolate);
testing::SetupIsolateForWasmModule(i_isolate);
......
......@@ -307,33 +307,30 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
}
}
void OneTimeEnableStagedWasmFeatures() {
void OneTimeEnableStagedWasmFeatures(v8::Isolate* isolate) {
struct EnableStagedWasmFeatures {
EnableStagedWasmFeatures() {
explicit EnableStagedWasmFeatures(v8::Isolate* isolate) {
#define ENABLE_STAGED_FEATURES(feat, desc, val) \
FLAG_experimental_wasm_##feat = true;
FOREACH_WASM_STAGING_FEATURE_FLAG(ENABLE_STAGED_FEATURES)
#undef ENABLE_STAGED_FEATURES
isolate->InstallConditionalFeatures(isolate->GetCurrentContext());
}
};
// The compiler will properly synchronize the constructor call.
static EnableStagedWasmFeatures one_time_enable_staged_features;
static EnableStagedWasmFeatures one_time_enable_staged_features(isolate);
}
void WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data,
bool require_valid) {
// 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();
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
// 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.
if (data.size() > max_input_size()) return;
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
i::Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
// Clear any pending exceptions from a prior run.
......@@ -342,6 +339,12 @@ void WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data,
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
// 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);
v8::TryCatch try_catch(isolate);
HandleScope scope(i_isolate);
......
......@@ -33,7 +33,7 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
// no-ops. This avoids race conditions with threads reading the flags. Fuzzers
// are executed in their own process anyway, so this should not interfere with
// anything.
void OneTimeEnableStagedWasmFeatures();
void OneTimeEnableStagedWasmFeatures(v8::Isolate* isolate);
class WasmExecutionFuzzer {
public:
......
......@@ -21,18 +21,14 @@
namespace i = v8::internal;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// 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.
i::wasm::fuzzer::OneTimeEnableStagedWasmFeatures();
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
// We reduce the maximum memory size and table size of WebAssembly instances
// to avoid OOMs in the fuzzer.
i::FLAG_wasm_max_mem_pages = 32;
i::FLAG_wasm_max_table_size = 100;
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
// Clear any pending exceptions from a prior run.
......@@ -43,6 +39,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
// 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.
i::wasm::fuzzer::OneTimeEnableStagedWasmFeatures(isolate);
v8::TryCatch try_catch(isolate);
i::wasm::testing::SetupIsolateForWasmModule(i_isolate);
i::wasm::ModuleWireBytes wire_bytes(data, data + size);
......
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