Bug 958324 - Remove same-compartment security wrappers for WNs. r=peterv,r=mrbkap
authorBobby Holley <bobbyholley@gmail.com>
Tue, 21 Jan 2014 08:26:40 -0800
changeset 174684 a8587cf48bb369cb1b8ca1cfabecc32304347b5d
parent 174683 960d29ff5ab0889dab79e4d247af531c046a8aa7
child 174685 05d4d9b9807c12a4ed79d0a53121b11b70c267a7
push idunknown
push userunknown
push dateunknown
reviewerspeterv, mrbkap
bugs958324
milestone29.0a1
Bug 958324 - Remove same-compartment security wrappers for WNs. r=peterv,r=mrbkap We've fixed Components, and SOWs are new-binding only. Youpie!
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/src/xpcpublic.h
js/xpconnect/wrappers/AccessCheck.cpp
js/xpconnect/wrappers/WrapperFactory.cpp
xpcom/base/ErrorList.h
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -23,25 +23,16 @@
 #include "mozilla/dom/BindingUtils.h"
 #include <algorithm>
 
 using namespace xpc;
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace JS;
 
-bool
-xpc_OkToHandOutWrapper(nsWrapperCache *cache)
-{
-    MOZ_ASSERT(cache->GetWrapper(), "Must have wrapper");
-    MOZ_ASSERT(IS_WN_REFLECTOR(cache->GetWrapper()),
-               "Must have XPCWrappedNative wrapper");
-    return !XPCWrappedNative::Get(cache->GetWrapper())->NeedsSOW();
-}
-
 /***************************************************************************/
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
 
 // No need to unlink the JS objects: if the XPCWrappedNative is cycle
 // collected then its mFlatJSObject will be cycle collected too and
 // finalization of the mFlatJSObject will unlink the JS objects (see
 // XPC_WN_NoHelper_Finalize and FlatJSObjectFinalized).
@@ -357,33 +348,28 @@ XPCWrappedNative::GetNewOrUsed(xpcObject
     // itself.
     const XPCNativeScriptableCreateInfo& sciWrapper =
         isClassInfo ? sci :
         GatherScriptableCreateInfo(identity, info, sciProto, sci);
 
     RootedObject parent(cx, Scope->GetGlobalJSObject());
 
     RootedValue newParentVal(cx, NullValue());
-    bool needsSOW = false;
-    bool needsCOW = false;
 
     mozilla::Maybe<JSAutoCompartment> ac;
 
     if (sciWrapper.GetFlags().WantPreCreate()) {
         // PreCreate may touch dead compartments.
         js::AutoMaybeTouchDeadZones agc(parent);
 
         RootedObject plannedParent(cx, parent);
         nsresult rv = sciWrapper.GetCallback()->PreCreate(identity, cx,
                                                           parent, parent.address());
         if (NS_FAILED(rv))
             return rv;
-
-        if (rv == NS_SUCCESS_CHROME_ACCESS_ONLY)
-            needsSOW = true;
         rv = NS_OK;
 
         MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
                    "Xray wrapper being used to parent XPCWrappedNative?");
 
         ac.construct(static_cast<JSContext*>(cx), parent);
 
         if (parent != plannedParent) {
@@ -411,25 +397,16 @@ XPCWrappedNative::GetNewOrUsed(xpcObject
                 MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
                 return rv;
             }
             *resultWrapper = wrapper.forget().get();
             return NS_OK;
         }
     } else {
         ac.construct(static_cast<JSContext*>(cx), parent);
-
-        nsISupports *Object = helper.Object();
-        if (nsXPCWrappedJSClass::IsWrappedJS(Object)) {
-            nsCOMPtr<nsIXPConnectWrappedJS> wrappedjs(do_QueryInterface(Object));
-            if (xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrappedjs->GetJSObject())) &&
-                !xpc::AccessCheck::isChrome(js::GetObjectCompartment(Scope->GetGlobalJSObject()))) {
-                needsCOW = true;
-            }
-        }
     }
 
     AutoMarkingWrappedNativeProtoPtr proto(cx);
 
     // If there is ClassInfo (and we are not building a wrapper for the
     // nsIClassInfo interface) then we use a wrapper that needs a prototype.
 
     // Note that the security check happens inside FindTearOff - after the
