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

Generated by: LCOV version 1.13-14-ga5dd952