Commit 095d09f2 authored by Erik Luo's avatar Erik Luo Committed by Commit Bot

[bindings] treat interface constructors as side-effect-free

Side-effect-free evaluation may call ScriptWrappable::Wrap() or
ToV8(), which can invoke constructors. Since wrapping is assumed
to be safe, this CL automatically whitelists bindings-generated
constructors.

Bug: 829571
Change-Id: I00133e04eb6b8307ea9c4924aafc5332f69f0abd
Reviewed-on: https://chromium-review.googlesource.com/1000813Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarYuki Shiino <yukishiino@chromium.org>
Commit-Queue: Erik Luo <luoe@chromium.org>
Cr-Commit-Position: refs/heads/master@{#550584}
parent 6aee4ab7
Tests that invoking embedder constructors is side-effect-free. Should not crash.
Checking functions on 'window'
(async function(testRunner) {
var {page, session, dp} = await testRunner.startBlank(
`Tests that invoking embedder constructors is side-effect-free. Should not crash.`);
var constructorNames = await session.evaluate(`
var windowProps = Object.keys(Object.getOwnPropertyDescriptors(window));
var constructorNames = windowProps.filter(prop => {
var value = window[prop];
return typeof value === 'function' &&
value.toString().endsWith('{ [native code] }');
});
var nativeConstructors = constructorNames.map(name => window[name]);
constructorNames;
`);
// These functions are for testing-only, should not be tested.
var TestFrameworkBuiltins = new Set(['gc']);
// These functions are V8 builtins that have not yet been whitelisted.
// Do not check for test.
var V8BuiltinsWithoutSideEffect = new Set(['Function', 'Promise', 'RegExp', 'Proxy']);
// These functions are actually side-effect-free, but fail the V8
// side-effect check. Methods and attributes marked with [Affects=Nothing]
// that invoke these as constructors MUST properly handle exceptions.
var EmbedderCallbacksWithoutSideEffect = new Set(['ReadableStream', 'WritableStream', 'TransformStream']);
testRunner.log(`Checking functions on 'window'`);
for (var i = 0; i < constructorNames.length; i++) {
var name = constructorNames[i];
if (TestFrameworkBuiltins.has(name) || V8BuiltinsWithoutSideEffect.has(name))
continue;
var response = await dp.Runtime.evaluate({expression: `new nativeConstructors[${i}]`, throwOnSideEffect: true});
var exception = response.result.exceptionDetails;
var failedSideEffectCheck = exception && exception.exception.description.startsWith('EvalError: Possible side-effect in debug-evaluate');
if (failedSideEffectCheck && !EmbedderCallbacksWithoutSideEffect.has(name))
testRunner.log(`Function "${name}" failed side-effect check`);
}
testRunner.completeTest();
})
......@@ -1554,6 +1554,8 @@ Summary: `[Affects=Nothing]` indicates that a function must not produce JS-obser
Marked functions are allowed to be nondeterministic and throw exceptions, but must not set values, cache objects, or schedule execution that will be observable after the function completes. If a marked function calls into V8, it must properly handle cases when the V8 call returns an MaybeHandle.
All DOM constructors are assumed to have no JS-observable side effects.
There is not yet support for marking SymbolKeyedMethodConfigurations as side-effect free. This requires additional support in V8 to whitelist Intrinsics.
Usage: `[Affects=Nothing]` can be specified on a method, or on an attribute to indicate that its getter callback is side effect free:
......
......@@ -794,8 +794,11 @@ v8::Local<v8::FunctionTemplate> V8DOMConfiguration::DomClassTemplate(
if (!interface_template.IsEmpty())
return interface_template;
// We assume all constructors have no JS-observable side effect.
interface_template = v8::FunctionTemplate::New(
isolate, V8ObjectConstructor::IsValidConstructorMode);
isolate, V8ObjectConstructor::IsValidConstructorMode,
v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0,
v8::ConstructorBehavior::kAllow, v8::SideEffectType::kHasNoSideEffect);
configure_dom_class_template(isolate, world, interface_template);
data->SetInterfaceTemplate(world, wrapper_type_info, interface_template);
return interface_template;
......
......@@ -745,7 +745,7 @@ v8::Local<v8::FunctionTemplate> {{v8_class}}Constructor::domTemplate(v8::Isolate
if (!result.IsEmpty())
return result;
result = v8::FunctionTemplate::New(isolate, {{v8_class}}ConstructorCallback);
result = v8::FunctionTemplate::New(isolate, {{v8_class}}ConstructorCallback, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow, v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::ObjectTemplate> instanceTemplate = result->InstanceTemplate();
instanceTemplate->SetInternalFieldCount({{v8_class}}::internalFieldCount);
result->SetClassName(V8AtomicString(isolate, "{{named_constructor.name}}"));
......
......@@ -452,7 +452,7 @@ static void install{{v8_class}}Template(
'v8::Local<v8::FunctionTemplate>()' %}
V8DOMConfiguration::InitializeDOMInterfaceTemplate(isolate, interfaceTemplate, {{v8_class}}::wrapperTypeInfo.interface_name, {{parent_interface_template}}, {{v8_class}}::internalFieldCount);
{% if constructors or has_custom_constructor or has_html_constructor %}
interfaceTemplate->SetCallHandler({{v8_class}}::constructorCallback);
interfaceTemplate->SetCallHandler({{v8_class}}::constructorCallback, v8::Local<v8::Value>(), v8::SideEffectType::kHasNoSideEffect);
interfaceTemplate->SetLength({{interface_length}});
{% endif %}
{% endif %}{# is_partial #}
......
......@@ -693,7 +693,7 @@ void V8TestInterface2::installV8TestInterface2Template(
v8::Local<v8::FunctionTemplate> interfaceTemplate) {
// Initialize the interface object's template.
V8DOMConfiguration::InitializeDOMInterfaceTemplate(isolate, interfaceTemplate, V8TestInterface2::wrapperTypeInfo.interface_name, v8::Local<v8::FunctionTemplate>(), V8TestInterface2::internalFieldCount);
interfaceTemplate->SetCallHandler(V8TestInterface2::constructorCallback);
interfaceTemplate->SetCallHandler(V8TestInterface2::constructorCallback, v8::Local<v8::Value>(), v8::SideEffectType::kHasNoSideEffect);
interfaceTemplate->SetLength(0);
v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interfaceTemplate);
......
......@@ -431,7 +431,7 @@ v8::Local<v8::FunctionTemplate> V8TestInterfaceConstructorConstructor::domTempla
if (!result.IsEmpty())
return result;
result = v8::FunctionTemplate::New(isolate, V8TestInterfaceConstructorConstructorCallback);
result = v8::FunctionTemplate::New(isolate, V8TestInterfaceConstructorConstructorCallback, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow, v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::ObjectTemplate> instanceTemplate = result->InstanceTemplate();
instanceTemplate->SetInternalFieldCount(V8TestInterfaceConstructor::internalFieldCount);
result->SetClassName(V8AtomicString(isolate, "Audio"));
......@@ -492,7 +492,7 @@ static void installV8TestInterfaceConstructorTemplate(
v8::Local<v8::FunctionTemplate> interfaceTemplate) {
// Initialize the interface object's template.
V8DOMConfiguration::InitializeDOMInterfaceTemplate(isolate, interfaceTemplate, V8TestInterfaceConstructor::wrapperTypeInfo.interface_name, v8::Local<v8::FunctionTemplate>(), V8TestInterfaceConstructor::internalFieldCount);
interfaceTemplate->SetCallHandler(V8TestInterfaceConstructor::constructorCallback);
interfaceTemplate->SetCallHandler(V8TestInterfaceConstructor::constructorCallback, v8::Local<v8::Value>(), v8::SideEffectType::kHasNoSideEffect);
interfaceTemplate->SetLength(0);
v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interfaceTemplate);
......
......@@ -255,7 +255,7 @@ static void installV8TestInterfaceConstructor2Template(
v8::Local<v8::FunctionTemplate> interfaceTemplate) {
// Initialize the interface object's template.
V8DOMConfiguration::InitializeDOMInterfaceTemplate(isolate, interfaceTemplate, V8TestInterfaceConstructor2::wrapperTypeInfo.interface_name, v8::Local<v8::FunctionTemplate>(), V8TestInterfaceConstructor2::internalFieldCount);
interfaceTemplate->SetCallHandler(V8TestInterfaceConstructor2::constructorCallback);
interfaceTemplate->SetCallHandler(V8TestInterfaceConstructor2::constructorCallback, v8::Local<v8::Value>(), v8::SideEffectType::kHasNoSideEffect);
interfaceTemplate->SetLength(1);
v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interfaceTemplate);
......
......@@ -105,7 +105,7 @@ static void installV8TestInterfaceConstructor3Template(
v8::Local<v8::FunctionTemplate> interfaceTemplate) {
// Initialize the interface object's template.
V8DOMConfiguration::InitializeDOMInterfaceTemplate(isolate, interfaceTemplate, V8TestInterfaceConstructor3::wrapperTypeInfo.interface_name, v8::Local<v8::FunctionTemplate>(), V8TestInterfaceConstructor3::internalFieldCount);
interfaceTemplate->SetCallHandler(V8TestInterfaceConstructor3::constructorCallback);
interfaceTemplate->SetCallHandler(V8TestInterfaceConstructor3::constructorCallback, v8::Local<v8::Value>(), v8::SideEffectType::kHasNoSideEffect);
interfaceTemplate->SetLength(1);
v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interfaceTemplate);
......
......@@ -139,7 +139,7 @@ static void installV8TestInterfaceConstructor4Template(
v8::Local<v8::FunctionTemplate> interfaceTemplate) {
// Initialize the interface object's template.
V8DOMConfiguration::InitializeDOMInterfaceTemplate(isolate, interfaceTemplate, V8TestInterfaceConstructor4::wrapperTypeInfo.interface_name, v8::Local<v8::FunctionTemplate>(), V8TestInterfaceConstructor4::internalFieldCount);
interfaceTemplate->SetCallHandler(V8TestInterfaceConstructor4::constructorCallback);
interfaceTemplate->SetCallHandler(V8TestInterfaceConstructor4::constructorCallback, v8::Local<v8::Value>(), v8::SideEffectType::kHasNoSideEffect);
interfaceTemplate->SetLength(1);
v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interfaceTemplate);
......
......@@ -84,7 +84,7 @@ static void installV8TestInterfaceCustomConstructorTemplate(
v8::Local<v8::FunctionTemplate> interfaceTemplate) {
// Initialize the interface object's template.
V8DOMConfiguration::InitializeDOMInterfaceTemplate(isolate, interfaceTemplate, V8TestInterfaceCustomConstructor::wrapperTypeInfo.interface_name, v8::Local<v8::FunctionTemplate>(), V8TestInterfaceCustomConstructor::internalFieldCount);
interfaceTemplate->SetCallHandler(V8TestInterfaceCustomConstructor::constructorCallback);
interfaceTemplate->SetCallHandler(V8TestInterfaceCustomConstructor::constructorCallback, v8::Local<v8::Value>(), v8::SideEffectType::kHasNoSideEffect);
interfaceTemplate->SetLength(0);
v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interfaceTemplate);
......
......@@ -151,7 +151,7 @@ static void installV8TestInterfaceEventInitConstructorTemplate(
v8::Local<v8::FunctionTemplate> interfaceTemplate) {
// Initialize the interface object's template.
V8DOMConfiguration::InitializeDOMInterfaceTemplate(isolate, interfaceTemplate, V8TestInterfaceEventInitConstructor::wrapperTypeInfo.interface_name, V8Event::domTemplate(isolate, world), V8TestInterfaceEventInitConstructor::internalFieldCount);
interfaceTemplate->SetCallHandler(V8TestInterfaceEventInitConstructor::constructorCallback);
interfaceTemplate->SetCallHandler(V8TestInterfaceEventInitConstructor::constructorCallback, v8::Local<v8::Value>(), v8::SideEffectType::kHasNoSideEffect);
interfaceTemplate->SetLength(2);
v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interfaceTemplate);
......
......@@ -112,7 +112,7 @@ v8::Local<v8::FunctionTemplate> V8TestInterfaceEventTargetConstructor::domTempla
if (!result.IsEmpty())
return result;
result = v8::FunctionTemplate::New(isolate, V8TestInterfaceEventTargetConstructorCallback);
result = v8::FunctionTemplate::New(isolate, V8TestInterfaceEventTargetConstructorCallback, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow, v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::ObjectTemplate> instanceTemplate = result->InstanceTemplate();
instanceTemplate->SetInternalFieldCount(V8TestInterfaceEventTarget::internalFieldCount);
result->SetClassName(V8AtomicString(isolate, "Name"));
......
......@@ -413,7 +413,7 @@ static void installV8TestInterfaceGarbageCollectedTemplate(
v8::Local<v8::FunctionTemplate> interfaceTemplate) {
// Initialize the interface object's template.
V8DOMConfiguration::InitializeDOMInterfaceTemplate(isolate, interfaceTemplate, V8TestInterfaceGarbageCollected::wrapperTypeInfo.interface_name, V8EventTarget::domTemplate(isolate, world), V8TestInterfaceGarbageCollected::internalFieldCount);
interfaceTemplate->SetCallHandler(V8TestInterfaceGarbageCollected::constructorCallback);
interfaceTemplate->SetCallHandler(V8TestInterfaceGarbageCollected::constructorCallback, v8::Local<v8::Value>(), v8::SideEffectType::kHasNoSideEffect);
interfaceTemplate->SetLength(1);
v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interfaceTemplate);
......
......@@ -195,7 +195,7 @@ v8::Local<v8::FunctionTemplate> V8TestInterfaceNamedConstructorConstructor::domT
if (!result.IsEmpty())
return result;
result = v8::FunctionTemplate::New(isolate, V8TestInterfaceNamedConstructorConstructorCallback);
result = v8::FunctionTemplate::New(isolate, V8TestInterfaceNamedConstructorConstructorCallback, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow, v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::ObjectTemplate> instanceTemplate = result->InstanceTemplate();
instanceTemplate->SetInternalFieldCount(V8TestInterfaceNamedConstructor::internalFieldCount);
result->SetClassName(V8AtomicString(isolate, "Audio"));
......
......@@ -121,7 +121,7 @@ v8::Local<v8::FunctionTemplate> V8TestInterfaceNamedConstructor2Constructor::dom
if (!result.IsEmpty())
return result;
result = v8::FunctionTemplate::New(isolate, V8TestInterfaceNamedConstructor2ConstructorCallback);
result = v8::FunctionTemplate::New(isolate, V8TestInterfaceNamedConstructor2ConstructorCallback, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow, v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::ObjectTemplate> instanceTemplate = result->InstanceTemplate();
instanceTemplate->SetInternalFieldCount(V8TestInterfaceNamedConstructor2::internalFieldCount);
result->SetClassName(V8AtomicString(isolate, "Audio"));
......
......@@ -265,7 +265,7 @@ static void installV8TestNodeTemplate(
v8::Local<v8::FunctionTemplate> interfaceTemplate) {
// Initialize the interface object's template.
V8DOMConfiguration::InitializeDOMInterfaceTemplate(isolate, interfaceTemplate, V8TestNode::wrapperTypeInfo.interface_name, V8Node::domTemplate(isolate, world), V8TestNode::internalFieldCount);
interfaceTemplate->SetCallHandler(V8TestNode::constructorCallback);
interfaceTemplate->SetCallHandler(V8TestNode::constructorCallback, v8::Local<v8::Value>(), v8::SideEffectType::kHasNoSideEffect);
interfaceTemplate->SetLength(0);
v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interfaceTemplate);
......
......@@ -577,7 +577,7 @@ static void installV8TestTypedefsTemplate(
v8::Local<v8::FunctionTemplate> interfaceTemplate) {
// Initialize the interface object's template.
V8DOMConfiguration::InitializeDOMInterfaceTemplate(isolate, interfaceTemplate, V8TestTypedefs::wrapperTypeInfo.interface_name, v8::Local<v8::FunctionTemplate>(), V8TestTypedefs::internalFieldCount);
interfaceTemplate->SetCallHandler(V8TestTypedefs::constructorCallback);
interfaceTemplate->SetCallHandler(V8TestTypedefs::constructorCallback, v8::Local<v8::Value>(), v8::SideEffectType::kHasNoSideEffect);
interfaceTemplate->SetLength(1);
v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interfaceTemplate);
......
......@@ -102,7 +102,7 @@ static void installV8TestVariadicConstructorArgumentsTemplate(
v8::Local<v8::FunctionTemplate> interfaceTemplate) {
// Initialize the interface object's template.
V8DOMConfiguration::InitializeDOMInterfaceTemplate(isolate, interfaceTemplate, V8TestVariadicConstructorArguments::wrapperTypeInfo.interface_name, v8::Local<v8::FunctionTemplate>(), V8TestVariadicConstructorArguments::internalFieldCount);
interfaceTemplate->SetCallHandler(V8TestVariadicConstructorArguments::constructorCallback);
interfaceTemplate->SetCallHandler(V8TestVariadicConstructorArguments::constructorCallback, v8::Local<v8::Value>(), v8::SideEffectType::kHasNoSideEffect);
interfaceTemplate->SetLength(0);
v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interfaceTemplate);
......
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