@@ -468,21 +445,16 @@ XPCWrappedNative::GetNewOrUsed(xpcObject
     if (!wrapper->Init(parent, &sciWrapper))
         return NS_ERROR_FAILURE;
 
     if (!wrapper->FindTearOff(Interface, false, &rv)) {
         MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
         return rv;
     }
 
-    if (needsSOW)
-        wrapper->SetNeedsSOW();
-    if (needsCOW)
-        wrapper->SetNeedsCOW();
-
     return FinishCreate(Scope, Interface, cache, wrapper, resultWrapper);
 }
 
 static nsresult
 FinishCreate(XPCWrappedNativeScope* Scope,
              XPCNativeInterface* Interface,
              nsWrapperCache *cache,
              XPCWrappedNative* inWrapper,
@@ -662,31 +634,16 @@ XPCWrappedNative::Destroy()
         if (rt && rt->GetDoingFinalization()) {
             nsContentUtils::DeferredFinalize(mIdentity);
             mIdentity = nullptr;
         } else {
             NS_RELEASE(mIdentity);
         }
     }
 
-    /*
-     * The only time GetRuntime() will be nullptr is if Destroy is called a
-     * second time on a wrapped native. Since we already unregistered the
-     * pointer the first time, there's no need to unregister again.
-     * Unregistration is safe the first time because mWrapper isn't used
-     * afterwards.
-     */
-    if (XPCJSRuntime *rt = GetRuntime()) {
-        if (IsIncrementalBarrierNeeded(rt->Runtime()))
-            IncrementalObjectBarrier(GetWrapperPreserveColor());
-        mWrapper.setToCrashOnTouch();
-    } else {
-        MOZ_ASSERT(mWrapper.isSetToCrashOnTouch());
-    }
-
     mMaybeScope = nullptr;
 }
 
 void
 XPCWrappedNative::UpdateScriptableInfo(XPCNativeScriptableInfo *si)
 {
     MOZ_ASSERT(mScriptableInfo, "UpdateScriptableInfo expects an existing scriptable info");
 
@@ -1221,25 +1178,16 @@ XPCWrappedNative::ReparentWrapperIfFound
             // the private of |flat|.
             //
             // NB: It's important to do this _after_ copying the properties to
             // propertyHolder. Otherwise, an object with |foo.x === foo| will
             // crash when JS_CopyPropertiesFrom tries to call wrap() on foo.x.
             JS_SetPrivate(flat, nullptr);
         }
 
-        // Before proceeding, eagerly create any same-compartment security wrappers
-        // that the object might have. This forces us to take the 'WithWrapper' path
-        // while transplanting that handles this stuff correctly.
-        {
-            JSAutoCompartment innerAC(cx, aOldScope->GetGlobalJSObject());
-            if (!wrapper->GetSameCompartmentSecurityWrapper(cx))
-                return NS_ERROR_FAILURE;
-        }
-
         // Update scope maps. This section modifies global state, so from
         // here on out we crash if anything fails.
         Native2WrappedNativeMap* oldMap = aOldScope->GetWrappedNativeMap();
         Native2WrappedNativeMap* newMap = aNewScope->GetWrappedNativeMap();
 
         oldMap->Remove(wrapper);
 
         if (wrapper->HasProto())
@@ -1266,30 +1214,20 @@ XPCWrappedNative::ReparentWrapperIfFound
 
         // Crash if the wrapper is already in the new scope.
         if (newMap->Find(wrapper->GetIdentityObject()))
             MOZ_CRASH();
 
         if (!newMap->Add(wrapper))
             MOZ_CRASH();
 
