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 { ...@@ -9493,6 +9493,13 @@ class V8_EXPORT Isolate {
void SetWasmExceptionsEnabledCallback(WasmExceptionsEnabledCallback callback); 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 * Check if V8 is dead and therefore unusable. This is the case after
* fatal errors such as out-of-memory situations. * fatal errors such as out-of-memory situations.
......
...@@ -115,6 +115,7 @@ ...@@ -115,6 +115,7 @@
#include "src/wasm/streaming-decoder.h" #include "src/wasm/streaming-decoder.h"
#include "src/wasm/value-type.h" #include "src/wasm/value-type.h"
#include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-objects-inl.h" #include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-result.h" #include "src/wasm/wasm-result.h"
#include "src/wasm/wasm-serialization.h" #include "src/wasm/wasm-serialization.h"
...@@ -8879,6 +8880,13 @@ CALLBACK_SETTER(WasmSimdEnabledCallback, WasmSimdEnabledCallback, ...@@ -8879,6 +8880,13 @@ CALLBACK_SETTER(WasmSimdEnabledCallback, WasmSimdEnabledCallback,
CALLBACK_SETTER(WasmExceptionsEnabledCallback, WasmExceptionsEnabledCallback, CALLBACK_SETTER(WasmExceptionsEnabledCallback, WasmExceptionsEnabledCallback,
wasm_exceptions_enabled_callback) 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 Isolate::AddNearHeapLimitCallback(v8::NearHeapLimitCallback callback,
void* data) { void* data) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this); i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
......
...@@ -23,11 +23,17 @@ WasmFeatures WasmFeatures::FromFlags() { ...@@ -23,11 +23,17 @@ WasmFeatures WasmFeatures::FromFlags() {
// static // static
WasmFeatures WasmFeatures::FromIsolate(Isolate* isolate) { 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(); WasmFeatures features = WasmFeatures::FromFlags();
if (isolate->IsWasmSimdEnabled(handle(isolate->context(), isolate))) { if (isolate->IsWasmSimdEnabled(context)) {
features.Add(kFeature_simd); features.Add(kFeature_simd);
} }
if (isolate->AreWasmExceptionsEnabled(handle(isolate->context(), isolate))) { if (isolate->AreWasmExceptionsEnabled(context)) {
features.Add(kFeature_eh); features.Add(kFeature_eh);
} }
return features; return features;
......
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
class Context;
template <typename T>
class Handle;
class Isolate; class Isolate;
namespace wasm { namespace wasm {
...@@ -57,6 +60,8 @@ class WasmFeatures : public base::EnumSet<WasmFeature> { ...@@ -57,6 +60,8 @@ class WasmFeatures : public base::EnumSet<WasmFeature> {
static inline constexpr WasmFeatures ForAsmjs(); static inline constexpr WasmFeatures ForAsmjs();
static WasmFeatures FromFlags(); static WasmFeatures FromFlags();
static V8_EXPORT_PRIVATE WasmFeatures FromIsolate(Isolate*); static V8_EXPORT_PRIVATE WasmFeatures FromIsolate(Isolate*);
static V8_EXPORT_PRIVATE WasmFeatures FromContext(Isolate*,
Handle<Context> context);
}; };
// static // static
......
...@@ -2242,26 +2242,26 @@ void WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) { ...@@ -2242,26 +2242,26 @@ void WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) {
v8_str(isolate, "WebAssembly.Global"), ro_attributes); v8_str(isolate, "WebAssembly.Global"), ro_attributes);
// Setup Exception // 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()) { 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, JSObject::AddProperty(isolate, webassembly, exception_name,
exception_constructor, DONT_ENUM); 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 // Setup Function
if (enabled_features.has_type_reflection()) { if (enabled_features.has_type_reflection()) {
...@@ -2308,6 +2308,30 @@ void WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) { ...@@ -2308,6 +2308,30 @@ void WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) {
runtime_error, DONT_ENUM); 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 ASSIGN
#undef EXTRACT_THIS #undef EXTRACT_THIS
......
...@@ -13,7 +13,9 @@ ...@@ -13,7 +13,9 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
class WasmFrame; class Context;
template <typename T>
class Handle;
namespace wasm { namespace wasm {
class StreamingDecoder; class StreamingDecoder;
...@@ -24,6 +26,9 @@ class WasmJs { ...@@ -24,6 +26,9 @@ class WasmJs {
public: public:
V8_EXPORT_PRIVATE static void Install(Isolate* isolate, V8_EXPORT_PRIVATE static void Install(Isolate* isolate,
bool exposed_on_global_object); bool exposed_on_global_object);
V8_EXPORT_PRIVATE static void InstallConditionalFeatures(
Isolate* isolate, Handle<Context> context);
}; };
} // namespace internal } // namespace internal
......
...@@ -45,18 +45,14 @@ class AsyncFuzzerResolver : public i::wasm::CompilationResultResolver { ...@@ -45,18 +45,14 @@ class AsyncFuzzerResolver : public i::wasm::CompilationResultResolver {
}; };
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// We explicitly enable staged WebAssembly features here to increase fuzzer v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
// coverage. For libfuzzer fuzzers it is not possible that the fuzzer enables v8::Isolate* isolate = support->GetIsolate();
// the flag by itself.
OneTimeEnableStagedWasmFeatures();
// Set some more flags. // Set some more flags.
FLAG_wasm_async_compilation = true; FLAG_wasm_async_compilation = true;
FLAG_wasm_max_mem_pages = 32; FLAG_wasm_max_mem_pages = 32;
FLAG_wasm_max_table_size = 100; 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); i::Isolate* i_isolate = reinterpret_cast<v8::internal::Isolate*>(isolate);
// Clear any pending exceptions from a prior run. // Clear any pending exceptions from a prior run.
...@@ -68,6 +64,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ...@@ -68,6 +64,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
i::HandleScope internal_scope(i_isolate); i::HandleScope internal_scope(i_isolate);
v8::Context::Scope context_scope(support->GetContext()); 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); TryCatch try_catch(isolate);
testing::SetupIsolateForWasmModule(i_isolate); testing::SetupIsolateForWasmModule(i_isolate);
......
...@@ -307,33 +307,30 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes, ...@@ -307,33 +307,30 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
} }
} }
void OneTimeEnableStagedWasmFeatures() { void OneTimeEnableStagedWasmFeatures(v8::Isolate* isolate) {
struct EnableStagedWasmFeatures { struct EnableStagedWasmFeatures {
EnableStagedWasmFeatures() { explicit EnableStagedWasmFeatures(v8::Isolate* isolate) {
#define ENABLE_STAGED_FEATURES(feat, desc, val) \ #define ENABLE_STAGED_FEATURES(feat, desc, val) \
FLAG_experimental_wasm_##feat = true; FLAG_experimental_wasm_##feat = true;
FOREACH_WASM_STAGING_FEATURE_FLAG(ENABLE_STAGED_FEATURES) FOREACH_WASM_STAGING_FEATURE_FLAG(ENABLE_STAGED_FEATURES)
#undef ENABLE_STAGED_FEATURES #undef ENABLE_STAGED_FEATURES
isolate->InstallConditionalFeatures(isolate->GetCurrentContext());
} }
}; };
// The compiler will properly synchronize the constructor call. // 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, void WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data,
bool require_valid) { bool require_valid) {
// We explicitly enable staged WebAssembly features here to increase fuzzer v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
// coverage. For libfuzzer fuzzers it is not possible that the fuzzer enables v8::Isolate* isolate = support->GetIsolate();
// the flag by itself.
OneTimeEnableStagedWasmFeatures();
// Strictly enforce the input size limit. Note that setting "max_len" on the // 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 // fuzzer target is not enough, since different fuzzers are used and not all
// respect that limit. // respect that limit.
if (data.size() > max_input_size()) return; 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); i::Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
// Clear any pending exceptions from a prior run. // Clear any pending exceptions from a prior run.
...@@ -342,6 +339,12 @@ void WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data, ...@@ -342,6 +339,12 @@ void WasmExecutionFuzzer::FuzzWasmModule(Vector<const uint8_t> data,
v8::Isolate::Scope isolate_scope(isolate); v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext()); 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); v8::TryCatch try_catch(isolate);
HandleScope scope(i_isolate); HandleScope scope(i_isolate);
......
...@@ -33,7 +33,7 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes, ...@@ -33,7 +33,7 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
// no-ops. This avoids race conditions with threads reading the flags. Fuzzers // 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 // are executed in their own process anyway, so this should not interfere with
// anything. // anything.
void OneTimeEnableStagedWasmFeatures(); void OneTimeEnableStagedWasmFeatures(v8::Isolate* isolate);
class WasmExecutionFuzzer { class WasmExecutionFuzzer {
public: public:
......
...@@ -21,18 +21,14 @@ ...@@ -21,18 +21,14 @@
namespace i = v8::internal; namespace i = v8::internal;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// We explicitly enable staged WebAssembly features here to increase fuzzer v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
// coverage. For libfuzzer fuzzers it is not possible that the fuzzer enables v8::Isolate* isolate = support->GetIsolate();
// the flag by itself.
i::wasm::fuzzer::OneTimeEnableStagedWasmFeatures();
// We reduce the maximum memory size and table size of WebAssembly instances // We reduce the maximum memory size and table size of WebAssembly instances
// to avoid OOMs in the fuzzer. // to avoid OOMs in the fuzzer.
i::FLAG_wasm_max_mem_pages = 32; i::FLAG_wasm_max_mem_pages = 32;
i::FLAG_wasm_max_table_size = 100; 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); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
// Clear any pending exceptions from a prior run. // Clear any pending exceptions from a prior run.
...@@ -43,6 +39,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ...@@ -43,6 +39,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8::Isolate::Scope isolate_scope(isolate); v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext()); 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); v8::TryCatch try_catch(isolate);
i::wasm::testing::SetupIsolateForWasmModule(i_isolate); i::wasm::testing::SetupIsolateForWasmModule(i_isolate);
i::wasm::ModuleWireBytes wire_bytes(data, data + size); 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