js/xpconnect/wrappers/WrapperFactory.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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/. */
     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"
    15 #include "xpcprivate.h"
    16 #include "XPCMaps.h"
    17 #include "mozilla/dom/BindingUtils.h"
    18 #include "jsfriendapi.h"
    19 #include "mozilla/Likely.h"
    20 #include "nsContentUtils.h"
    22 using namespace JS;
    23 using namespace js;
    24 using namespace mozilla;
    26 namespace xpc {
    28 // When chrome pulls a naked property across the membrane using
    29 // .wrappedJSObject, we want it to cross the membrane into the
    30 // chrome compartment without automatically being wrapped into an
    31 // X-ray wrapper. We achieve this by wrapping it into a special
    32 // transparent wrapper in the origin (non-chrome) compartment. When
    33 // an object with that special wrapper applied crosses into chrome,
    34 // we know to not apply an X-ray wrapper.
    35 Wrapper XrayWaiver(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG);
    37 // When objects for which we waived the X-ray wrapper cross into
    38 // chrome, we wrap them into a special cross-compartment wrapper
    39 // that transitively extends the waiver to all properties we get
    40 // off it.
    41 WaiveXrayWrapper WaiveXrayWrapper::singleton(0);
    43 bool
    44 WrapperFactory::IsCOW(JSObject *obj)
    45 {
    46     return IsWrapper(obj) &&
    47            Wrapper::wrapperHandler(obj) == &ChromeObjectWrapper::singleton;
    48 }
    50 JSObject *
    51 WrapperFactory::GetXrayWaiver(HandleObject obj)
    52 {
    53     // Object should come fully unwrapped but outerized.
    54     MOZ_ASSERT(obj == UncheckedUnwrap(obj));
    55     MOZ_ASSERT(!js::GetObjectClass(obj)->ext.outerObject);
    56     XPCWrappedNativeScope *scope = GetObjectScope(obj);
    57     MOZ_ASSERT(scope);
    59     if (!scope->mWaiverWrapperMap)
    60         return nullptr;
    62     JSObject* xrayWaiver = scope->mWaiverWrapperMap->Find(obj);
    63     if (xrayWaiver)
    64         JS::ExposeObjectToActiveJS(xrayWaiver);
    66     return xrayWaiver;
    67 }
    69 JSObject *
    70 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     MOZ_ASSERT(!GetXrayWaiver(obj));
    75     XPCWrappedNativeScope *scope = GetObjectScope(obj);
    77     JSAutoCompartment ac(cx, obj);
    78     JSObject *waiver = Wrapper::New(cx, obj,
    79                                     JS_GetGlobalForObject(cx, obj),
    80                                     &XrayWaiver);
    81     if (!waiver)
    82         return nullptr;
    84     // Add the new waiver to the map. It's important that we only ever have
    85     // one waiver for the lifetime of the target object.
    86     if (!scope->mWaiverWrapperMap) {
    87         scope->mWaiverWrapperMap =
    88           JSObject2JSObjectMap::newMap(XPC_WRAPPER_MAP_SIZE);
    89         MOZ_ASSERT(scope->mWaiverWrapperMap);
    90     }
    91     if (!scope->mWaiverWrapperMap->Add(cx, obj, waiver))
    92         return nullptr;
    93     return waiver;
    94 }
    96 JSObject *
    97 WrapperFactory::WaiveXray(JSContext *cx, JSObject *objArg)
    98 {
    99     RootedObject obj(cx, objArg);
   100     obj = UncheckedUnwrap(obj);
   101     MOZ_ASSERT(!js::IsInnerObject(obj));
   103     JSObject *waiver = GetXrayWaiver(obj);
   104     if (waiver)
   105         return waiver;
   106     return CreateXrayWaiver(cx, obj);
   107 }
   109 // DoubleWrap is called from PrepareForWrapping to maintain the state that
   110 // we're supposed to waive Xray wrappers for the given on. On entrance, it
   111 // expects |cx->compartment != obj->compartment()|. The returned object will
   112 // be in the same compartment as |obj|.
   113 JSObject *
   114 WrapperFactory::DoubleWrap(JSContext *cx, HandleObject obj, unsigned flags)
   115 {
   116     if (flags & WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG) {
   117         JSAutoCompartment ac(cx, obj);
   118         return WaiveXray(cx, obj);
   119     }
   120     return obj;
   121 }
   123 JSObject *
   124 WrapperFactory::PrepareForWrapping(JSContext *cx, HandleObject scope,
   125                                    HandleObject objArg, unsigned flags)
   126 {
   127     RootedObject obj(cx, objArg);
   128     // Outerize any raw inner objects at the entry point here, so that we don't
   129     // have to worry about them for the rest of the wrapping code.
   130     if (js::IsInnerObject(obj)) {
   131         JSAutoCompartment ac(cx, obj);
   132         obj = JS_ObjectToOuterObject(cx, obj);
   133         NS_ENSURE_TRUE(obj, nullptr);
   134         // The outerization hook wraps, which means that we can end up with a
   135         // CCW here if |obj| was a navigated-away-from inner. Strip any CCWs.
   136         obj = js::UncheckedUnwrap(obj);
   137         MOZ_ASSERT(js::IsOuterObject(obj));
   138     }
   140     // If we've got an outer window, there's nothing special that needs to be
   141     // done here, and we can move on to the next phase of wrapping. We handle
   142     // this case first to allow us to assert against wrappers below.
   143     if (js::IsOuterObject(obj))
   144         return DoubleWrap(cx, obj, flags);
   146     // Here are the rules for wrapping:
   147     // We should never get a proxy here (the JS engine unwraps those for us).
   148     MOZ_ASSERT(!IsWrapper(obj));
   150     // If the object being wrapped is a prototype for a standard class and the
   151     // wrapper does not subsumes the wrappee, use the one from the content
   152     // compartment. This is generally safer all-around, and in the COW case this
   153     // lets us safely take advantage of things like .forEach() via the
   154     // ChromeObjectWrapper machinery.
   155     //
   156     // If the prototype chain of chrome object |obj| looks like this:
   157     //
   158     // obj => foo => bar => chromeWin.StandardClass.prototype
   159     //
   160     // The prototype chain of COW(obj) looks lke this:
   161     //
   162     // COW(obj) => COW(foo) => COW(bar) => contentWin.StandardClass.prototype
   163     //
   164     // NB: We now remap all non-subsuming access of standard prototypes.
   165     //
   166     // NB: We need to ignore domain here so that the security relationship we
   167     // compute here can't change over time. See the comment above the other
   168     // subsumes call below.
   169     bool subsumes = AccessCheck::subsumes(js::GetContextCompartment(cx),
   170                                           js::GetObjectCompartment(obj));
   171     XrayType xrayType = GetXrayType(obj);
   172     if (!subsumes && xrayType == NotXray) {
   173         JSProtoKey key = JSProto_Null;
   174         {
   175             JSAutoCompartment ac(cx, obj);
   176             key = IdentifyStandardPrototype(obj);
   177         }
   178         if (key != JSProto_Null) {
   179             RootedObject homeProto(cx);
   180             if (!JS_GetClassPrototype(cx, key, &homeProto))
   181                 return nullptr;
   182             MOZ_ASSERT(homeProto);
   183             // No need to double-wrap here. We should never have waivers to
   184             // COWs.
   185             return homeProto;
   186         }
   187     }
   189     // Now, our object is ready to be wrapped, but several objects (notably
   190     // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of
   191     // those objects in a security wrapper, then we need to hand back the
   192     // wrapper for the new scope instead. Also, global objects don't move
   193     // between scopes so for those we also want to return the wrapper. So...
   194     if (!IS_WN_REFLECTOR(obj) || !js::GetObjectParent(obj))
   195         return DoubleWrap(cx, obj, flags);
   197     XPCWrappedNative *wn = XPCWrappedNative::Get(obj);
   199     JSAutoCompartment ac(cx, obj);
   200     XPCCallContext ccx(JS_CALLER, cx, obj);
   201     RootedObject wrapScope(cx, scope);
   203     {
   204         if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) {
   205             // We have a precreate hook. This object might enforce that we only
   206             // ever create JS object for it.
   208             // Note: this penalizes objects that only have one wrapper, but are
   209             // being accessed across compartments. We would really prefer to
   210             // replace the above code with a test that says "do you only have one
   211             // wrapper?"
   212             nsresult rv = wn->GetScriptableInfo()->GetCallback()->
   213                 PreCreate(wn->Native(), cx, scope, wrapScope.address());
   214             NS_ENSURE_SUCCESS(rv, DoubleWrap(cx, obj, flags));
   216             // If the handed back scope differs from the passed-in scope and is in
   217             // a separate compartment, then this object is explicitly requesting
   218             // that we don't create a second JS object for it: create a security
   219             // wrapper.
   220             if (js::GetObjectCompartment(scope) != js::GetObjectCompartment(wrapScope))
   221                 return DoubleWrap(cx, obj, flags);
   223             RootedObject currentScope(cx, JS_GetGlobalForObject(cx, obj));
   224             if (MOZ_UNLIKELY(wrapScope != currentScope)) {
   225                 // The wrapper claims it wants to be in the new scope, but
   226                 // currently has a reflection that lives in the old scope. This
   227                 // can mean one of two things, both of which are rare:
   228                 //
   229                 // 1 - The object has a PreCreate hook (we checked for it above),
   230                 // but is deciding to request one-wrapper-per-scope (rather than
   231                 // one-wrapper-per-native) for some reason. Usually, a PreCreate
   232                 // hook indicates one-wrapper-per-native. In this case we want to
   233                 // make a new wrapper in the new scope.
   234                 //
   235                 // 2 - We're midway through wrapper reparenting. The document has
   236                 // moved to a new scope, but |wn| hasn't been moved yet, and
   237                 // we ended up calling JS_WrapObject() on its JS object. In this
   238                 // case, we want to return the existing wrapper.
   239                 //
   240                 // So we do a trick: call PreCreate _again_, but say that we're
   241                 // wrapping for the old scope, rather than the new one. If (1) is
   242                 // the case, then PreCreate will return the scope we pass to it
   243                 // (the old scope). If (2) is the case, PreCreate will return the
   244                 // scope of the document (the new scope).
   245                 RootedObject probe(cx);
   246                 rv = wn->GetScriptableInfo()->GetCallback()->
   247                     PreCreate(wn->Native(), cx, currentScope, probe.address());
   249                 // Check for case (2).
   250                 if (probe != currentScope) {
   251                     MOZ_ASSERT(probe == wrapScope);
   252                     return DoubleWrap(cx, obj, flags);
   253                 }
   255                 // Ok, must be case (1). Fall through and create a new wrapper.
   256             }
   258             // Nasty hack for late-breaking bug 781476. This will confuse identity checks,
   259             // but it's probably better than any of our alternatives.
   260             //
   261             // Note: We have to ignore domain here. The JS engine assumes that, given a
   262             // compartment c, if c->wrap(x) returns a cross-compartment wrapper at time t0,
   263             // it will also return a cross-compartment wrapper for any time t1 > t0 unless
   264             // an explicit transplant is performed. In particular, wrapper recomputation
   265             // assumes that recomputing a wrapper will always result in a wrapper.
   266             //
   267             // This doesn't actually pose a security issue, because we'll still compute
   268             // the correct (opaque) wrapper for the object below given the security
   269             // characteristics of the two compartments.
   270             if (!AccessCheck::isChrome(js::GetObjectCompartment(wrapScope)) &&
   271                  AccessCheck::subsumes(js::GetObjectCompartment(wrapScope),
   272                                        js::GetObjectCompartment(obj)))
   273             {
   274                 return DoubleWrap(cx, obj, flags);
   275             }
   276         }
   277     }
   279     // This public WrapNativeToJSVal API enters the compartment of 'wrapScope'
   280     // so we don't have to.
   281     RootedValue v(cx);
   282     nsresult rv =
   283         nsXPConnect::XPConnect()->WrapNativeToJSVal(cx, wrapScope, wn->Native(), nullptr,
   284                                                     &NS_GET_IID(nsISupports), false, &v);
   285     NS_ENSURE_SUCCESS(rv, nullptr);
   287     obj.set(&v.toObject());
   288     MOZ_ASSERT(IS_WN_REFLECTOR(obj), "bad object");
   290     // Because the underlying native didn't have a PreCreate hook, we had
   291     // to a new (or possibly pre-existing) XPCWN in our compartment.
   292     // This could be a problem for chrome code that passes XPCOM objects
   293     // across compartments, because the effects of QI would disappear across
   294     // compartments.
   295     //
   296     // So whenever we pull an XPCWN across compartments in this manner, we
   297     // give the destination object the union of the two native sets. We try
   298     // to do this cleverly in the common case to avoid too much overhead.
   299     XPCWrappedNative *newwn = XPCWrappedNative::Get(obj);
   300     XPCNativeSet *unionSet = XPCNativeSet::GetNewOrUsed(newwn->GetSet(),
   301                                                         wn->GetSet(), false);
   302     if (!unionSet)
   303         return nullptr;
   304     newwn->SetSet(unionSet);
   306     return DoubleWrap(cx, obj, flags);
   307 }
   309 #ifdef DEBUG
   310 static void
   311 DEBUG_CheckUnwrapSafety(HandleObject obj, js::Wrapper *handler,
   312                         JSCompartment *origin, JSCompartment *target)
   313 {
   314     if (AccessCheck::isChrome(target) || xpc::IsUniversalXPConnectEnabled(target)) {
   315         // If the caller is chrome (or effectively so), unwrap should always be allowed.
   316         MOZ_ASSERT(!handler->hasSecurityPolicy());
   317     } else if (handler == &FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>::singleton) {
   318         // We explicitly use a SecurityWrapper to protect privileged callers from
   319         // less-privileged objects that they should never see. Skip the check in
   320         // this case.
   321     } else {
   322         // Otherwise, it should depend on whether the target subsumes the origin.
   323         MOZ_ASSERT(handler->hasSecurityPolicy() == !AccessCheck::subsumesConsideringDomain(target, origin));
   324     }
   325 }
   326 #else
   327 #define DEBUG_CheckUnwrapSafety(obj, handler, origin, target) {}
   328 #endif
   330 static Wrapper *
   331 SelectWrapper(bool securityWrapper, bool wantXrays, XrayType xrayType,
   332               bool waiveXrays, bool originIsXBLScope)
   333 {
   334     // Waived Xray uses a modified CCW that has transparent behavior but
   335     // transitively waives Xrays on arguments.
   336     if (waiveXrays) {
   337         MOZ_ASSERT(!securityWrapper);
   338         return &WaiveXrayWrapper::singleton;
   339     }
   341     // If we don't want or can't use Xrays, select a wrapper that's either
   342     // entirely transparent or entirely opaque.
   343     if (!wantXrays || xrayType == NotXray) {
   344         if (!securityWrapper)
   345             return &CrossCompartmentWrapper::singleton;
   346         // In general, we don't want opaque function wrappers to be callable.
   347         // But in the case of XBL, we rely on content being able to invoke
   348         // functions exposed from the XBL scope. We could remove this exception,
   349         // if needed, by using ExportFunction to generate the content-side
   350         // representations of XBL methods.
   351         else if (originIsXBLScope)
   352             return &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton;
   353         return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
   354     }
   356     // Ok, we're using Xray. If this isn't a security wrapper, use the permissive
   357     // version and skip the filter.
   358     if (!securityWrapper) {
   359         if (xrayType == XrayForWrappedNative)
   360             return &PermissiveXrayXPCWN::singleton;
   361         else if (xrayType == XrayForDOMObject)
   362             return &PermissiveXrayDOM::singleton;
   363         MOZ_ASSERT(xrayType == XrayForJSObject);
   364         return &PermissiveXrayJS::singleton;
   365     }
   367     // This is a security wrapper. Use the security versions and filter.
   368     if (xrayType == XrayForWrappedNative)
   369         return &FilteringWrapper<SecurityXrayXPCWN,
   370                                  CrossOriginAccessiblePropertiesOnly>::singleton;
   371     else if (xrayType == XrayForDOMObject)
   372         return &FilteringWrapper<SecurityXrayDOM,
   373                                  CrossOriginAccessiblePropertiesOnly>::singleton;
   374     // There's never any reason to expose pure JS objects to non-subsuming actors.
   375     // Just use an opaque wrapper in this case.
   376     MOZ_ASSERT(xrayType == XrayForJSObject);
   377     return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
   378 }
   380 JSObject *
   381 WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj,
   382                        HandleObject wrappedProto, HandleObject parent,
   383                        unsigned flags)
   384 {
   385     MOZ_ASSERT(!IsWrapper(obj) ||
   386                GetProxyHandler(obj) == &XrayWaiver ||
   387                js::GetObjectClass(obj)->ext.innerObject,
   388                "wrapped object passed to rewrap");
   389     MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj)), "trying to wrap a holder");
   390     MOZ_ASSERT(!js::IsInnerObject(obj));
   391     // We sometimes end up here after nsContentUtils has been shut down but before
   392     // XPConnect has been shut down, so check the context stack the roundabout way.
   393     MOZ_ASSERT(XPCJSRuntime::Get()->GetJSContextStack()->Peek() == cx);
   395     // Compute the information we need to select the right wrapper.
   396     JSCompartment *origin = js::GetObjectCompartment(obj);
   397     JSCompartment *target = js::GetContextCompartment(cx);
   398     bool originIsChrome = AccessCheck::isChrome(origin);
   399     bool targetIsChrome = AccessCheck::isChrome(target);
   400     bool originSubsumesTarget = AccessCheck::subsumesConsideringDomain(origin, target);
   401     bool targetSubsumesOrigin = AccessCheck::subsumesConsideringDomain(target, origin);
   402     bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget;
   403     XrayType xrayType = GetXrayType(obj);
   404     bool waiveXrayFlag = flags & WAIVE_XRAY_WRAPPER_FLAG;
   406     Wrapper *wrapper;
   407     CompartmentPrivate *targetdata = EnsureCompartmentPrivate(target);
   409     //
   410     // First, handle the special cases.
   411     //
   413     // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use
   414     // a vanilla CCW.
   415     if (xpc::IsUniversalXPConnectEnabled(target)) {
   416         wrapper = &CrossCompartmentWrapper::singleton;
   417     }
   419     // If this is a chrome object being exposed to content without Xrays, use
   420     // a COW.
   421     else if (originIsChrome && !targetIsChrome && xrayType == NotXray) {
   422         wrapper = &ChromeObjectWrapper::singleton;
   423     }
   425     // Normally, a non-xrayable non-waived content object that finds itself in
   426     // a privileged scope is wrapped with a CrossCompartmentWrapper, even though
   427     // the lack of a waiver _really_ should give it an opaque wrapper. This is
   428     // a bit too entrenched to change for content-chrome, but we can at least fix
   429     // it for XBL scopes.
   430     //
   431     // See bug 843829.
   432     else if (targetSubsumesOrigin && !originSubsumesTarget &&
   433              !waiveXrayFlag && xrayType == NotXray &&
   434              IsXBLScope(target))
   435     {
   436         wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>::singleton;
   437     }
   439     //
   440     // Now, handle the regular cases.
   441     //
   442     // These are wrappers we can compute using a rule-based approach. In order
   443     // to do so, we need to compute some parameters.
   444     //
   445     else {
   447         // The wrapper is a security wrapper (protecting the wrappee) if and
   448         // only if the target does not subsume the origin.
   449         bool securityWrapper = !targetSubsumesOrigin;
   451         // Xrays are warranted if either the target or the origin don't trust
   452         // each other. This is generally the case, unless the two are same-origin
   453         // and the caller has not requested same-origin Xrays.
   454         //
   455         // Xrays are a bidirectional protection, since it affords clarity to the
   456         // caller and privacy to the callee.
   457         bool wantXrays = !(sameOrigin && !targetdata->wantXrays);
   459         // If Xrays are warranted, the caller may waive them for non-security
   460         // wrappers.
   461         bool waiveXrays = wantXrays && !securityWrapper && waiveXrayFlag;
   463         // We have slightly different behavior for the case when the object
   464         // being wrapped is in an XBL scope.
   465         bool originIsXBLScope = IsXBLScope(origin);
   467         wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays,
   468                                 originIsXBLScope);
   469     }
   471     if (!targetSubsumesOrigin) {
   472         // Do a belt-and-suspenders check against exposing eval()/Function() to
   473         // non-subsuming content.
   474         JSFunction *fun = JS_GetObjectFunction(obj);
   475         if (fun) {
   476             if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) {
   477                 JS_ReportError(cx, "Permission denied to expose eval or Function to non-subsuming content");
   478                 return nullptr;
   479             }
   480         }
   481     }
   483     DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target);
   485     if (existing)
   486         return Wrapper::Renew(cx, existing, obj, wrapper);
   488     return Wrapper::New(cx, obj, parent, wrapper);
   489 }
   491 // Call WaiveXrayAndWrap when you have a JS object that you don't want to be
   492 // wrapped in an Xray wrapper. cx->compartment is the compartment that will be
   493 // using the returned object. If the object to be wrapped is already in the
   494 // correct compartment, then this returns the unwrapped object.
   495 bool
   496 WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleValue vp)
   497 {
   498     if (vp.isPrimitive())
   499         return JS_WrapValue(cx, vp);
   501     RootedObject obj(cx, &vp.toObject());
   502     if (!WaiveXrayAndWrap(cx, &obj))
   503         return false;
   505     vp.setObject(*obj);
   506     return true;
   507 }
   509 bool
   510 WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleObject argObj)
   511 {
   512     MOZ_ASSERT(argObj);
   513     RootedObject obj(cx, js::UncheckedUnwrap(argObj));
   514     MOZ_ASSERT(!js::IsInnerObject(obj));
   515     if (js::IsObjectInContextCompartment(obj, cx)) {
   516         argObj.set(obj);
   517         return true;
   518     }
   520     // Even though waivers have no effect on access by scopes that don't subsume
   521     // the underlying object, good defense-in-depth dictates that we should avoid
   522     // handing out waivers to callers that can't use them. The transitive waiving
   523     // machinery unconditionally calls WaiveXrayAndWrap on return values from
   524     // waived functions, even though the return value might be not be same-origin
   525     // with the function. So if we find ourselves trying to create a waiver for
   526     // |cx|, we should check whether the caller has any business with waivers
   527     // to things in |obj|'s compartment.
   528     JSCompartment *target = js::GetContextCompartment(cx);
   529     JSCompartment *origin = js::GetObjectCompartment(obj);
   530     obj = AccessCheck::subsumes(target, origin) ? WaiveXray(cx, obj) : obj;
   531     if (!obj)
   532         return false;
   534     if (!JS_WrapObject(cx, &obj))
   535         return false;
   536     argObj.set(obj);
   537     return true;
   538 }
   540 bool
   541 WrapperFactory::XrayWrapperNotShadowing(JSObject *wrapper, jsid id)
   542 {
   543     ResolvingId *rid = ResolvingId::getResolvingIdFromWrapper(wrapper);
   544     return rid->isXrayShadowing(id);
   545 }
   547 /*
   548  * Calls to JS_TransplantObject* should go through these helpers here so that
   549  * waivers get fixed up properly.
   550  */
   552 static bool
   553 FixWaiverAfterTransplant(JSContext *cx, HandleObject oldWaiver, HandleObject newobj)
   554 {
   555     MOZ_ASSERT(Wrapper::wrapperHandler(oldWaiver) == &XrayWaiver);
   556     MOZ_ASSERT(!js::IsCrossCompartmentWrapper(newobj));
   558     // Create a waiver in the new compartment. We know there's not one already
   559     // because we _just_ transplanted, which means that |newobj| was either
   560     // created from scratch, or was previously cross-compartment wrapper (which
   561     // should have no waiver). CreateXrayWaiver asserts this.
   562     JSObject *newWaiver = WrapperFactory::CreateXrayWaiver(cx, newobj);
   563     if (!newWaiver)
   564         return false;
   566     // Update all the cross-compartment references to oldWaiver to point to
   567     // newWaiver.
   568     if (!js::RemapAllWrappersForObject(cx, oldWaiver, newWaiver))
   569         return false;
   571     // There should be no same-compartment references to oldWaiver, and we
   572     // just remapped all cross-compartment references. It's dead, so we can
   573     // remove it from the map.
   574     XPCWrappedNativeScope *scope = GetObjectScope(oldWaiver);
   575     JSObject *key = Wrapper::wrappedObject(oldWaiver);
   576     MOZ_ASSERT(scope->mWaiverWrapperMap->Find(key));
   577     scope->mWaiverWrapperMap->Remove(key);
   578     return true;
   579 }
   581 JSObject *
   582 TransplantObject(JSContext *cx, JS::HandleObject origobj, JS::HandleObject target)
   583 {
   584     RootedObject oldWaiver(cx, WrapperFactory::GetXrayWaiver(origobj));
   585     RootedObject newIdentity(cx, JS_TransplantObject(cx, origobj, target));
   586     if (!newIdentity || !oldWaiver)
   587        return newIdentity;
   589     if (!FixWaiverAfterTransplant(cx, oldWaiver, newIdentity))
   590         return nullptr;
   591     return newIdentity;
   592 }
   594 nsIGlobalObject *
   595 GetNativeForGlobal(JSObject *obj)
   596 {
   597     MOZ_ASSERT(JS_IsGlobalObject(obj));
   598     if (!MaybeGetObjectScope(obj))
   599         return nullptr;
   601     // Every global needs to hold a native as its private or be a
   602     // WebIDL object with an nsISupports DOM object.
   603     MOZ_ASSERT((GetObjectClass(obj)->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS |
   604                                              JSCLASS_HAS_PRIVATE)) ||
   605                dom::UnwrapDOMObjectToISupports(obj));
   607     nsISupports *native = dom::UnwrapDOMObjectToISupports(obj);
   608     if (!native) {
   609         native = static_cast<nsISupports *>(js::GetObjectPrivate(obj));
   610         MOZ_ASSERT(native);
   612         // In some cases (like for windows) it is a wrapped native,
   613         // in other cases (sandboxes, backstage passes) it's just
   614         // a direct pointer to the native. If it's a wrapped native
   615         // let's unwrap it first.
   616         if (nsCOMPtr<nsIXPConnectWrappedNative> wn = do_QueryInterface(native)) {
   617             native = wn->Native();
   618         }
   619     }
   621     nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(native);
   622     MOZ_ASSERT(global, "Native held by global needs to implement nsIGlobalObject!");
   624     return global;
   625 }
   627 }

mercurial