-        RootedObject ww(cx, wrapper->GetWrapper());
-        if (ww) {
-            RootedObject newwrapper(cx);
-            MOZ_ASSERT(wrapper->NeedsSOW(), "weird wrapper wrapper");
-
-            // Oops. We don't support transplanting objects with SOWs anymore.
+        flat = xpc::TransplantObject(cx, flat, newobj);
+        if (!flat)
             MOZ_CRASH();
 
-        } else {
-            flat = xpc::TransplantObject(cx, flat, newobj);
-            if (!flat)
-                MOZ_CRASH();
-        }
-
         MOZ_ASSERT(flat);
         wrapper->mFlatJSObject = flat;
         wrapper->mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
 
         if (cache) {
             bool preserving = cache->PreservingWrapper();
             cache->SetPreservingWrapper(false);
             cache->SetWrapper(flat);
@@ -1304,21 +1242,16 @@ XPCWrappedNative::ReparentWrapperIfFound
             (void) si->GetCallback()->PostTransplant(wrapper, cx, flat);
     }
 
     // Now we can just fix up the parent and return the wrapper
 
     if (aNewParent) {
         if (!JS_SetParent(cx, flat, aNewParent))
             MOZ_CRASH();
-
-        JSObject *nw = wrapper->GetWrapper();
-        if (nw && !JS_SetParent(cx, nw, JS_GetGlobalForObject(cx, aNewParent))) {
-            MOZ_CRASH();
-        }
     }
 
     return NS_OK;
 }
 
 // Orphans are sad little things - If only we could treat them better. :-(
 //
 // When a wrapper gets reparented to another scope (for example, when calling
@@ -1668,57 +1601,16 @@ XPCWrappedNative::InitTearOffJSObject(XP
     if (!obj)
         return false;
 
     JS_SetPrivate(obj, to);
     to->SetJSObject(obj);
     return true;
 }
 
-JSObject*
-XPCWrappedNative::GetSameCompartmentSecurityWrapper(JSContext *cx)
-{
-    // Grab the current state of affairs.
-    RootedObject flat(cx, GetFlatJSObject());
-    RootedObject wrapper(cx, GetWrapper());
-
-    // If we already have a wrapper, it must be what we want.
-    if (wrapper)
-        return wrapper;
-
-    // Chrome callers don't need same-compartment security wrappers.
-    JSCompartment *cxCompartment = js::GetContextCompartment(cx);
-    MOZ_ASSERT(cxCompartment == js::GetObjectCompartment(flat));
-    if (xpc::AccessCheck::isChrome(cxCompartment)) {
-        MOZ_ASSERT(wrapper == nullptr);
-        return flat;
-    }
-
-    // Check the possibilities. Note that we need to check for null in each
-    // case in order to distinguish between the 'no need for wrapper' and
-    // 'wrapping failed' cases.
-    //
-    // NB: We don't make SOWs for remote XUL domains where XBL scopes are
-    // disallowed.
-    if (NeedsSOW() && xpc::AllowXBLScope(js::GetContextCompartment(cx))) {
-        wrapper = xpc::WrapperFactory::WrapSOWObject(cx, flat);
-        if (!wrapper)
-            return nullptr;
-    }
-
-    // If we made a wrapper, cache it and return it.
-    if (wrapper) {
-        SetWrapper(wrapper);
-        return wrapper;
-    }
-
-    // Otherwise, just return the bare JS reflection.
-    return flat;
-}
-
 /***************************************************************************/
 
 static bool Throw(nsresult errNum, XPCCallContext& ccx)
 {
     XPCThrower::Throw(errNum, ccx);
     return false;
 }
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1025,36 +1025,16 @@ static inline bool IS_PROTO_CLASS(const 
 {
     return clazz == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
            clazz == &XPC_WN_NoMods_NoCall_Proto_JSClass ||
            clazz == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||
            clazz == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass;
 }
 
 /***************************************************************************/
-
-namespace XPCWrapper {
-
-enum WrapperType {
-    UNKNOWN         = 0,
-    NONE            = 0,
-    XPCNW_IMPLICIT  = 1 << 0,
-    XPCNW_EXPLICIT  = 1 << 1,
-    XPCNW           = (XPCNW_IMPLICIT | XPCNW_EXPLICIT),
-    SJOW            = 1 << 2,
-    // SJOW must be the last wrapper type that can be returned to chrome.
-
-    XOW             = 1 << 3,
-    COW             = 1 << 4,
-    SOW             = 1 << 5
-};
-
-}
-
-/***************************************************************************/
 // XPCWrappedNativeScope is one-to-one with a JS global object.
 
 class nsXPCComponentsBase;
 class XPCWrappedNativeScope : public PRCList
 {
 public:
 
     static XPCWrappedNativeScope*
@@ -2184,17 +2164,16 @@ public:
             mSet->Mark();
             if (mScriptableInfo)
                 mScriptableInfo->Mark();
         }
         if (HasProto())
             GetProto()->TraceSelf(trc);
         else
             GetScope()->TraceSelf(trc);
-        TraceWrapper(trc);
         if (mFlatJSObject && JS_IsGlobalObject(mFlatJSObject))
         {
             TraceXPCGlobal(trc, mFlatJSObject);
         }
     }
 
     void TraceJS(JSTracer *trc) {
         TraceInside(trc);
@@ -2227,51 +2206,16 @@ public:
     // Returns a string that shuld be free'd using JS_smprintf_free (or null).
     char* ToString(XPCWrappedNativeTearOff* to = nullptr) const;
 
     static void GatherProtoScriptableCreateInfo(nsIClassInfo* classInfo,
                                                 XPCNativeScriptableCreateInfo& sciProto);
 
     bool HasExternalReference() const {return mRefCnt > 1;}
 
-    bool NeedsSOW() { return mWrapper.hasFlag(WRAPPER_NEEDS_SOW); }
-    void SetNeedsSOW() { mWrapper.setFlags(WRAPPER_NEEDS_SOW); }
-    bool NeedsCOW() { return mWrapper.hasFlag(WRAPPER_NEEDS_COW); }
-    void SetNeedsCOW() { mWrapper.setFlags(WRAPPER_NEEDS_COW); }
-
-    JSObject* GetWrapperPreserveColor() const { return mWrapper.getPtr(); }
-
-    JSObject* GetWrapper()
-    {
-        JSObject* wrapper = GetWrapperPreserveColor();
-        if (wrapper) {
-            JS::ExposeObjectToActiveJS(wrapper);
-            // Call this to unmark mFlatJSObject.
-            GetFlatJSObject();
-        }
-        return wrapper;
-    }
-    void SetWrapper(JSObject *obj)
-    {
-        JS::IncrementalObjectBarrier(GetWrapperPreserveColor());
-        mWrapper.setPtr(obj);
-    }
-
-    void TraceWrapper(JSTracer *trc)
-    {
-        JS_CallTenuredObjectTracer(trc, &mWrapper, "XPCWrappedNative::mWrapper");
-    }
-
-    // Returns the relevant same-compartment security if applicable, or
-    // mFlatJSObject otherwise.
-    //
-    // This takes care of checking mWrapper to see if we already have such
-    // a wrapper.
-    JSObject *GetSameCompartmentSecurityWrapper(JSContext *cx);
-
     void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);
 
     // Make ctor and dtor protected (rather than private) to placate nsCOMPtr.
 protected:
     XPCWrappedNative(); // not implemented
 
     // This ctor is used if this object will have a proto.
     XPCWrappedNative(already_AddRefed<nsISupports> aIdentity,
@@ -2284,20 +2228,16 @@ protected:
 
     virtual ~XPCWrappedNative();
     void Destroy();
 
     void UpdateScriptableInfo(XPCNativeScriptableInfo *si);
 
 private:
     enum {
-        // Flags bits for mWrapper:
-        WRAPPER_NEEDS_SOW = JS_BIT(0),
-        WRAPPER_NEEDS_COW = JS_BIT(1),
-
         // Flags bits for mFlatJSObject:
         FLAT_JS_OBJECT_VALID = JS_BIT(0)
     };
 
 private:
 
     bool Init(JS::HandleObject parent, const XPCNativeScriptableCreateInfo* sci);
     bool FinishInit();
@@ -2321,17 +2261,16 @@ private:
     {
         XPCWrappedNativeScope*   mMaybeScope;
         XPCWrappedNativeProto*   mMaybeProto;
     };
     XPCNativeSet*                mSet;
     JS::TenuredHeap<JSObject*>   mFlatJSObject;
     XPCNativeScriptableInfo*     mScriptableInfo;
     XPCWrappedNativeTearOffChunk mFirstChunk;
-    JS::TenuredHeap<JSObject*>   mWrapper;
 };
 
 /***************************************************************************
 ****************************************************************************
 *
 * Core classes for wrapped JSObject for use from native code...
 *
 ****************************************************************************
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -114,28 +114,25 @@ struct RuntimeStats;
     JSCLASS_DOM_GLOBAL | JSCLASS_HAS_PRIVATE |                                \
     JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_IMPLEMENTS_BARRIERS |            \
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS + n)
 
 #define XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET (JSCLASS_GLOBAL_SLOT_COUNT + DOM_GLOBAL_SLOTS)
 
 #define XPCONNECT_GLOBAL_FLAGS XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(0)
 
-extern bool
-xpc_OkToHandOutWrapper(nsWrapperCache *cache);
-
 inline JSObject*
 xpc_FastGetCachedWrapper(nsWrapperCache *cache, JSObject *scope, JS::MutableHandleValue vp)
 {
     if (cache) {
         JSObject* wrapper = cache->GetWrapper();
         if (wrapper &&
             js::GetObjectCompartment(wrapper) == js::GetObjectCompartment(scope) &&
-            (cache->IsDOMBinding() ? !cache->HasSystemOnlyWrapper() :
-                                     xpc_OkToHandOutWrapper(cache))) {
+            !(cache->IsDOMBinding() && cache->HasSystemOnlyWrapper()))
+        {
             vp.setObject(*wrapper);
             return wrapper;
         }
     }
 
     return nullptr;
 }
 
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -258,22 +258,17 @@ AccessCheck::isCrossOriginAccessPermitte
 }
 
 bool
 AccessCheck::needsSystemOnlyWrapper(JSObject *obj)
 {
     JSObject* wrapper = obj;
     if (dom::GetSameCompartmentWrapperForDOMBinding(wrapper))
         return wrapper != obj;
-
-    if (!IS_WN_REFLECTOR(obj))
-        return false;
-
-    XPCWrappedNative *wn = XPCWrappedNative::Get(obj);
-    return wn->NeedsSOW();
+    return false;
 }
 
 enum Access { READ = (1<<0), WRITE = (1<<1), NO_ACCESS = 0 };
 
 static void
 EnterAndThrow(JSContext *cx, JSObject *wrapper, const char *msg)
 {
     JSAutoCompartment ac(cx, wrapper);
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -489,32 +489,19 @@ WrapperFactory::WrapForSameCompartment(J
     // be a security wrapper. These checks implicitly handle the security
     // wrapper case.
 
     // Outerize if necessary. This, in combination with the check in
     // PrepareForUnwrapping, means that calling JS_Wrap* always outerizes.
     obj = JS_ObjectToOuterObject(cx, obj);
     NS_ENSURE_TRUE(obj, nullptr);
 
-    if (dom::GetSameCompartmentWrapperForDOMBinding(*obj.address())) {
-        return obj;
-    }
-
-    MOZ_ASSERT(!dom::IsDOMObject(obj));
-
-    if (!IS_WN_REFLECTOR(obj))
-        return obj;
-
-    // Extract the WN. It should exist.
-    XPCWrappedNative *wn = XPCWrappedNative::Get(obj);
-    MOZ_ASSERT(wn, "Trying to wrap a dead WN!");
-
-    // The WN knows what to do.
-    RootedObject wrapper(cx, wn->GetSameCompartmentSecurityWrapper(cx));
-    return wrapper;
+    // The method below is a no-op for non-DOM objects.
+    dom::GetSameCompartmentWrapperForDOMBinding(*obj.address());
+    return obj;
 }
 
 // Call WaiveXrayAndWrap when you have a JS object that you don't want to be
 // wrapped in an Xray wrapper. cx->compartment is the compartment that will be
 // using the returned object. If the object to be wrapped is already in the
 // correct compartment, then this returns the unwrapped object.
 bool
 WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleValue vp)
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -595,20 +595,16 @@
   ERROR(NS_ERROR_XPC_BAD_INITIALIZER_NAME,             FAILURE(50)),
   ERROR(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN,                FAILURE(51)),
   ERROR(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN,           FAILURE(52)),
   ERROR(NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL,   FAILURE(53)),
   ERROR(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE,         FAILURE(54)),
   /* any new errors here should have an associated entry added in xpc.msg */
 
   ERROR(NS_SUCCESS_I_DID_SOMETHING,      SUCCESS(1)),
-  /* Classes that want to only be touched by chrome (or from code whose
-   * filename begins with chrome://global/) shoudl return this from their
-   * scriptable helper's PreCreate hook. */
-  ERROR(NS_SUCCESS_CHROME_ACCESS_ONLY,   SUCCESS(2)),
 #undef MODULE
 
 
   /* ======================================================================= */
   /* 19: NS_ERROR_MODULE_PROFILE */
   /* ======================================================================= */
 #define MODULE NS_ERROR_MODULE_PROFILE
   ERROR(NS_ERROR_LAUNCHED_CHILD_PROCESS,  FAILURE(200)),