Bug 1246091 - patch 7/7 - Correct use of JSCompartment in Console.cpp, r=bz

This commit is contained in:
Andrea Marchesini 2016-03-23 22:55:07 +01:00
Родитель 934bd500b6
Коммит dffe78c29b
2 изменённых файлов: 80 добавлений и 69 удалений

Просмотреть файл

@ -117,6 +117,8 @@ public:
mMethodName = aName;
mMethodString = aString;
mGlobal = JS::CurrentGlobalOrNull(aCx);
for (uint32_t i = 0; i < aArguments.Length(); ++i) {
if (NS_WARN_IF(!mCopiedArguments.AppendElement(aArguments[i]))) {
aConsole->UnstoreCallData(this);
@ -171,6 +173,8 @@ public:
for (uint32_t i = 0; i < mCopiedArguments.Length(); ++i) {
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCopiedArguments[i])
}
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal);
}
void
@ -180,6 +184,8 @@ public:
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
}
JS::Heap<JSObject*> mGlobal;
// This is a copy of the arguments we received from the DOM bindings. Console
// object traces them because this ConsoleCallData calls
// RegisterConsoleCallData() in the Initialize().
@ -317,9 +323,9 @@ public:
}
bool
Dispatch(JS::Handle<JSObject*> aGlobal)
Dispatch(JSContext* aCx)
{
if (!DispatchInternal(aGlobal)) {
if (!DispatchInternal(aCx)) {
ReleaseData();
return false;
}
@ -358,13 +364,11 @@ private:
}
bool
DispatchInternal(JS::Handle<JSObject*> aGlobal)
DispatchInternal(JSContext* aCx)
{
mWorkerPrivate->AssertIsOnWorkerThread();
JSContext* cx = mWorkerPrivate->GetJSContext();
if (NS_WARN_IF(!PreDispatch(cx, aGlobal))) {
if (NS_WARN_IF(!PreDispatch(aCx))) {
return false;
}
@ -471,7 +475,7 @@ private:
protected:
// This method is called in the owning thread of the Console object.
virtual bool
PreDispatch(JSContext* aCx, JS::Handle<JSObject*> aGlobal) = 0;
PreDispatch(JSContext* aCx) = 0;
// This method is called in the main-thread.
virtual void
@ -567,13 +571,12 @@ private:
}
bool
PreDispatch(JSContext* aCx, JS::Handle<JSObject*> aGlobal) override
PreDispatch(JSContext* aCx) override
{
mWorkerPrivate->AssertIsOnWorkerThread();
mCallData->AssertIsOnOwningThread();
ClearException ce(aCx);
JSAutoCompartment ac(aCx, aGlobal);
JS::Rooted<JSObject*> arguments(aCx,
JS_NewArrayObject(aCx, mCallData->mCopiedArguments.Length()));
@ -694,9 +697,7 @@ private:
MOZ_ASSERT(values.Length() == length);
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
mConsole->ProcessCallData(mCallData, global, values);
mConsole->ProcessCallData(aCx, mCallData, values);
}
RefPtr<ConsoleCallData> mCallData;
@ -717,17 +718,10 @@ public:
private:
bool
PreDispatch(JSContext* aCx, JS::Handle<JSObject*> aGlobal) override
PreDispatch(JSContext* aCx) override
{
ClearException ce(aCx);
JS::Rooted<JSObject*> global(aCx, aGlobal);
if (NS_WARN_IF(!global)) {
return false;
}
JSAutoCompartment ac(aCx, global);
JS::Rooted<JSObject*> arguments(aCx,
JS_NewArrayObject(aCx, mArguments.Length()));
if (NS_WARN_IF(!arguments)) {
@ -1101,7 +1095,7 @@ Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
new ConsoleProfileRunnable(this, aAction, aData);
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
runnable->Dispatch(global);
runnable->Dispatch(aCx);
return;
}
@ -1373,11 +1367,9 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
callData->mCountLabel);
}
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
if (NS_IsMainThread()) {
callData->SetIDs(mOuterID, mInnerID);
ProcessCallData(callData, global, aData);
ProcessCallData(aCx, callData, aData);
// Just because we don't want to expose
// retrieveConsoleEvents/setConsoleEventHandler to main-thread, we can
@ -1387,11 +1379,11 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
}
// We do this only in workers for now.
NotifyHandler(aCx, global, aData, callData);
NotifyHandler(aCx, aData, callData);
RefPtr<ConsoleCallDataRunnable> runnable =
new ConsoleCallDataRunnable(this, callData);
NS_WARN_IF(!runnable->Dispatch(global));
NS_WARN_IF(!runnable->Dispatch(aCx));
}
// We store information to lazily compute the stack in the reserved slots of
@ -1440,20 +1432,29 @@ LazyStackGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
}
void
Console::ProcessCallData(ConsoleCallData* aData, JS::Handle<JSObject*> aGlobal,
Console::ProcessCallData(JSContext* aCx, ConsoleCallData* aData,
const Sequence<JS::Value>& aArguments)
{
AssertIsOnMainThread();
MOZ_ASSERT(aData);
AutoJSAPI jsapi;
if (!jsapi.Init(aGlobal)) {
return;
}
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> eventValue(aCx);
JS::Rooted<JS::Value> eventValue(cx);
if (NS_WARN_IF(!PopulateEvent(cx, aGlobal, aArguments, &eventValue, aData))) {
// We want to create a console event object and pass it to our
// nsIConsoleAPIStorage implementation. We want to define some accessor
// properties on this object, and those will need to keep an nsIStackFrame
// alive. But nsIStackFrame cannot be wrapped in an untrusted scope. And
// further, passing untrusted objects to system code is likely to run afoul of
// Object Xrays. So we want to wrap in a system-principal scope here. But
// which one? We could cheat and try to get the underlying JSObject* of
// mStorage, but that's a bit fragile. Instead, we just use the junk scope,
// with explicit permission from the XPConnect module owner. If you're
// tempted to do that anywhere else, talk to said module owner first.
// aCx and aArguments are in the same compartment.
if (NS_WARN_IF(!PopulateConsoleObjectInTheTargetScope(aCx, aArguments,
xpc::PrivilegedJunkScope(),
&eventValue, aData))) {
return;
}
@ -1484,14 +1485,15 @@ Console::ProcessCallData(ConsoleCallData* aData, JS::Handle<JSObject*> aGlobal,
}
bool
Console::PopulateEvent(JSContext* aCx,
JS::Handle<JSObject*> aGlobal,
const Sequence<JS::Value>& aArguments,
JS::MutableHandle<JS::Value> aEventValue,
ConsoleCallData* aData) const
Console::PopulateConsoleObjectInTheTargetScope(JSContext* aCx,
const Sequence<JS::Value>& aArguments,
JSObject* aTargetScope,
JS::MutableHandle<JS::Value> aEventValue,
ConsoleCallData* aData) const
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(aData);
MOZ_ASSERT(aTargetScope);
ConsoleStackEntry frame;
if (aData->mTopStackFrame) {
@ -1501,8 +1503,6 @@ Console::PopulateEvent(JSContext* aCx,
ClearException ce(aCx);
RootedDictionary<ConsoleEvent> event(aCx);
JSAutoCompartment ac(aCx, aGlobal);
event.mID.Construct();
event.mInnerID.Construct();
@ -1590,17 +1590,7 @@ Console::PopulateEvent(JSContext* aCx,
aData->mCountValue);
}
// We want to create a console event object and pass it to our
// nsIConsoleAPIStorage implementation. We want to define some accessor
// properties on this object, and those will need to keep an nsIStackFrame
// alive. But nsIStackFrame cannot be wrapped in an untrusted scope. And
// further, passing untrusted objects to system code is likely to run afoul of
// Object Xrays. So we want to wrap in a system-principal scope here. But
// which one? We could cheat and try to get the underlying JSObject* of
// mStorage, but that's a bit fragile. Instead, we just use the junk scope,
// with explicit permission from the XPConnect module owner. If you're
// tempted to do that anywhere else, talk to said module owner first.
JSAutoCompartment ac2(aCx, xpc::PrivilegedJunkScope());
JSAutoCompartment ac2(aCx, aTargetScope);
if (NS_WARN_IF(!ToJSValue(aCx, event, aEventValue))) {
return false;
@ -2262,8 +2252,7 @@ Console::ReleaseCallData(ConsoleCallData* aCallData)
}
void
Console::NotifyHandler(JSContext* aCx, JS::Handle<JSObject*> aGlobal,
const Sequence<JS::Value>& aArguments,
Console::NotifyHandler(JSContext* aCx, const Sequence<JS::Value>& aArguments,
ConsoleCallData* aCallData) const
{
AssertIsOnOwningThread();
@ -2274,11 +2263,14 @@ Console::NotifyHandler(JSContext* aCx, JS::Handle<JSObject*> aGlobal,
return;
}
JSAutoCompartment ac(aCx, mConsoleEventNotifier->Callable());
JS::Rooted<JS::Value> value(aCx);
if (NS_WARN_IF(!PopulateEvent(aCx, mConsoleEventNotifier->Callable(),
aArguments, &value, aCallData))) {
// aCx and aArguments are in the same compartment because this method is
// called directly when a Console.something() runs.
// mConsoleEventNotifier->Callable() is the scope where value will be sent to.
if (NS_WARN_IF(!PopulateConsoleObjectInTheTargetScope(aCx, aArguments,
mConsoleEventNotifier->Callable(),
&value, aCallData))) {
return;
}
@ -2295,11 +2287,14 @@ Console::RetrieveConsoleEvents(JSContext* aCx, nsTArray<JS::Value>& aEvents,
// We don't want to expose this functionality to main-thread yet.
MOZ_ASSERT(!NS_IsMainThread());
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
JS::Rooted<JSObject*> targetScope(aCx, JS::CurrentGlobalOrNull(aCx));
for (uint32_t i = 0; i < mCallDataStorage.Length(); ++i) {
JS::Rooted<JS::Value> value(aCx);
JS::Rooted<JSObject*> sequenceScope(aCx, mCallDataStorage[i]->mGlobal);
JSAutoCompartment ac(aCx, sequenceScope);
Sequence<JS::Value> sequence;
SequenceRooter<JS::Value> arguments(aCx, &sequence);
@ -2308,8 +2303,12 @@ Console::RetrieveConsoleEvents(JSContext* aCx, nsTArray<JS::Value>& aEvents,
return;
}
if (NS_WARN_IF(!PopulateEvent(aCx, global, sequence, &value,
mCallDataStorage[i]))) {
// Here we have aCx and sequence in the same compartment.
// targetScope is the destination scope and value will be populated in its
// compartment.
if (NS_WARN_IF(!PopulateConsoleObjectInTheTargetScope(aCx, sequence,
targetScope, &value,
mCallDataStorage[i]))) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}

Просмотреть файл

@ -163,9 +163,10 @@ private:
Method(JSContext* aCx, MethodName aName, const nsAString& aString,
const Sequence<JS::Value>& aData);
// This method must receive aCx and aArguments in the same JSCompartment.
void
ProcessCallData(ConsoleCallData* aData,
JS::Handle<JSObject*> aGlobal,
ProcessCallData(JSContext* aCx,
ConsoleCallData* aData,
const Sequence<JS::Value>& aArguments);
void
@ -178,18 +179,29 @@ private:
void
ReleaseCallData(ConsoleCallData* aCallData);
// aCx and aArguments must be in the same JS compartment.
void
NotifyHandler(JSContext* aCx,
JS::Handle<JSObject*> aGlobal,
const Sequence<JS::Value>& aArguments,
ConsoleCallData* aData) const;
// PopulateConsoleObjectInTheTargetScope receives aCx and aArguments in the
// same JS compartment and populates the ConsoleEvent object (aValue) in the
// aTargetScope.
// aTargetScope can be:
// - the system-principal scope when we want to dispatch the ConsoleEvent to
// nsIConsoleAPIStorage (See the comment in Console.cpp about the use of
// xpc::PrivilegedJunkScope()
// - the mConsoleEventNotifier->Callable() scope when we want to notify this
// handler about a new ConsoleEvent.
// - It can be the global from the JSContext when RetrieveConsoleEvents is
// called.
bool
PopulateEvent(JSContext* aCx,
JS::Handle<JSObject*> aGlobal,
const Sequence<JS::Value>& aArguments,
JS::MutableHandle<JS::Value> aValue,
ConsoleCallData* aData) const;
PopulateConsoleObjectInTheTargetScope(JSContext* aCx,
const Sequence<JS::Value>& aArguments,
JSObject* aTargetScope,
JS::MutableHandle<JS::Value> aValue,
ConsoleCallData* aData) const;
// If the first JS::Value of the array is a string, this method uses it to
// format a string. The supported sequences are: