LCOV - code coverage report
Current view: top level - js/xpconnect/wrappers - WrapperFactory.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 188 283 66.4 %
Date: 2017-07-14 16:53:18 Functions: 13 17 76.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* vim: set ts=8 sts=4 et sw=4 tw=99: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "WaiveXrayWrapper.h"
       8             : #include "FilteringWrapper.h"
       9             : #include "AddonWrapper.h"
      10             : #include "XrayWrapper.h"
      11             : #include "AccessCheck.h"
      12             : #include "XPCWrapper.h"
      13             : #include "ChromeObjectWrapper.h"
      14             : #include "WrapperFactory.h"
      15             : 
      16             : #include "xpcprivate.h"
      17             : #include "XPCMaps.h"
      18             : #include "mozilla/dom/BindingUtils.h"
      19             : #include "jsfriendapi.h"
      20             : #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
      21             : #include "mozilla/Likely.h"
      22             : #include "mozilla/dom/ScriptSettings.h"
      23             : #include "nsContentUtils.h"
      24             : #include "nsXULAppAPI.h"
      25             : 
      26             : using namespace JS;
      27             : using namespace js;
      28             : using namespace mozilla;
      29             : 
      30             : namespace xpc {
      31             : 
      32             : // When chrome pulls a naked property across the membrane using
      33             : // .wrappedJSObject, we want it to cross the membrane into the
      34             : // chrome compartment without automatically being wrapped into an
      35             : // X-ray wrapper. We achieve this by wrapping it into a special
      36             : // transparent wrapper in the origin (non-chrome) compartment. When
      37             : // an object with that special wrapper applied crosses into chrome,
      38             : // we know to not apply an X-ray wrapper.
      39             : const Wrapper XrayWaiver(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG);
      40             : 
      41             : // When objects for which we waived the X-ray wrapper cross into
      42             : // chrome, we wrap them into a special cross-compartment wrapper
      43             : // that transitively extends the waiver to all properties we get
      44             : // off it.
      45             : const WaiveXrayWrapper WaiveXrayWrapper::singleton(0);
      46             : 
      47             : bool
      48           0 : WrapperFactory::IsCOW(JSObject* obj)
      49             : {
      50           0 :     return IsWrapper(obj) &&
      51           0 :            Wrapper::wrapperHandler(obj) == &ChromeObjectWrapper::singleton;
      52             : }
      53             : 
      54             : JSObject*
      55           5 : WrapperFactory::GetXrayWaiver(HandleObject obj)
      56             : {
      57             :     // Object should come fully unwrapped but outerized.
      58           5 :     MOZ_ASSERT(obj == UncheckedUnwrap(obj));
      59           5 :     MOZ_ASSERT(!js::IsWindow(obj));
      60           5 :     XPCWrappedNativeScope* scope = ObjectScope(obj);
      61           5 :     MOZ_ASSERT(scope);
      62             : 
      63           5 :     if (!scope->mWaiverWrapperMap)
      64           4 :         return nullptr;
      65             : 
      66           1 :     return scope->mWaiverWrapperMap->Find(obj);
      67             : }
      68             : 
      69             : JSObject*
      70           1 : WrapperFactory::CreateXrayWaiver(JSContext* cx, HandleObject obj)
      71             : {
      72             :     // The caller is required to have already done a lookup.
      73             :     // NB: This implictly performs the assertions of GetXrayWaiver.
      74           1 :     MOZ_ASSERT(!GetXrayWaiver(obj));
      75           1 :     XPCWrappedNativeScope* scope = ObjectScope(obj);
      76             : 
      77           2 :     JSAutoCompartment ac(cx, obj);
      78           1 :     JSObject* waiver = Wrapper::New(cx, obj, &XrayWaiver);
      79           1 :     if (!waiver)
      80           0 :         return nullptr;
      81             : 
      82             :     // Add the new waiver to the map. It's important that we only ever have
      83             :     // one waiver for the lifetime of the target object.
      84           1 :     if (!scope->mWaiverWrapperMap) {
      85             :         scope->mWaiverWrapperMap =
      86           1 :           JSObject2JSObjectMap::newMap(XPC_WRAPPER_MAP_LENGTH);
      87             :     }
      88           1 :     if (!scope->mWaiverWrapperMap->Add(cx, obj, waiver))
      89           0 :         return nullptr;
      90           1 :     return waiver;
      91             : }
      92             : 
      93             : JSObject*
      94           2 : WrapperFactory::WaiveXray(JSContext* cx, JSObject* objArg)
      95             : {
      96           4 :     RootedObject obj(cx, objArg);
      97           2 :     obj = UncheckedUnwrap(obj);
      98           2 :     MOZ_ASSERT(!js::IsWindow(obj));
      99             : 
     100           2 :     JSObject* waiver = GetXrayWaiver(obj);
     101           2 :     if (!waiver) {
     102           1 :         waiver = CreateXrayWaiver(cx, obj);
     103             :     }
     104           2 :     MOZ_ASSERT(JS::ObjectIsNotGray(waiver));
     105           4 :     return waiver;
     106             : }
     107             : 
     108             : /* static */ bool
     109           1 : WrapperFactory::AllowWaiver(JSCompartment* target, JSCompartment* origin)
     110             : {
     111           2 :     return CompartmentPrivate::Get(target)->allowWaivers &&
     112           2 :            AccessCheck::subsumes(target, origin);
     113             : }
     114             : 
     115             : /* static */ bool
     116           0 : WrapperFactory::AllowWaiver(JSObject* wrapper) {
     117           0 :     MOZ_ASSERT(js::IsCrossCompartmentWrapper(wrapper));
     118           0 :     return AllowWaiver(js::GetObjectCompartment(wrapper),
     119           0 :                        js::GetObjectCompartment(js::UncheckedUnwrap(wrapper)));
     120             : }
     121             : 
     122             : inline bool
     123       26203 : ShouldWaiveXray(JSContext* cx, JSObject* originalObj)
     124             : {
     125             :     unsigned flags;
     126       26203 :     (void) js::UncheckedUnwrap(originalObj, /* stopAtWindowProxy = */ true, &flags);
     127             : 
     128             :     // If the original object did not point through an Xray waiver, we're done.
     129       26203 :     if (!(flags & WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG))
     130       26202 :         return false;
     131             : 
     132             :     // If the original object was not a cross-compartment wrapper, that means
     133             :     // that the caller explicitly created a waiver. Preserve it so that things
     134             :     // like WaiveXrayAndWrap work.
     135           1 :     if (!(flags & Wrapper::CROSS_COMPARTMENT))
     136           1 :         return true;
     137             : 
     138             :     // Otherwise, this is a case of explicitly passing a wrapper across a
     139             :     // compartment boundary. In that case, we only want to preserve waivers
     140             :     // in transactions between same-origin compartments.
     141           0 :     JSCompartment* oldCompartment = js::GetObjectCompartment(originalObj);
     142           0 :     JSCompartment* newCompartment = js::GetContextCompartment(cx);
     143           0 :     bool sameOrigin = false;
     144           0 :     if (OriginAttributes::IsRestrictOpenerAccessForFPI()) {
     145           0 :         sameOrigin =
     146           0 :             AccessCheck::subsumesConsideringDomain(oldCompartment, newCompartment) &&
     147           0 :             AccessCheck::subsumesConsideringDomain(newCompartment, oldCompartment);
     148             :     } else {
     149           0 :         sameOrigin =
     150           0 :             AccessCheck::subsumesConsideringDomainIgnoringFPD(oldCompartment, newCompartment) &&
     151           0 :             AccessCheck::subsumesConsideringDomainIgnoringFPD(newCompartment, oldCompartment);
     152             :     }
     153           0 :     return sameOrigin;
     154             : }
     155             : 
     156             : void
     157       26203 : WrapperFactory::PrepareForWrapping(JSContext* cx, HandleObject scope,
     158             :                                    HandleObject objArg, HandleObject objectPassedToWrap,
     159             :                                    MutableHandleObject retObj)
     160             : {
     161       26203 :     bool waive = ShouldWaiveXray(cx, objectPassedToWrap);
     162       28971 :     RootedObject obj(cx, objArg);
     163       26203 :     retObj.set(nullptr);
     164             :     // Outerize any raw inner objects at the entry point here, so that we don't
     165             :     // have to worry about them for the rest of the wrapping code.
     166       26203 :     if (js::IsWindow(obj)) {
     167          16 :         JSAutoCompartment ac(cx, obj);
     168           8 :         obj = js::ToWindowProxyIfWindow(obj);
     169           8 :         MOZ_ASSERT(obj);
     170             :         // ToWindowProxyIfWindow can return a CCW if |obj| was a
     171             :         // navigated-away-from Window. Strip any CCWs.
     172           8 :         obj = js::UncheckedUnwrap(obj);
     173           8 :         if (JS_IsDeadWrapper(obj)) {
     174           0 :             JS_ReportErrorASCII(cx, "Can't wrap dead object");
     175           0 :             return;
     176             :         }
     177           8 :         MOZ_ASSERT(js::IsWindowProxy(obj));
     178             :         // We crossed a compartment boundary there, so may now have a gray
     179             :         // object.  This function is not allowed to return gray objects, so
     180             :         // don't do that.
     181           8 :         ExposeObjectToActiveJS(obj);
     182             :     }
     183             : 
     184             :     // If the object is a dead wrapper, return a new dead wrapper rather than
     185             :     // trying to wrap it for a different compartment.
     186       26203 :     if (JS_IsDeadWrapper(obj)) {
     187           0 :         retObj.set(JS_NewDeadWrapper(cx, obj));
     188           0 :         return;
     189             :     }
     190             : 
     191             :     // If we've somehow gotten to this point after either the source or target
     192             :     // compartment has been nuked, return a DeadObjectProxy to prevent further
     193             :     // access.
     194             :     // However, we always need to provide live wrappers for ScriptSourceObjects,
     195             :     // since they're used for cross-compartment cloned scripts, and need to
     196             :     // remain accessible even after the original compartment has been nuked.
     197       26203 :     JSCompartment* origin = js::GetObjectCompartment(obj);
     198       26203 :     JSCompartment* target = js::GetObjectCompartment(scope);
     199       43926 :     if (!JS_IsScriptSourceObject(obj) &&
     200       35446 :         (CompartmentPrivate::Get(origin)->wasNuked ||
     201       17723 :          CompartmentPrivate::Get(target)->wasNuked)) {
     202           0 :         NS_WARNING("Trying to create a wrapper into or out of a nuked compartment");
     203             : 
     204           0 :         retObj.set(JS_NewDeadWrapper(cx));
     205           0 :         return;
     206             :     }
     207             : 
     208             : 
     209             :     // If we've got a WindowProxy, there's nothing special that needs to be
     210             :     // done here, and we can move on to the next phase of wrapping. We handle
     211             :     // this case first to allow us to assert against wrappers below.
     212       26203 :     if (js::IsWindowProxy(obj)) {
     213         425 :         retObj.set(waive ? WaiveXray(cx, obj) : obj);
     214         425 :         return;
     215             :     }
     216             : 
     217             :     // Here are the rules for wrapping:
     218             :     // We should never get a proxy here (the JS engine unwraps those for us).
     219       25778 :     MOZ_ASSERT(!IsWrapper(obj));
     220             : 
     221             :     // Now, our object is ready to be wrapped, but several objects (notably
     222             :     // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of
     223             :     // those objects in a security wrapper, then we need to hand back the
     224             :     // wrapper for the new scope instead. Also, global objects don't move
     225             :     // between scopes so for those we also want to return the wrapper. So...
     226       25778 :     if (!IS_WN_REFLECTOR(obj) || JS_IsGlobalObject(obj)) {
     227       23007 :         retObj.set(waive ? WaiveXray(cx, obj) : obj);
     228       23007 :         return;
     229             :     }
     230             : 
     231        2771 :     XPCWrappedNative* wn = XPCWrappedNative::Get(obj);
     232             : 
     233        5539 :     JSAutoCompartment ac(cx, obj);
     234        5539 :     XPCCallContext ccx(cx, obj);
     235        5539 :     RootedObject wrapScope(cx, scope);
     236             : 
     237             :     {
     238        2771 :         if (ccx.GetScriptable() && ccx.GetScriptable()->WantPreCreate()) {
     239             :             // We have a precreate hook. This object might enforce that we only
     240             :             // ever create JS object for it.
     241             : 
     242             :             // Note: this penalizes objects that only have one wrapper, but are
     243             :             // being accessed across compartments. We would really prefer to
     244             :             // replace the above code with a test that says "do you only have one
     245             :             // wrapper?"
     246         114 :             nsresult rv = wn->GetScriptable()->
     247         114 :                 PreCreate(wn->Native(), cx, scope, wrapScope.address());
     248         114 :             if (NS_FAILED(rv)) {
     249           0 :                 retObj.set(waive ? WaiveXray(cx, obj) : obj);
     250           2 :                 return;
     251             :             }
     252             : 
     253             :             // If the handed back scope differs from the passed-in scope and is in
     254             :             // a separate compartment, then this object is explicitly requesting
     255             :             // that we don't create a second JS object for it: create a security
     256             :             // wrapper.
     257         114 :             if (js::GetObjectCompartment(scope) != js::GetObjectCompartment(wrapScope)) {
     258           2 :                 retObj.set(waive ? WaiveXray(cx, obj) : obj);
     259           2 :                 return;
     260             :             }
     261             : 
     262         224 :             RootedObject currentScope(cx, JS_GetGlobalForObject(cx, obj));
     263         112 :             if (MOZ_UNLIKELY(wrapScope != currentScope)) {
     264             :                 // The wrapper claims it wants to be in the new scope, but
     265             :                 // currently has a reflection that lives in the old scope. This
     266             :                 // can mean one of two things, both of which are rare:
     267             :                 //
     268             :                 // 1 - The object has a PreCreate hook (we checked for it above),
     269             :                 // but is deciding to request one-wrapper-per-scope (rather than
     270             :                 // one-wrapper-per-native) for some reason. Usually, a PreCreate
     271             :                 // hook indicates one-wrapper-per-native. In this case we want to
     272             :                 // make a new wrapper in the new scope.
     273             :                 //
     274             :                 // 2 - We're midway through wrapper reparenting. The document has
     275             :                 // moved to a new scope, but |wn| hasn't been moved yet, and
     276             :                 // we ended up calling JS_WrapObject() on its JS object. In this
     277             :                 // case, we want to return the existing wrapper.
     278             :                 //
     279             :                 // So we do a trick: call PreCreate _again_, but say that we're
     280             :                 // wrapping for the old scope, rather than the new one. If (1) is
     281             :                 // the case, then PreCreate will return the scope we pass to it
     282             :                 // (the old scope). If (2) is the case, PreCreate will return the
     283             :                 // scope of the document (the new scope).
     284         224 :                 RootedObject probe(cx);
     285         112 :                 rv = wn->GetScriptable()->
     286         112 :                     PreCreate(wn->Native(), cx, currentScope, probe.address());
     287             : 
     288             :                 // Check for case (2).
     289         112 :                 if (probe != currentScope) {
     290           0 :                     MOZ_ASSERT(probe == wrapScope);
     291           0 :                     retObj.set(waive ? WaiveXray(cx, obj) : obj);
     292           0 :                     return;
     293             :                 }
     294             : 
     295             :                 // Ok, must be case (1). Fall through and create a new wrapper.
     296             :             }
     297             : 
     298             :             // Nasty hack for late-breaking bug 781476. This will confuse identity checks,
     299             :             // but it's probably better than any of our alternatives.
     300             :             //
     301             :             // Note: We have to ignore domain here. The JS engine assumes that, given a
     302             :             // compartment c, if c->wrap(x) returns a cross-compartment wrapper at time t0,
     303             :             // it will also return a cross-compartment wrapper for any time t1 > t0 unless
     304             :             // an explicit transplant is performed. In particular, wrapper recomputation
     305             :             // assumes that recomputing a wrapper will always result in a wrapper.
     306             :             //
     307             :             // This doesn't actually pose a security issue, because we'll still compute
     308             :             // the correct (opaque) wrapper for the object below given the security
     309             :             // characteristics of the two compartments.
     310         112 :             if (!AccessCheck::isChrome(js::GetObjectCompartment(wrapScope)) &&
     311           0 :                  AccessCheck::subsumes(js::GetObjectCompartment(wrapScope),
     312           0 :                                        js::GetObjectCompartment(obj)))
     313             :             {
     314           0 :                 retObj.set(waive ? WaiveXray(cx, obj) : obj);
     315           0 :                 return;
     316             :             }
     317             :         }
     318             :     }
     319             : 
     320             :     // This public WrapNativeToJSVal API enters the compartment of 'wrapScope'
     321             :     // so we don't have to.
     322        5537 :     RootedValue v(cx);
     323             :     nsresult rv =
     324        8306 :         nsXPConnect::XPConnect()->WrapNativeToJSVal(cx, wrapScope, wn->Native(), nullptr,
     325        5538 :                                                     &NS_GET_IID(nsISupports), false, &v);
     326        2768 :     if (NS_FAILED(rv)) {
     327           0 :         return;
     328             :     }
     329             : 
     330        2768 :     obj.set(&v.toObject());
     331        2768 :     MOZ_ASSERT(IS_WN_REFLECTOR(obj), "bad object");
     332        2768 :     MOZ_ASSERT(JS::ObjectIsNotGray(obj), "Should never return gray reflectors");
     333             : 
     334             :     // Because the underlying native didn't have a PreCreate hook, we had
     335             :     // to a new (or possibly pre-existing) XPCWN in our compartment.
     336             :     // This could be a problem for chrome code that passes XPCOM objects
     337             :     // across compartments, because the effects of QI would disappear across
     338             :     // compartments.
     339             :     //
     340             :     // So whenever we pull an XPCWN across compartments in this manner, we
     341             :     // give the destination object the union of the two native sets. We try
     342             :     // to do this cleverly in the common case to avoid too much overhead.
     343        2768 :     XPCWrappedNative* newwn = XPCWrappedNative::Get(obj);
     344        5536 :     RefPtr<XPCNativeSet> unionSet = XPCNativeSet::GetNewOrUsed(newwn->GetSet(),
     345        5536 :                                                                wn->GetSet(), false);
     346        2768 :     if (!unionSet) {
     347           0 :         return;
     348             :     }
     349        2768 :     newwn->SetSet(unionSet.forget());
     350             : 
     351        2768 :     retObj.set(waive ? WaiveXray(cx, obj) : obj);
     352             : }
     353             : 
     354             : #ifdef DEBUG
     355             : static void
     356        9310 : DEBUG_CheckUnwrapSafety(HandleObject obj, const js::Wrapper* handler,
     357             :                         JSCompartment* origin, JSCompartment* target)
     358             : {
     359       17494 :     if (!JS_IsScriptSourceObject(obj) &&
     360       16368 :         (CompartmentPrivate::Get(origin)->wasNuked || CompartmentPrivate::Get(target)->wasNuked)) {
     361             :         // If either compartment has already been nuked, we should have returned
     362             :         // a dead wrapper from our prewrap callback, and this function should
     363             :         // not be called.
     364           0 :         MOZ_ASSERT_UNREACHABLE("CheckUnwrapSafety called for a dead wrapper");
     365        9310 :     } else if (AccessCheck::isChrome(target) || xpc::IsUniversalXPConnectEnabled(target)) {
     366             :         // If the caller is chrome (or effectively so), unwrap should always be allowed.
     367        9309 :         MOZ_ASSERT(!handler->hasSecurityPolicy());
     368           1 :     } else if (CompartmentPrivate::Get(origin)->forcePermissiveCOWs) {
     369             :         // Similarly, if this is a privileged scope that has opted to make itself
     370             :         // accessible to the world (allowed only during automation), unwrap should
     371             :         // be allowed.
     372           0 :         MOZ_ASSERT(!handler->hasSecurityPolicy());
     373             :     } else {
     374             :         // Otherwise, it should depend on whether the target subsumes the origin.
     375           1 :         MOZ_ASSERT(handler->hasSecurityPolicy() == !(OriginAttributes::IsRestrictOpenerAccessForFPI() ?
     376             :                                                        AccessCheck::subsumesConsideringDomain(target, origin) :
     377             :                                                        AccessCheck::subsumesConsideringDomainIgnoringFPD(target, origin)));
     378             :     }
     379        9310 : }
     380             : #else
     381             : #define DEBUG_CheckUnwrapSafety(obj, handler, origin, target) {}
     382             : #endif
     383             : 
     384             : static const Wrapper*
     385        9308 : SelectWrapper(bool securityWrapper, XrayType xrayType, bool waiveXrays, JSObject* obj)
     386             : {
     387             :     // Waived Xray uses a modified CCW that has transparent behavior but
     388             :     // transitively waives Xrays on arguments.
     389        9308 :     if (waiveXrays) {
     390           0 :         MOZ_ASSERT(!securityWrapper);
     391           0 :         return &WaiveXrayWrapper::singleton;
     392             :     }
     393             : 
     394             :     // If we don't want or can't use Xrays, select a wrapper that's either
     395             :     // entirely transparent or entirely opaque.
     396        9308 :     if (xrayType == NotXray) {
     397        8062 :         if (!securityWrapper)
     398        8062 :             return &CrossCompartmentWrapper::singleton;
     399           0 :         return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
     400             :     }
     401             : 
     402             :     // Ok, we're using Xray. If this isn't a security wrapper, use the permissive
     403             :     // version and skip the filter.
     404        1246 :     if (!securityWrapper) {
     405        1245 :         if (xrayType == XrayForWrappedNative)
     406           0 :             return &PermissiveXrayXPCWN::singleton;
     407        1245 :         else if (xrayType == XrayForDOMObject)
     408         111 :             return &PermissiveXrayDOM::singleton;
     409        1134 :         else if (xrayType == XrayForJSObject)
     410           8 :             return &PermissiveXrayJS::singleton;
     411        1126 :         MOZ_ASSERT(xrayType == XrayForOpaqueObject);
     412        1126 :         return &PermissiveXrayOpaque::singleton;
     413             :     }
     414             : 
     415             :     // This is a security wrapper. Use the security versions and filter.
     416           1 :     if (xrayType == XrayForDOMObject && IdentifyCrossOriginObject(obj) != CrossOriginOpaque)
     417             :         return &FilteringWrapper<CrossOriginXrayWrapper,
     418           1 :                                  CrossOriginAccessiblePropertiesOnly>::singleton;
     419             : 
     420             :     // There's never any reason to expose other objects to non-subsuming actors.
     421             :     // Just use an opaque wrapper in these cases.
     422             :     //
     423             :     // In general, we don't want opaque function wrappers to be callable.
     424             :     // But in the case of XBL, we rely on content being able to invoke
     425             :     // functions exposed from the XBL scope. We could remove this exception,
     426             :     // if needed, by using ExportFunction to generate the content-side
     427             :     // representations of XBL methods.
     428           0 :     if (xrayType == XrayForJSObject && IsInContentXBLScope(obj))
     429           0 :         return &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton;
     430           0 :     return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
     431             : }
     432             : 
     433             : static const Wrapper*
     434           0 : SelectAddonWrapper(JSContext* cx, HandleObject obj, const Wrapper* wrapper)
     435             : {
     436           0 :     JSAddonId* originAddon = JS::AddonIdOfObject(obj);
     437           0 :     JSAddonId* targetAddon = JS::AddonIdOfObject(JS::CurrentGlobalOrNull(cx));
     438             : 
     439           0 :     MOZ_ASSERT(AccessCheck::isChrome(JS::CurrentGlobalOrNull(cx)));
     440           0 :     MOZ_ASSERT(targetAddon);
     441             : 
     442           0 :     if (targetAddon == originAddon)
     443           0 :         return wrapper;
     444             : 
     445             :     // Add-on interposition only supports certain wrapper types, so we check if
     446             :     // we would have used one of the supported ones.
     447           0 :     if (wrapper == &CrossCompartmentWrapper::singleton)
     448           0 :         return &AddonWrapper<CrossCompartmentWrapper>::singleton;
     449           0 :     else if (wrapper == &PermissiveXrayXPCWN::singleton)
     450           0 :         return &AddonWrapper<PermissiveXrayXPCWN>::singleton;
     451           0 :     else if (wrapper == &PermissiveXrayDOM::singleton)
     452           0 :         return &AddonWrapper<PermissiveXrayDOM>::singleton;
     453             : 
     454             :     // |wrapper| is not supported for interposition, so we don't do it.
     455           0 :     return wrapper;
     456             : }
     457             : 
     458             : JSObject*
     459        9310 : WrapperFactory::Rewrap(JSContext* cx, HandleObject existing, HandleObject obj)
     460             : {
     461        9310 :     MOZ_ASSERT(!IsWrapper(obj) ||
     462             :                GetProxyHandler(obj) == &XrayWaiver ||
     463             :                js::IsWindowProxy(obj),
     464             :                "wrapped object passed to rewrap");
     465        9310 :     MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj)), "trying to wrap a holder");
     466        9310 :     MOZ_ASSERT(!js::IsWindow(obj));
     467        9310 :     MOZ_ASSERT(dom::IsJSAPIActive());
     468             : 
     469             :     // Compute the information we need to select the right wrapper.
     470        9310 :     JSCompartment* origin = js::GetObjectCompartment(obj);
     471        9310 :     JSCompartment* target = js::GetContextCompartment(cx);
     472        9310 :     bool originIsChrome = AccessCheck::isChrome(origin);
     473        9310 :     bool targetIsChrome = AccessCheck::isChrome(target);
     474        9310 :     bool originSubsumesTarget = OriginAttributes::IsRestrictOpenerAccessForFPI() ?
     475             :                                   AccessCheck::subsumesConsideringDomain(origin, target) :
     476        9310 :                                   AccessCheck::subsumesConsideringDomainIgnoringFPD(origin, target);
     477        9310 :     bool targetSubsumesOrigin = OriginAttributes::IsRestrictOpenerAccessForFPI() ?
     478             :                                   AccessCheck::subsumesConsideringDomain(target, origin) :
     479        9310 :                                   AccessCheck::subsumesConsideringDomainIgnoringFPD(target, origin);
     480        9310 :     bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget;
     481             : 
     482             :     const Wrapper* wrapper;
     483             : 
     484             :     CompartmentPrivate* originCompartmentPrivate =
     485        9310 :       CompartmentPrivate::Get(origin);
     486             :     CompartmentPrivate* targetCompartmentPrivate =
     487        9310 :       CompartmentPrivate::Get(target);
     488             : 
     489             :     //
     490             :     // First, handle the special cases.
     491             :     //
     492             : 
     493             :     // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use
     494             :     // a vanilla CCW.
     495        9310 :     if (targetCompartmentPrivate->universalXPConnectEnabled) {
     496           2 :         CrashIfNotInAutomation();
     497           2 :         wrapper = &CrossCompartmentWrapper::singleton;
     498             :     }
     499             : 
     500             :     // Let the SpecialPowers scope make its stuff easily accessible to content.
     501        9308 :     else if (originCompartmentPrivate->forcePermissiveCOWs) {
     502           0 :         CrashIfNotInAutomation();
     503           0 :         wrapper = &CrossCompartmentWrapper::singleton;
     504             :     }
     505             : 
     506             :     // Special handling for chrome objects being exposed to content.
     507        9308 :     else if (originIsChrome && !targetIsChrome) {
     508             :         // If this is a chrome function being exposed to content, we need to allow
     509             :         // call (but nothing else). We allow CPOWs that purport to be function's
     510             :         // here, but only in the content process.
     511           0 :         if ((IdentifyStandardInstance(obj) == JSProto_Function ||
     512           0 :             (jsipc::IsCPOW(obj) && JS::IsCallable(obj) &&
     513           0 :              XRE_IsContentProcess())))
     514             :         {
     515           0 :             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton;
     516             :         }
     517             : 
     518             :         // For Vanilla JSObjects exposed from chrome to content, we use a wrapper
     519             :         // that supports __exposedProps__. We'd like to get rid of these eventually,
     520             :         // but in their current form they don't cause much trouble.
     521           0 :         else if (IdentifyStandardInstance(obj) == JSProto_Object) {
     522           0 :             wrapper = &ChromeObjectWrapper::singleton;
     523             :         }
     524             : 
     525             :         // Otherwise we get an opaque wrapper.
     526             :         else {
     527           0 :             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
     528             :         }
     529             :     }
     530             : 
     531             :     //
     532             :     // Now, handle the regular cases.
     533             :     //
     534             :     // These are wrappers we can compute using a rule-based approach. In order
     535             :     // to do so, we need to compute some parameters.
     536             :     //
     537             :     else {
     538             : 
     539             :         // The wrapper is a security wrapper (protecting the wrappee) if and
     540             :         // only if the target does not subsume the origin.
     541        9308 :         bool securityWrapper = !targetSubsumesOrigin;
     542             : 
     543             :         // Xrays are warranted if either the target or the origin don't trust
     544             :         // each other. This is generally the case, unless the two are same-origin
     545             :         // and the caller has not requested same-origin Xrays.
     546             :         //
     547             :         // Xrays are a bidirectional protection, since it affords clarity to the
     548             :         // caller and privacy to the callee.
     549       17490 :         bool sameOriginXrays = originCompartmentPrivate->wantXrays ||
     550       17490 :                                targetCompartmentPrivate->wantXrays;
     551        9308 :         bool wantXrays = !sameOrigin || sameOriginXrays;
     552             : 
     553        9308 :         XrayType xrayType = wantXrays ? GetXrayType(obj) : NotXray;
     554             : 
     555             :         // If Xrays are warranted, the caller may waive them for non-security
     556             :         // wrappers (unless explicitly forbidden from doing so).
     557        2491 :         bool waiveXrays = wantXrays && !securityWrapper &&
     558       11798 :                           targetCompartmentPrivate->allowWaivers &&
     559       10553 :                           HasWaiveXrayFlag(obj);
     560             : 
     561        9308 :         wrapper = SelectWrapper(securityWrapper, xrayType, waiveXrays, obj);
     562             : 
     563             :         // If we want to apply add-on interposition in the target compartment,
     564             :         // then we try to "upgrade" the wrapper to an interposing one.
     565        9308 :         if (targetCompartmentPrivate->scope->HasInterposition())
     566           0 :             wrapper = SelectAddonWrapper(cx, obj, wrapper);
     567             :     }
     568             : 
     569        9310 :     if (!targetSubsumesOrigin) {
     570             :         // Do a belt-and-suspenders check against exposing eval()/Function() to
     571             :         // non-subsuming content.
     572           3 :         if (JSFunction* fun = JS_GetObjectFunction(obj)) {
     573           0 :             if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) {
     574           0 :                 NS_WARNING("Trying to expose eval or Function to non-subsuming content!");
     575           0 :                 wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
     576             :             }
     577             :         }
     578             :     }
     579             : 
     580        9310 :     DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target);
     581             : 
     582        9310 :     if (existing)
     583          10 :         return Wrapper::Renew(existing, obj, wrapper);
     584             : 
     585        9300 :     return Wrapper::New(cx, obj, wrapper);
     586             : }
     587             : 
     588             : // Call WaiveXrayAndWrap when you have a JS object that you don't want to be
     589             : // wrapped in an Xray wrapper. cx->compartment is the compartment that will be
     590             : // using the returned object. If the object to be wrapped is already in the
     591             : // correct compartment, then this returns the unwrapped object.
     592             : bool
     593           1 : WrapperFactory::WaiveXrayAndWrap(JSContext* cx, MutableHandleValue vp)
     594             : {
     595           1 :     if (vp.isPrimitive())
     596           0 :         return JS_WrapValue(cx, vp);
     597             : 
     598           2 :     RootedObject obj(cx, &vp.toObject());
     599           1 :     if (!WaiveXrayAndWrap(cx, &obj))
     600           0 :         return false;
     601             : 
     602           1 :     vp.setObject(*obj);
     603           1 :     return true;
     604             : }
     605             : 
     606             : bool
     607           1 : WrapperFactory::WaiveXrayAndWrap(JSContext* cx, MutableHandleObject argObj)
     608             : {
     609           1 :     MOZ_ASSERT(argObj);
     610           2 :     RootedObject obj(cx, js::UncheckedUnwrap(argObj));
     611           1 :     MOZ_ASSERT(!js::IsWindow(obj));
     612           1 :     if (js::IsObjectInContextCompartment(obj, cx)) {
     613           0 :         argObj.set(obj);
     614           0 :         return true;
     615             :     }
     616             : 
     617             :     // Even though waivers have no effect on access by scopes that don't subsume
     618             :     // the underlying object, good defense-in-depth dictates that we should avoid
     619             :     // handing out waivers to callers that can't use them. The transitive waiving
     620             :     // machinery unconditionally calls WaiveXrayAndWrap on return values from
     621             :     // waived functions, even though the return value might be not be same-origin
     622             :     // with the function. So if we find ourselves trying to create a waiver for
     623             :     // |cx|, we should check whether the caller has any business with waivers
     624             :     // to things in |obj|'s compartment.
     625           1 :     JSCompartment* target = js::GetContextCompartment(cx);
     626           1 :     JSCompartment* origin = js::GetObjectCompartment(obj);
     627           1 :     obj = AllowWaiver(target, origin) ? WaiveXray(cx, obj) : obj;
     628           1 :     if (!obj)
     629           0 :         return false;
     630             : 
     631           1 :     if (!JS_WrapObject(cx, &obj))
     632           0 :         return false;
     633           1 :     argObj.set(obj);
     634           1 :     return true;
     635             : }
     636             : 
     637             : /*
     638             :  * Calls to JS_TransplantObject* should go through these helpers here so that
     639             :  * waivers get fixed up properly.
     640             :  */
     641             : 
     642             : static bool
     643           0 : FixWaiverAfterTransplant(JSContext* cx, HandleObject oldWaiver, HandleObject newobj)
     644             : {
     645           0 :     MOZ_ASSERT(Wrapper::wrapperHandler(oldWaiver) == &XrayWaiver);
     646           0 :     MOZ_ASSERT(!js::IsCrossCompartmentWrapper(newobj));
     647             : 
     648             :     // Create a waiver in the new compartment. We know there's not one already
     649             :     // because we _just_ transplanted, which means that |newobj| was either
     650             :     // created from scratch, or was previously cross-compartment wrapper (which
     651             :     // should have no waiver). CreateXrayWaiver asserts this.
     652           0 :     JSObject* newWaiver = WrapperFactory::CreateXrayWaiver(cx, newobj);
     653           0 :     if (!newWaiver)
     654           0 :         return false;
     655             : 
     656             :     // Update all the cross-compartment references to oldWaiver to point to
     657             :     // newWaiver.
     658           0 :     if (!js::RemapAllWrappersForObject(cx, oldWaiver, newWaiver))
     659           0 :         return false;
     660             : 
     661             :     // There should be no same-compartment references to oldWaiver, and we
     662             :     // just remapped all cross-compartment references. It's dead, so we can
     663             :     // remove it from the map.
     664           0 :     XPCWrappedNativeScope* scope = ObjectScope(oldWaiver);
     665           0 :     JSObject* key = Wrapper::wrappedObject(oldWaiver);
     666           0 :     MOZ_ASSERT(scope->mWaiverWrapperMap->Find(key));
     667           0 :     scope->mWaiverWrapperMap->Remove(key);
     668           0 :     return true;
     669             : }
     670             : 
     671             : JSObject*
     672           2 : TransplantObject(JSContext* cx, JS::HandleObject origobj, JS::HandleObject target)
     673             : {
     674           4 :     RootedObject oldWaiver(cx, WrapperFactory::GetXrayWaiver(origobj));
     675           4 :     RootedObject newIdentity(cx, JS_TransplantObject(cx, origobj, target));
     676           2 :     if (!newIdentity || !oldWaiver)
     677           2 :        return newIdentity;
     678             : 
     679           0 :     if (!FixWaiverAfterTransplant(cx, oldWaiver, newIdentity))
     680           0 :         return nullptr;
     681           0 :     return newIdentity;
     682             : }
     683             : 
     684             : nsIGlobalObject*
     685        5141 : NativeGlobal(JSObject* obj)
     686             : {
     687        5141 :     obj = js::GetGlobalForObjectCrossCompartment(obj);
     688             : 
     689             :     // Every global needs to hold a native as its private or be a
     690             :     // WebIDL object with an nsISupports DOM object.
     691        5141 :     MOZ_ASSERT((GetObjectClass(obj)->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS |
     692             :                                              JSCLASS_HAS_PRIVATE)) ||
     693             :                dom::UnwrapDOMObjectToISupports(obj));
     694             : 
     695        5141 :     nsISupports* native = dom::UnwrapDOMObjectToISupports(obj);
     696        5141 :     if (!native) {
     697        4408 :         native = static_cast<nsISupports*>(js::GetObjectPrivate(obj));
     698        4408 :         MOZ_ASSERT(native);
     699             : 
     700             :         // In some cases (like for windows) it is a wrapped native,
     701             :         // in other cases (sandboxes, backstage passes) it's just
     702             :         // a direct pointer to the native. If it's a wrapped native
     703             :         // let's unwrap it first.
     704        8816 :         if (nsCOMPtr<nsIXPConnectWrappedNative> wn = do_QueryInterface(native)) {
     705        3919 :             native = wn->Native();
     706             :         }
     707             :     }
     708             : 
     709       10282 :     nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(native);
     710        5141 :     MOZ_ASSERT(global, "Native held by global needs to implement nsIGlobalObject!");
     711             : 
     712       10282 :     return global;
     713             : }
     714             : 
     715             : } // namespace xpc

Generated by: LCOV version 1.13