Bug 1052089 - Swap out the SafeJSContextGlobal for the new UnprivilegedJunkScope. r=billm
authorBobby Holley <bobbyholley@gmail.com>
Mon, 18 Aug 2014 10:57:30 -0700
changeset 221809 621470d025e717313e6e0645258cd0330694c779
parent 221808 2a2883202fff196ab1f139d83fee51172b6893ec
child 221810 0380ce352a89541b487577cf925ea31a725e7095
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1052089
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1052089 - Swap out the SafeJSContextGlobal for the new UnprivilegedJunkScope. r=billm
dom/base/ScriptSettings.cpp
dom/base/nsJSUtils.cpp
dom/bindings/Codegen.py
js/xpconnect/src/XPCJSContextStack.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCWrappedNativeScope.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/src/xpcpublic.h
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -494,17 +494,17 @@ ThreadsafeAutoJSContext::operator JSCont
     return mCx;
   } else {
     return *mAutoJSContext;
   }
 }
 
 AutoSafeJSContext::AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
   : AutoJSContext(true MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
-  , mAc(mCx, XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContextGlobal())
+  , mAc(mCx, xpc::UnprivilegedJunkScope())
 {
 }
 
 ThreadsafeAutoSafeJSContext::ThreadsafeAutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
 {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
   if (NS_IsMainThread()) {
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -119,17 +119,17 @@ nsJSUtils::ReportPendingException(JSCont
       nsIScriptContext* scx = GetScriptContextFromJSContext(aContext);
       JS::Rooted<JSObject*> scope(aContext);
       scope = scx ? scx->GetWindowProxy()
                   : js::DefaultObjectForContextOrNull(aContext);
       if (!scope) {
         // The SafeJSContext has no default object associated with it.
         MOZ_ASSERT(NS_IsMainThread());
         MOZ_ASSERT(aContext == nsContentUtils::GetSafeJSContext());
-        scope = xpc::GetSafeJSContextGlobal();
+        scope = xpc::UnprivilegedJunkScope(); // Usage approved by bholley
       }
       JSAutoCompartment ac(aContext, scope);
       JS_ReportPendingException(aContext);
     }
     if (saved) {
       JS_RestoreFrameChain(aContext);
     }
   }
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -11099,17 +11099,17 @@ class CGDictionary(CGThing):
         return ClassMethod(
             "ToJSON", "bool",
             [Argument('nsAString&', 'aJSON')],
             body=dedent("""
                 MOZ_ASSERT(NS_IsMainThread());
                 AutoJSAPI jsapi;
                 jsapi.Init();
                 JSContext *cx = jsapi.cx();
-                JSAutoCompartment ac(cx, xpc::GetSafeJSContextGlobal()); // Usage approved by bholley
+                JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope()); // Usage approved by bholley
                 JS::Rooted<JS::Value> obj(cx);
                 return ToObjectInternal(cx, &obj) && StringifyToJSON(cx, &obj, aJSON);
             """))
 
     def toObjectInternalMethod(self):
         body = ""
         if self.needToInitIds:
             body += fill(
@@ -11887,17 +11887,17 @@ class CGBindingRoot(CGThing):
         bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode
         bindingHeaders["mozilla/dom/OwningNonNull.h"] = hasCode
         bindingHeaders["mozilla/dom/BindingDeclarations.h"] = (
             not hasCode and enums)
 
         bindingHeaders["WrapperFactory.h"] = descriptors
         bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors
         bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries # AutoJSAPI
-        bindingHeaders["xpcpublic.h"] = dictionaries ## xpc::GetSafeJSContextGlobal
+        bindingHeaders["xpcpublic.h"] = dictionaries ## xpc::UnprivilegedJunkScope
 
         # Do codegen for all the dictionaries.  We have to be a bit careful
         # here, because we have to generate these in order from least derived
         # to most derived so that class inheritance works out.  We also have to
         # generate members before the dictionary that contains them.
         cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False))
                          for d in
                          dependencySortObjects(dictionaries,
--- a/js/xpconnect/src/XPCJSContextStack.cpp
+++ b/js/xpconnect/src/XPCJSContextStack.cpp
@@ -17,17 +17,16 @@ using namespace JS;
 using namespace xpc;
 using mozilla::dom::DestroyProtoAndIfaceCache;
 
 /***************************************************************************/
 
 XPCJSContextStack::~XPCJSContextStack()
 {
     if (mSafeJSContext) {
-        mSafeJSContextGlobal = nullptr;
         JS_DestroyContextNoGC(mSafeJSContext);
         mSafeJSContext = nullptr;
     }
 }
 
 JSContext*
 XPCJSContextStack::Pop()
 {
@@ -104,92 +103,26 @@ bool
 XPCJSContextStack::HasJSContext(JSContext *cx)
 {
     for (uint32_t i = 0; i < mStack.Length(); i++)
         if (cx == mStack[i].cx)
             return true;
     return false;
 }
 
-static bool
-SafeGlobalResolve(JSContext *cx, HandleObject obj, HandleId id)
-{
-    bool resolved;
-    return JS_ResolveStandardClass(cx, obj, id, &resolved);
-}
-
-static void
-SafeFinalize(JSFreeOp *fop, JSObject* obj)
-{
-    SandboxPrivate* sop =
-        static_cast<SandboxPrivate*>(xpc_GetJSPrivate(obj));
-    sop->ForgetGlobalObject();
-    NS_IF_RELEASE(sop);
-    DestroyProtoAndIfaceCache(obj);
-}
-
-const JSClass xpc::SafeJSContextGlobalClass = {
-    "global_for_XPCJSContextStack_SafeJSContext",
-    XPCONNECT_GLOBAL_FLAGS,
-    JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-    JS_EnumerateStub, SafeGlobalResolve, JS_ConvertStub, SafeFinalize,
-    nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
-};
-
 JSContext*
 XPCJSContextStack::GetSafeJSContext()
 {
     MOZ_ASSERT(mSafeJSContext);
     return mSafeJSContext;
 }
 
-JSObject*
-XPCJSContextStack::GetSafeJSContextGlobal()
-{
-    MOZ_ASSERT(mSafeJSContextGlobal);
-    return mSafeJSContextGlobal;
-}
-
 JSContext*
 XPCJSContextStack::InitSafeJSContext()
 {
     MOZ_ASSERT(!mSafeJSContext);
-
-    // Start by getting the principal holder and principal for this
-    // context.  If we can't manage that, don't bother with the rest.
-    nsRefPtr<nsNullPrincipal> principal = new nsNullPrincipal();
-    nsresult rv = principal->Init();
-    if (NS_FAILED(rv))
-        MOZ_CRASH();
-
-    nsXPConnect* xpc = nsXPConnect::XPConnect();
-    JSRuntime *rt = xpc->GetRuntime()->Runtime();
-    if (!rt)
-        MOZ_CRASH();
-
-    mSafeJSContext = JS_NewContext(rt, 8192);
+    mSafeJSContext = JS_NewContext(XPCJSRuntime::Get()->Runtime(), 8192);
     if (!mSafeJSContext)
         MOZ_CRASH();
-    JSAutoRequest req(mSafeJSContext);
     ContextOptionsRef(mSafeJSContext).setNoDefaultCompartmentObject(true);
-
     JS_SetErrorReporter(mSafeJSContext, xpc::SystemErrorReporter);
-
-    // Note - We intentionally avoid firing OnNewGlobalObject while
-    // simultaneously skipping the call to setInvisibleToDebugger(true) here.
-    // This lets us piggy-back on the assertions in the JS engine (which make
-    // sure that, for non-invisible globals, we always fire onNewGlobalObject
-    // before creating scripts), to assert that we never create scripts with
-    // the SafeJSContextGlobal. This is all happening way before anyone could be
-    // listening for debugger notifications anyway.
-    JS::CompartmentOptions options;
-    options.setZone(JS::SystemZone)
-           .setTrace(TraceXPCGlobal);
-    mSafeJSContextGlobal = CreateGlobalObject(mSafeJSContext,
-                                              &SafeJSContextGlobalClass,
-                                              principal, options);
-    if (!mSafeJSContextGlobal)
-        MOZ_CRASH();
-
-    nsRefPtr<SandboxPrivate> sp = new SandboxPrivate(principal, mSafeJSContextGlobal);
-    JS_SetPrivate(mSafeJSContextGlobal, sp.forget().take());
     return mSafeJSContext;
 }
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -556,33 +556,33 @@ EnableUniversalXPConnect(JSContext *cx)
     XPCWrappedNativeScope *scope = priv->scope;
     if (!scope)
         return true;
     scope->ForcePrivilegedComponents();
     return scope->AttachComponentsObject(cx);
 }
 
 JSObject *
+UnprivilegedJunkScope()
+{
+    return XPCJSRuntime::Get()->UnprivilegedJunkScope();
+}
+
+JSObject *
 PrivilegedJunkScope()
 {
     return XPCJSRuntime::Get()->PrivilegedJunkScope();
 }
 
 JSObject *
 CompilationScope()
 {
     return XPCJSRuntime::Get()->CompilationScope();
 }
 
-JSObject *
-GetSafeJSContextGlobal()
-{
-    return XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContextGlobal();
-}
-
 nsGlobalWindow*
 WindowOrNull(JSObject *aObj)
 {
     MOZ_ASSERT(aObj);
     MOZ_ASSERT(!js::IsWrapper(aObj));
 
     // This will always return null until we have Window on WebIDL bindings,
     // at which point it will do the right thing.
@@ -3110,16 +3110,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
    mGCIsRunning(false),
    mWrappedJSToReleaseArray(),
    mNativesToReleaseArray(),
    mDoingFinalization(false),
    mVariantRoots(nullptr),
    mWrappedJSRoots(nullptr),
    mObjectHolderRoots(nullptr),
    mWatchdogManager(new WatchdogManager(MOZ_THIS_IN_INITIALIZER_LIST())),
+   mUnprivilegedJunkScope(MOZ_THIS_IN_INITIALIZER_LIST()->Runtime(), nullptr),
    mPrivilegedJunkScope(MOZ_THIS_IN_INITIALIZER_LIST()->Runtime(), nullptr),
    mCompilationScope(MOZ_THIS_IN_INITIALIZER_LIST()->Runtime(), nullptr),
    mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite())
 {
     DOM_InitInterfaces();
 
     // these jsids filled in later when we have a JSContext to work with.
     mStrIDs[0] = JSID_VOID;
@@ -3540,17 +3541,25 @@ void
 XPCJSRuntime::InitSingletonScopes()
 {
     // This all happens very early, so we don't bother with cx pushing.
     JSContext *cx = GetJSContextStack()->GetSafeJSContext();
     JSAutoRequest ar(cx);
     RootedValue v(cx);
     nsresult rv;
 
-    // Create the Junk Scope.
+    // Create the Unprivileged Junk Scope.
+    SandboxOptions unprivilegedJunkScopeOptions;
+    unprivilegedJunkScopeOptions.sandboxName.AssignLiteral("XPConnect Junk Compartment");
+    unprivilegedJunkScopeOptions.invisibleToDebugger = true;
+    rv = CreateSandboxObject(cx, &v, nullptr, unprivilegedJunkScopeOptions);
+    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+    mUnprivilegedJunkScope = js::UncheckedUnwrap(&v.toObject());
+
+    // Create the Privileged Junk Scope.
     SandboxOptions privilegedJunkScopeOptions;
     privilegedJunkScopeOptions.sandboxName.AssignLiteral("XPConnect Privileged Junk Compartment");
     privilegedJunkScopeOptions.invisibleToDebugger = true;
     privilegedJunkScopeOptions.wantComponents = false;
     rv = CreateSandboxObject(cx, &v, nsXPConnect::SystemPrincipal(), privilegedJunkScopeOptions);
     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
     mPrivilegedJunkScope = js::UncheckedUnwrap(&v.toObject());
 
@@ -3562,11 +3571,12 @@ XPCJSRuntime::InitSingletonScopes()
     rv = CreateSandboxObject(cx, &v, /* principal = */ nullptr, compilationScopeOptions);
     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
     mCompilationScope = js::UncheckedUnwrap(&v.toObject());
 }
 
 void
 XPCJSRuntime::DeleteSingletonScopes()
 {
+    mUnprivilegedJunkScope = nullptr;
     mPrivilegedJunkScope = nullptr;
     mCompilationScope = nullptr;
 }
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -47,25 +47,21 @@ XPCWrappedNativeScope::ClearInterpositio
     return NS_OK;
 }
 
 static bool
 RemoteXULForbidsXBLScope(nsIPrincipal *aPrincipal, HandleObject aGlobal)
 {
   MOZ_ASSERT(aPrincipal);
 
-  // The SafeJSContext is lazily created, and tends to be created at really
-  // weird times, at least for xpcshell (often very early in startup or late
-  // in shutdown). Its scope isn't system principal, so if we proceeded we'd
-  // end up calling into AllowXULXBLForPrincipal, which depends on all kinds
-  // of persistent storage and permission machinery that may or not be running.
-  // We know the answer to the question here, so just short-circuit.
-  //
-  // We do the same for sandboxes, for similar reasons.
-  if (JS_GetClass(aGlobal) == &SafeJSContextGlobalClass || IsSandbox(aGlobal))
+  // Certain singleton sandoxes are created very early in startup - too early
+  // to call into AllowXULXBLForPrincipal. We never create XBL scopes for
+  // sandboxes anway, and certainly not for these singleton scopes. So we just
+  // short-circuit here.
+  if (IsSandbox(aGlobal))
       return false;
 
   // AllowXULXBLForPrincipal will return true for system principal, but we
   // don't want that here.
   MOZ_ASSERT(nsContentUtils::IsInitialized());
   if (nsContentUtils::IsSystemPrincipal(aPrincipal))
       return false;
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -614,16 +614,17 @@ public:
     static void CTypesActivityCallback(JSContext *cx,
                                        js::CTypesActivityType type);
     static bool InterruptCallback(JSContext *cx);
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     AutoMarkingPtr**  GetAutoRootsAdr() {return &mAutoRoots;}
 
+    JSObject* UnprivilegedJunkScope() { return mUnprivilegedJunkScope; }
     JSObject* PrivilegedJunkScope() { return mPrivilegedJunkScope; }
     JSObject* CompilationScope() { return mCompilationScope; }
 
     void InitSingletonScopes();
     void DeleteSingletonScopes();
 
     PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);
     void OnAfterProcessNextEvent() { mSlowScriptCheckpoint = mozilla::TimeStamp(); }
@@ -658,16 +659,17 @@ private:
     bool mDoingFinalization;
     XPCRootSetElem *mVariantRoots;
     XPCRootSetElem *mWrappedJSRoots;
     XPCRootSetElem *mObjectHolderRoots;
     nsTArray<xpcGCCallback> extraGCCallbacks;
     nsTArray<xpcContextCallback> extraContextCallbacks;
     nsRefPtr<WatchdogManager> mWatchdogManager;
     JS::GCSliceCallback mPrevGCSliceCallback;
+    JS::PersistentRootedObject mUnprivilegedJunkScope;
     JS::PersistentRootedObject mPrivilegedJunkScope;
     JS::PersistentRootedObject mCompilationScope;
     nsRefPtr<AsyncFreeSnowWhite> mAsyncSnowWhiteFreer;
 
     mozilla::TimeStamp mSlowScriptCheckpoint;
 
     friend class Watchdog;
     friend class AutoLockWatchdog;
@@ -2852,34 +2854,32 @@ class AutoCxPusher;
 }
 
 class XPCJSContextStack
 {
 public:
     explicit XPCJSContextStack(XPCJSRuntime *aRuntime)
       : mRuntime(aRuntime)
       , mSafeJSContext(nullptr)
-      , mSafeJSContextGlobal(aRuntime->Runtime(), nullptr)
     { }
 
     virtual ~XPCJSContextStack();
 
     uint32_t Count()
     {
         return mStack.Length();
     }
 
     JSContext *Peek()
     {
         return mStack.IsEmpty() ? nullptr : mStack[mStack.Length() - 1].cx;
     }
 
     JSContext *InitSafeJSContext();
     JSContext *GetSafeJSContext();
-    JSObject *GetSafeJSContextGlobal();
     bool HasJSContext(JSContext *cx);
 
     const InfallibleTArray<XPCJSContextInfo>* GetStack()
     { return &mStack; }
 
 private:
     friend class mozilla::dom::danger::AutoCxPusher;
     friend bool xpc::PushJSContextNoScriptContext(JSContext *aCx);;
@@ -2888,17 +2888,16 @@ private:
     // We make these private so that stack manipulation can only happen
     // through one of the above friends.
     JSContext *Pop();
     bool Push(JSContext *cx);
 
     AutoInfallibleTArray<XPCJSContextInfo, 16> mStack;
     XPCJSRuntime* mRuntime;
     JSContext*  mSafeJSContext;
-    JS::PersistentRootedObject mSafeJSContextGlobal;
 };
 
 /***************************************************************************/
 // 'Components' object implementations. nsXPCComponentsBase has the
 // less-privileged stuff that we're willing to expose to XBL.
 
 class nsXPCComponentsBase : public nsIXPCComponentsBase
 {
@@ -3725,18 +3724,16 @@ CrashIfNotInAutomation()
 }
 
 inline XPCWrappedNativeScope*
 ObjectScope(JSObject *obj)
 {
     return CompartmentPrivate::Get(obj)->scope;
 }
 
-extern const JSClass SafeJSContextGlobalClass;
-
 JSObject* NewOutObject(JSContext* cx, JSObject* scope);
 bool IsOutObject(JSContext* cx, JSObject* obj);
 
 nsresult HasInstance(JSContext *cx, JS::HandleObject objArg, const nsID *iid, bool *bp);
 
 /**
  * Define quick stubs on the given object, @a proto.
  *
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -432,27 +432,26 @@ GetNativeForGlobal(JSObject *global);
 /**
  * Returns the nsISupports native behind a given reflector (either DOM or
  * XPCWN).
  */
 nsISupports *
 UnwrapReflectorToISupports(JSObject *reflector);
 
 /**
- * In some cases a native object does not really belong to any compartment (XBL,
- * document created from by XHR of a worker, etc.). But when for some reason we
- * have to wrap these natives (because of an event for example) instead of just
- * wrapping them into some random compartment we find on the context stack (like
- * we did previously) a default compartment is used. This function returns that
- * compartment's global. It is a singleton on the runtime.
- * If you find yourself wanting to use this compartment, you're probably doing
+ * Singleton scopes for stuff that really doesn't fit anywhere else.
+ *
+ * If you find yourself wanting to use these compartments, you're probably doing
  * something wrong. Callers MUST consult with the XPConnect module owner before
  * using this compartment. If you don't, bholley will hunt you down.
  */
 JSObject *
+UnprivilegedJunkScope();
+
+JSObject *
 PrivilegedJunkScope();
 
 /**
  * Shared compilation scope for XUL prototype documents and XBL
  * precompilation. This compartment has a null principal. No code may run, and
  * it is invisible to the debugger.
  */
 JSObject *
@@ -460,23 +459,16 @@ CompilationScope();
 
 /**
  * If |aObj| is a window, returns the associated nsGlobalWindow.
  * Otherwise, returns null.
  */
 nsGlobalWindow*
 WindowOrNull(JSObject *aObj);
 
-/*
- * Returns the dummy global associated with the SafeJSContext. Callers MUST
- * consult with the XPConnect module owner before using this function.
- */
-JSObject *
-GetSafeJSContextGlobal();
-
 /**
  * If |aObj| has a window for a global, returns the associated nsGlobalWindow.
  * Otherwise, returns null.
  */
 nsGlobalWindow*
 WindowGlobalOrNull(JSObject *aObj);
 
 // Error reporter used when there is no associated DOM window on to which to