Commit a65c5fb7 authored by Andrey Kosyakov's avatar Andrey Kosyakov Committed by Commit Bot

DevTools: ensure binding is only exposed into the specified context

... when addBinding is called with contextId. Previously, due to
a subtle type, we exposed bidings added with executionContextId to
all contexts created after the binding was added.

Also, do not persist context-specific bindings to agent state,
as context ids don't make sense across the process.

This also adds a test instrastructure to create additional context in
given context group.

Change-Id: I1b3e96cb65b756424bc7872d200bbbf41e4c30b8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2440982Reviewed-by: 's avatarSimon Zünd <szuend@chromium.org>
Commit-Queue: Andrey Kosyakov <caseq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70261}
parent 54f852d1
...@@ -665,13 +665,7 @@ void V8RuntimeAgentImpl::terminateExecution( ...@@ -665,13 +665,7 @@ void V8RuntimeAgentImpl::terminateExecution(
Response V8RuntimeAgentImpl::addBinding(const String16& name, Response V8RuntimeAgentImpl::addBinding(const String16& name,
Maybe<int> executionContextId) { Maybe<int> executionContextId) {
if (!m_state->getObject(V8RuntimeAgentImplState::bindings)) { if (m_activeBindings.count(name)) return Response::Success();
m_state->setObject(V8RuntimeAgentImplState::bindings,
protocol::DictionaryValue::create());
}
protocol::DictionaryValue* bindings =
m_state->getObject(V8RuntimeAgentImplState::bindings);
if (bindings->booleanProperty(name, false)) return Response::Success();
if (executionContextId.isJust()) { if (executionContextId.isJust()) {
int contextId = executionContextId.fromJust(); int contextId = executionContextId.fromJust();
InspectedContext* context = InspectedContext* context =
...@@ -681,10 +675,16 @@ Response V8RuntimeAgentImpl::addBinding(const String16& name, ...@@ -681,10 +675,16 @@ Response V8RuntimeAgentImpl::addBinding(const String16& name,
"Cannot find execution context with given executionContextId"); "Cannot find execution context with given executionContextId");
} }
addBinding(context, name); addBinding(context, name);
// false means that we should not add this binding later.
bindings->setBoolean(name, false);
return Response::Success(); return Response::Success();
} }
// Only persist non context-specific bindings, as contextIds don't make
// any sense when state is restored in a different process.
if (!m_state->getObject(V8RuntimeAgentImplState::bindings)) {
m_state->setObject(V8RuntimeAgentImplState::bindings,
protocol::DictionaryValue::create());
}
protocol::DictionaryValue* bindings =
m_state->getObject(V8RuntimeAgentImplState::bindings);
bindings->setBoolean(name, true); bindings->setBoolean(name, true);
m_inspector->forEachContext( m_inspector->forEachContext(
m_session->contextGroupId(), m_session->contextGroupId(),
...@@ -730,23 +730,22 @@ void V8RuntimeAgentImpl::addBinding(InspectedContext* context, ...@@ -730,23 +730,22 @@ void V8RuntimeAgentImpl::addBinding(InspectedContext* context,
.ToLocal(&functionValue)) { .ToLocal(&functionValue)) {
v8::Maybe<bool> success = global->Set(localContext, v8Name, functionValue); v8::Maybe<bool> success = global->Set(localContext, v8Name, functionValue);
USE(success); USE(success);
m_activeBindings.insert(name);
} }
} }
Response V8RuntimeAgentImpl::removeBinding(const String16& name) { Response V8RuntimeAgentImpl::removeBinding(const String16& name) {
protocol::DictionaryValue* bindings = protocol::DictionaryValue* bindings =
m_state->getObject(V8RuntimeAgentImplState::bindings); m_state->getObject(V8RuntimeAgentImplState::bindings);
if (!bindings) return Response::Success(); if (bindings) bindings->remove(name);
bindings->remove(name); m_activeBindings.erase(name);
return Response::Success(); return Response::Success();
} }
void V8RuntimeAgentImpl::bindingCalled(const String16& name, void V8RuntimeAgentImpl::bindingCalled(const String16& name,
const String16& payload, const String16& payload,
int executionContextId) { int executionContextId) {
protocol::DictionaryValue* bindings = if (!m_activeBindings.count(name)) return;
m_state->getObject(V8RuntimeAgentImplState::bindings);
if (!bindings || !bindings->get(name)) return;
m_frontend.bindingCalled(name, payload, executionContextId); m_frontend.bindingCalled(name, payload, executionContextId);
} }
...@@ -755,10 +754,8 @@ void V8RuntimeAgentImpl::addBindings(InspectedContext* context) { ...@@ -755,10 +754,8 @@ void V8RuntimeAgentImpl::addBindings(InspectedContext* context) {
protocol::DictionaryValue* bindings = protocol::DictionaryValue* bindings =
m_state->getObject(V8RuntimeAgentImplState::bindings); m_state->getObject(V8RuntimeAgentImplState::bindings);
if (!bindings) return; if (!bindings) return;
for (size_t i = 0; i < bindings->size(); ++i) { for (size_t i = 0; i < bindings->size(); ++i)
if (!bindings->at(i).second) continue;
addBinding(context, bindings->at(i).first); addBinding(context, bindings->at(i).first);
}
} }
void V8RuntimeAgentImpl::restore() { void V8RuntimeAgentImpl::restore() {
......
...@@ -32,14 +32,14 @@ ...@@ -32,14 +32,14 @@
#define V8_INSPECTOR_V8_RUNTIME_AGENT_IMPL_H_ #define V8_INSPECTOR_V8_RUNTIME_AGENT_IMPL_H_
#include <memory> #include <memory>
#include <set>
#include <unordered_map> #include <unordered_map>
#include "include/v8.h"
#include "src/base/macros.h" #include "src/base/macros.h"
#include "src/inspector/protocol/Forward.h" #include "src/inspector/protocol/Forward.h"
#include "src/inspector/protocol/Runtime.h" #include "src/inspector/protocol/Runtime.h"
#include "include/v8.h"
namespace v8_inspector { namespace v8_inspector {
class InjectedScript; class InjectedScript;
...@@ -145,6 +145,7 @@ class V8RuntimeAgentImpl : public protocol::Runtime::Backend { ...@@ -145,6 +145,7 @@ class V8RuntimeAgentImpl : public protocol::Runtime::Backend {
bool m_enabled; bool m_enabled;
std::unordered_map<String16, std::unique_ptr<v8::Global<v8::Script>>> std::unordered_map<String16, std::unique_ptr<v8::Global<v8::Script>>>
m_compiledScripts; m_compiledScripts;
std::set<String16> m_activeBindings;
DISALLOW_COPY_AND_ASSIGN(V8RuntimeAgentImpl); DISALLOW_COPY_AND_ASSIGN(V8RuntimeAgentImpl);
}; };
......
...@@ -148,7 +148,7 @@ class FrontendChannelImpl : public v8_inspector::V8Inspector::Channel { ...@@ -148,7 +148,7 @@ class FrontendChannelImpl : public v8_inspector::V8Inspector::Channel {
v8::MicrotasksScope::kRunMicrotasks); v8::MicrotasksScope::kRunMicrotasks);
v8::HandleScope handle_scope(data->isolate()); v8::HandleScope handle_scope(data->isolate());
v8::Local<v8::Context> context = v8::Local<v8::Context> context =
data->GetContext(channel_->context_group_id_); data->GetDefaultContext(channel_->context_group_id_);
v8::Context::Scope context_scope(context); v8::Context::Scope context_scope(context);
v8::Local<v8::Value> message = ToV8String(data->isolate(), message_); v8::Local<v8::Value> message = ToV8String(data->isolate(), message_);
v8::MaybeLocal<v8::Value> result; v8::MaybeLocal<v8::Value> result;
...@@ -252,7 +252,7 @@ class ExecuteStringTask : public TaskRunner::Task { ...@@ -252,7 +252,7 @@ class ExecuteStringTask : public TaskRunner::Task {
v8::MicrotasksScope microtasks_scope(data->isolate(), v8::MicrotasksScope microtasks_scope(data->isolate(),
v8::MicrotasksScope::kRunMicrotasks); v8::MicrotasksScope::kRunMicrotasks);
v8::HandleScope handle_scope(data->isolate()); v8::HandleScope handle_scope(data->isolate());
v8::Local<v8::Context> context = data->GetContext(context_group_id_); v8::Local<v8::Context> context = data->GetDefaultContext(context_group_id_);
v8::Context::Scope context_scope(context); v8::Context::Scope context_scope(context);
v8::ScriptOrigin origin( v8::ScriptOrigin origin(
ToV8String(data->isolate(), name_), ToV8String(data->isolate(), name_),
...@@ -344,6 +344,9 @@ class UtilsExtension : public IsolateData::SetupGlobalTask { ...@@ -344,6 +344,9 @@ class UtilsExtension : public IsolateData::SetupGlobalTask {
utils->Set(isolate, "createContextGroup", utils->Set(isolate, "createContextGroup",
v8::FunctionTemplate::New(isolate, v8::FunctionTemplate::New(isolate,
&UtilsExtension::CreateContextGroup)); &UtilsExtension::CreateContextGroup));
utils->Set(
isolate, "createContext",
v8::FunctionTemplate::New(isolate, &UtilsExtension::CreateContext));
utils->Set( utils->Set(
isolate, "resetContextGroup", isolate, "resetContextGroup",
v8::FunctionTemplate::New(isolate, &UtilsExtension::ResetContextGroup)); v8::FunctionTemplate::New(isolate, &UtilsExtension::ResetContextGroup));
...@@ -576,6 +579,21 @@ class UtilsExtension : public IsolateData::SetupGlobalTask { ...@@ -576,6 +579,21 @@ class UtilsExtension : public IsolateData::SetupGlobalTask {
v8::Int32::New(args.GetIsolate(), context_group_id)); v8::Int32::New(args.GetIsolate(), context_group_id));
} }
static void CreateContext(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 2) {
fprintf(stderr, "Internal error: createContext(context, name).");
Exit();
}
int context_group_id = args[0].As<v8::Int32>()->Value();
std::vector<uint16_t> name =
ToVector(args.GetIsolate(), args[1].As<v8::String>());
RunSyncTask(backend_runner_, [&context_group_id, name](IsolateData* data) {
data->CreateContext(context_group_id,
v8_inspector::StringView(name.data(), name.size()));
});
}
static void ResetContextGroup( static void ResetContextGroup(
const v8::FunctionCallbackInfo<v8::Value>& args) { const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 1 || !args[0]->IsInt32()) { if (args.Length() != 1 || !args[0]->IsInt32()) {
...@@ -664,7 +682,7 @@ class SetTimeoutTask : public TaskRunner::Task { ...@@ -664,7 +682,7 @@ class SetTimeoutTask : public TaskRunner::Task {
v8::MicrotasksScope microtasks_scope(data->isolate(), v8::MicrotasksScope microtasks_scope(data->isolate(),
v8::MicrotasksScope::kRunMicrotasks); v8::MicrotasksScope::kRunMicrotasks);
v8::HandleScope handle_scope(data->isolate()); v8::HandleScope handle_scope(data->isolate());
v8::Local<v8::Context> context = data->GetContext(context_group_id_); v8::Local<v8::Context> context = data->GetDefaultContext(context_group_id_);
v8::Context::Scope context_scope(context); v8::Context::Scope context_scope(context);
v8::Local<v8::Function> callback = callback_.Get(data->isolate()); v8::Local<v8::Function> callback = callback_.Get(data->isolate());
...@@ -798,7 +816,8 @@ class InspectorExtension : public IsolateData::SetupGlobalTask { ...@@ -798,7 +816,8 @@ class InspectorExtension : public IsolateData::SetupGlobalTask {
const v8::FunctionCallbackInfo<v8::Value>& args) { const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext(); v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
IsolateData* data = IsolateData::FromContext(context); IsolateData* data = IsolateData::FromContext(context);
data->FireContextCreated(context, data->GetContextGroupId(context)); data->FireContextCreated(context, data->GetContextGroupId(context),
v8_inspector::StringView());
} }
static void FireContextDestroyed( static void FireContextDestroyed(
......
...@@ -87,6 +87,13 @@ IsolateData* IsolateData::FromContext(v8::Local<v8::Context> context) { ...@@ -87,6 +87,13 @@ IsolateData* IsolateData::FromContext(v8::Local<v8::Context> context) {
} }
int IsolateData::CreateContextGroup() { int IsolateData::CreateContextGroup() {
int context_group_id = ++last_context_group_id_;
CreateContext(context_group_id, v8_inspector::StringView());
return context_group_id;
}
void IsolateData::CreateContext(int context_group_id,
v8_inspector::StringView name) {
v8::HandleScope handle_scope(isolate_.get()); v8::HandleScope handle_scope(isolate_.get());
v8::Local<v8::ObjectTemplate> global_template = v8::Local<v8::ObjectTemplate> global_template =
v8::ObjectTemplate::New(isolate_.get()); v8::ObjectTemplate::New(isolate_.get());
...@@ -97,17 +104,15 @@ int IsolateData::CreateContextGroup() { ...@@ -97,17 +104,15 @@ int IsolateData::CreateContextGroup() {
v8::Local<v8::Context> context = v8::Local<v8::Context> context =
v8::Context::New(isolate_.get(), nullptr, global_template); v8::Context::New(isolate_.get(), nullptr, global_template);
context->SetAlignedPointerInEmbedderData(kIsolateDataIndex, this); context->SetAlignedPointerInEmbedderData(kIsolateDataIndex, this);
int context_group_id = ++last_context_group_id_;
// Should be 2-byte aligned. // Should be 2-byte aligned.
context->SetAlignedPointerInEmbedderData( context->SetAlignedPointerInEmbedderData(
kContextGroupIdIndex, reinterpret_cast<void*>(context_group_id * 2)); kContextGroupIdIndex, reinterpret_cast<void*>(context_group_id * 2));
contexts_[context_group_id].Reset(isolate_.get(), context); contexts_[context_group_id].emplace_back(isolate_.get(), context);
if (inspector_) FireContextCreated(context, context_group_id); if (inspector_) FireContextCreated(context, context_group_id, name);
return context_group_id;
} }
v8::Local<v8::Context> IsolateData::GetContext(int context_group_id) { v8::Local<v8::Context> IsolateData::GetDefaultContext(int context_group_id) {
return contexts_[context_group_id].Get(isolate_.get()); return contexts_[context_group_id].begin()->Get(isolate_.get());
} }
void IsolateData::ResetContextGroup(int context_group_id) { void IsolateData::ResetContextGroup(int context_group_id) {
...@@ -338,9 +343,9 @@ void IsolateData::PromiseRejectHandler(v8::PromiseRejectMessage data) { ...@@ -338,9 +343,9 @@ void IsolateData::PromiseRejectHandler(v8::PromiseRejectMessage data) {
} }
void IsolateData::FireContextCreated(v8::Local<v8::Context> context, void IsolateData::FireContextCreated(v8::Local<v8::Context> context,
int context_group_id) { int context_group_id,
v8_inspector::V8ContextInfo info(context, context_group_id, v8_inspector::StringView name) {
v8_inspector::StringView()); v8_inspector::V8ContextInfo info(context, context_group_id, name);
info.hasMemoryOnConsole = true; info.hasMemoryOnConsole = true;
v8::SealHandleScope seal_handle_scope(isolate()); v8::SealHandleScope seal_handle_scope(isolate());
inspector_->contextCreated(info); inspector_->contextCreated(info);
...@@ -388,7 +393,7 @@ bool IsolateData::isInspectableHeapObject(v8::Local<v8::Object> object) { ...@@ -388,7 +393,7 @@ bool IsolateData::isInspectableHeapObject(v8::Local<v8::Object> object) {
v8::Local<v8::Context> IsolateData::ensureDefaultContextInGroup( v8::Local<v8::Context> IsolateData::ensureDefaultContextInGroup(
int context_group_id) { int context_group_id) {
return GetContext(context_group_id); return GetDefaultContext(context_group_id);
} }
void IsolateData::SetCurrentTimeMS(double time) { void IsolateData::SetCurrentTimeMS(double time) {
......
...@@ -36,8 +36,9 @@ class IsolateData : public v8_inspector::V8InspectorClient { ...@@ -36,8 +36,9 @@ class IsolateData : public v8_inspector::V8InspectorClient {
// Setting things up. // Setting things up.
int CreateContextGroup(); int CreateContextGroup();
void CreateContext(int context_group_id, v8_inspector::StringView name);
void ResetContextGroup(int context_group_id); void ResetContextGroup(int context_group_id);
v8::Local<v8::Context> GetContext(int context_group_id); v8::Local<v8::Context> GetDefaultContext(int context_group_id);
int GetContextGroupId(v8::Local<v8::Context> context); int GetContextGroupId(v8::Local<v8::Context> context);
void RegisterModule(v8::Local<v8::Context> context, void RegisterModule(v8::Local<v8::Context> context,
v8::internal::Vector<uint16_t> name, v8::internal::Vector<uint16_t> name,
...@@ -76,7 +77,8 @@ class IsolateData : public v8_inspector::V8InspectorClient { ...@@ -76,7 +77,8 @@ class IsolateData : public v8_inspector::V8InspectorClient {
void SetAdditionalConsoleApi(v8_inspector::StringView api_script); void SetAdditionalConsoleApi(v8_inspector::StringView api_script);
void SetMaxAsyncTaskStacksForTest(int limit); void SetMaxAsyncTaskStacksForTest(int limit);
void DumpAsyncTaskStacksStateForTest(); void DumpAsyncTaskStacksStateForTest();
void FireContextCreated(v8::Local<v8::Context> context, int context_group_id); void FireContextCreated(v8::Local<v8::Context> context, int context_group_id,
v8_inspector::StringView name);
void FireContextDestroyed(v8::Local<v8::Context> context); void FireContextDestroyed(v8::Local<v8::Context> context);
void FreeContext(v8::Local<v8::Context> context); void FreeContext(v8::Local<v8::Context> context);
void SetResourceNamePrefix(v8::Local<v8::String> prefix); void SetResourceNamePrefix(v8::Local<v8::String> prefix);
...@@ -137,7 +139,7 @@ class IsolateData : public v8_inspector::V8InspectorClient { ...@@ -137,7 +139,7 @@ class IsolateData : public v8_inspector::V8InspectorClient {
std::unique_ptr<v8::Isolate, IsolateDeleter> isolate_; std::unique_ptr<v8::Isolate, IsolateDeleter> isolate_;
std::unique_ptr<v8_inspector::V8Inspector> inspector_; std::unique_ptr<v8_inspector::V8Inspector> inspector_;
int last_context_group_id_ = 0; int last_context_group_id_ = 0;
std::map<int, v8::Global<v8::Context>> contexts_; std::map<int, std::vector<v8::Global<v8::Context>>> contexts_;
std::map<v8::internal::Vector<uint16_t>, v8::Global<v8::Module>, std::map<v8::internal::Vector<uint16_t>, v8::Global<v8::Module>,
VectorCompare> VectorCompare>
modules_; modules_;
......
...@@ -142,6 +142,10 @@ InspectorTest.ContextGroup = class { ...@@ -142,6 +142,10 @@ InspectorTest.ContextGroup = class {
this.id = utils.createContextGroup(); this.id = utils.createContextGroup();
} }
createContext(name) {
utils.createContext(this.id, name || '');
}
schedulePauseOnNextStatement(reason, details) { schedulePauseOnNextStatement(reason, details) {
utils.schedulePauseOnNextStatement(this.id, reason, details); utils.schedulePauseOnNextStatement(this.id, reason, details);
} }
......
...@@ -37,6 +37,15 @@ binding called in session2 ...@@ -37,6 +37,15 @@ binding called in session2
Disable agent inside session1.. Disable agent inside session1..
Call binding.. Call binding..
binding called in session1
{
method : Runtime.bindingCalled
params : {
executionContextId : <executionContextId>
name : send
payload : payload
}
}
binding called in session2 binding called in session2
{ {
method : Runtime.bindingCalled method : Runtime.bindingCalled
...@@ -49,9 +58,45 @@ binding called in session2 ...@@ -49,9 +58,45 @@ binding called in session2
Disable agent inside session2.. Disable agent inside session2..
Call binding.. Call binding..
binding called in session1
{
method : Runtime.bindingCalled
params : {
executionContextId : <executionContextId>
name : send
payload : payload
}
}
binding called in session2
{
method : Runtime.bindingCalled
params : {
executionContextId : <executionContextId>
name : send
payload : payload
}
}
Enable agent inside session1.. Enable agent inside session1..
Call binding.. Call binding..
binding called in session1
{
method : Runtime.bindingCalled
params : {
executionContextId : <executionContextId>
name : send
payload : payload
}
}
binding called in session2
{
method : Runtime.bindingCalled
params : {
executionContextId : <executionContextId>
name : send
payload : payload
}
}
Running test: testReconnect Running test: testReconnect
...@@ -97,3 +142,17 @@ binding called in session1 ...@@ -97,3 +142,17 @@ binding called in session1
} }
Remove binding inside session.. Remove binding inside session..
Call binding.. Call binding..
Running test: testAddBindingToContextById
Call binding in default context (binding should NOT be exposed)
Call binding in target context (binding should be exposed)
binding called in session1
{
method : Runtime.bindingCalled
params : {
executionContextId : <executionContextId>
name : frobnicate
payload : message
}
}
Call binding in newly created context (binding should NOT be exposed)
...@@ -63,6 +63,28 @@ InspectorTest.runAsyncTestSuite([ ...@@ -63,6 +63,28 @@ InspectorTest.runAsyncTestSuite([
session.Protocol.Runtime.removeBinding({name: 'send'}); session.Protocol.Runtime.removeBinding({name: 'send'});
InspectorTest.log('Call binding..'); InspectorTest.log('Call binding..');
await session.Protocol.Runtime.evaluate({expression: `send('payload')`}); await session.Protocol.Runtime.evaluate({expression: `send('payload')`});
},
async function testAddBindingToContextById() {
const {contextGroup, sessions: [session]} = setupSessions(1);
const contextId1 = (await session.Protocol.Runtime.onceExecutionContextCreated()).params.context.id;
contextGroup.createContext();
const contextId2 = (await session.Protocol.Runtime.onceExecutionContextCreated()).params.context.id;
await session.Protocol.Runtime.addBinding({name: 'frobnicate', executionContextId: contextId2});
const expression = `frobnicate('message')`;
InspectorTest.log('Call binding in default context (binding should NOT be exposed)');
await session.Protocol.Runtime.evaluate({expression});
InspectorTest.log('Call binding in target context (binding should be exposed)');
await session.Protocol.Runtime.evaluate({expression, contextId: contextId2});
InspectorTest.log('Call binding in newly created context (binding should NOT be exposed)');
contextGroup.createContext();
const contextId3 = (await session.Protocol.Runtime.onceExecutionContextCreated()).params.context.id;
await session.Protocol.Runtime.evaluate({expression, contextId: contextId3});
} }
]); ]);
......
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