LCOV - code coverage report
Current view: top level - js/src/proxy - CrossCompartmentWrapper.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 105 278 37.8 %
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 "gc/PublicIterators.h"
       8             : #include "js/Wrapper.h"
       9             : #include "proxy/DeadObjectProxy.h"
      10             : #include "vm/Iteration.h"
      11             : #include "vm/WrapperObject.h"
      12             : 
      13             : #include "gc/Nursery-inl.h"
      14             : #include "vm/Compartment-inl.h"
      15             : #include "vm/JSObject-inl.h"
      16             : #include "vm/Realm-inl.h"
      17             : 
      18             : using namespace js;
      19             : 
      20             : #define PIERCE(cx, wrapper, pre, op, post)                      \
      21             :     JS_BEGIN_MACRO                                              \
      22             :         bool ok;                                                \
      23             :         {                                                       \
      24             :             AutoRealm call(cx, wrappedObject(wrapper));         \
      25             :             ok = (pre) && (op);                                 \
      26             :         }                                                       \
      27             :         return ok && (post);                                    \
      28             :     JS_END_MACRO
      29             : 
      30             : #define NOTHING (true)
      31             : 
      32             : static bool
      33             : MarkAtoms(JSContext* cx, jsid id)
      34             : {
      35       29425 :     cx->markId(id);
      36             :     return true;
      37             : }
      38             : 
      39             : static bool
      40         185 : MarkAtoms(JSContext* cx, const AutoIdVector& ids)
      41             : {
      42           0 :     for (size_t i = 0; i < ids.length(); i++)
      43           0 :         cx->markId(ids[i]);
      44         185 :     return true;
      45             : }
      46             : 
      47             : bool
      48           0 : CrossCompartmentWrapper::getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
      49             :                                                MutableHandle<PropertyDescriptor> desc) const
      50             : {
      51           0 :     PIERCE(cx, wrapper,
      52             :            MarkAtoms(cx, id),
      53             :            Wrapper::getPropertyDescriptor(cx, wrapper, id, desc),
      54             :            cx->compartment()->wrap(cx, desc));
      55             : }
      56             : 
      57             : bool
      58         233 : CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
      59             :                                                   MutableHandle<PropertyDescriptor> desc) const
      60             : {
      61        1398 :     PIERCE(cx, wrapper,
      62             :            MarkAtoms(cx, id),
      63             :            Wrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc),
      64             :            cx->compartment()->wrap(cx, desc));
      65             : }
      66             : 
      67             : bool
      68         497 : CrossCompartmentWrapper::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
      69             :                                         Handle<PropertyDescriptor> desc,
      70             :                                         ObjectOpResult& result) const
      71             : {
      72           0 :     Rooted<PropertyDescriptor> desc2(cx, desc);
      73        4473 :     PIERCE(cx, wrapper,
      74             :            MarkAtoms(cx, id) && cx->compartment()->wrap(cx, &desc2),
      75             :            Wrapper::defineProperty(cx, wrapper, id, desc2, result),
      76             :            NOTHING);
      77             : }
      78             : 
      79             : bool
      80          22 : CrossCompartmentWrapper::ownPropertyKeys(JSContext* cx, HandleObject wrapper,
      81             :                                          AutoIdVector& props) const
      82             : {
      83          66 :     PIERCE(cx, wrapper,
      84             :            NOTHING,
      85             :            Wrapper::ownPropertyKeys(cx, wrapper, props),
      86             :            MarkAtoms(cx, props));
      87             : }
      88             : 
      89             : bool
      90          14 : CrossCompartmentWrapper::delete_(JSContext* cx, HandleObject wrapper, HandleId id,
      91             :                                  ObjectOpResult& result) const
      92             : {
      93          70 :     PIERCE(cx, wrapper,
      94             :            MarkAtoms(cx, id),
      95             :            Wrapper::delete_(cx, wrapper, id, result),
      96             :            NOTHING);
      97             : }
      98             : 
      99             : bool
     100           0 : CrossCompartmentWrapper::getPrototype(JSContext* cx, HandleObject wrapper,
     101             :                                       MutableHandleObject protop) const
     102             : {
     103             :     {
     104           0 :         RootedObject wrapped(cx, wrappedObject(wrapper));
     105           0 :         AutoRealm call(cx, wrapped);
     106           0 :         if (!GetPrototype(cx, wrapped, protop))
     107           0 :             return false;
     108           0 :         if (protop) {
     109           0 :             if (!JSObject::setDelegate(cx, protop))
     110             :                 return false;
     111             :         }
     112             :     }
     113             : 
     114           0 :     return cx->compartment()->wrap(cx, protop);
     115             : }
     116             : 
     117             : bool
     118           0 : CrossCompartmentWrapper::setPrototype(JSContext* cx, HandleObject wrapper,
     119             :                                       HandleObject proto, ObjectOpResult& result) const
     120             : {
     121           0 :     RootedObject protoCopy(cx, proto);
     122           0 :     PIERCE(cx, wrapper,
     123             :            cx->compartment()->wrap(cx, &protoCopy),
     124             :            Wrapper::setPrototype(cx, wrapper, protoCopy, result),
     125             :            NOTHING);
     126             : }
     127             : 
     128             : bool
     129           2 : CrossCompartmentWrapper::getPrototypeIfOrdinary(JSContext* cx, HandleObject wrapper,
     130             :                                                 bool* isOrdinary, MutableHandleObject protop) const
     131             : {
     132             :     {
     133           0 :         RootedObject wrapped(cx, wrappedObject(wrapper));
     134           0 :         AutoRealm call(cx, wrapped);
     135           0 :         if (!GetPrototypeIfOrdinary(cx, wrapped, isOrdinary, protop))
     136           0 :             return false;
     137             : 
     138           2 :         if (!*isOrdinary)
     139             :             return true;
     140             : 
     141           0 :         if (protop) {
     142           2 :             if (!JSObject::setDelegate(cx, protop))
     143             :                 return false;
     144             :         }
     145             :     }
     146             : 
     147           4 :     return cx->compartment()->wrap(cx, protop);
     148             : }
     149             : 
     150             : bool
     151           0 : CrossCompartmentWrapper::setImmutablePrototype(JSContext* cx, HandleObject wrapper, bool* succeeded) const
     152             : {
     153           0 :     PIERCE(cx, wrapper,
     154             :            NOTHING,
     155             :            Wrapper::setImmutablePrototype(cx, wrapper, succeeded),
     156             :            NOTHING);
     157             : }
     158             : 
     159             : bool
     160           0 : CrossCompartmentWrapper::preventExtensions(JSContext* cx, HandleObject wrapper,
     161             :                                            ObjectOpResult& result) const
     162             : {
     163           0 :     PIERCE(cx, wrapper,
     164             :            NOTHING,
     165             :            Wrapper::preventExtensions(cx, wrapper, result),
     166             :            NOTHING);
     167             : }
     168             : 
     169             : bool
     170           0 : CrossCompartmentWrapper::isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const
     171             : {
     172           0 :     PIERCE(cx, wrapper,
     173             :            NOTHING,
     174             :            Wrapper::isExtensible(cx, wrapper, extensible),
     175             :            NOTHING);
     176             : }
     177             : 
     178             : bool
     179          24 : CrossCompartmentWrapper::has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
     180             : {
     181         120 :     PIERCE(cx, wrapper,
     182             :            MarkAtoms(cx, id),
     183             :            Wrapper::has(cx, wrapper, id, bp),
     184             :            NOTHING);
     185             : }
     186             : 
     187             : bool
     188         274 : CrossCompartmentWrapper::hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
     189             : {
     190        1370 :     PIERCE(cx, wrapper,
     191             :            MarkAtoms(cx, id),
     192             :            Wrapper::hasOwn(cx, wrapper, id, bp),
     193             :            NOTHING);
     194             : }
     195             : 
     196             : static bool
     197       28383 : WrapReceiver(JSContext* cx, HandleObject wrapper, MutableHandleValue receiver)
     198             : {
     199             :     // Usually the receiver is the wrapper and we can just unwrap it. If the
     200             :     // wrapped object is also a wrapper, things are more complicated and we
     201             :     // fall back to the slow path (it calls UncheckedUnwrap to unwrap all
     202             :     // wrappers).
     203           0 :     if (ObjectValue(*wrapper) == receiver) {
     204           0 :         JSObject* wrapped = Wrapper::wrappedObject(wrapper);
     205           0 :         if (!IsWrapper(wrapped)) {
     206           0 :             MOZ_ASSERT(wrapped->compartment() == cx->compartment());
     207           0 :             MOZ_ASSERT(!IsWindow(wrapped));
     208           0 :             receiver.setObject(*wrapped);
     209       27936 :             return true;
     210             :         }
     211             :     }
     212             : 
     213         447 :     return cx->compartment()->wrap(cx, receiver);
     214             : }
     215             : 
     216             : bool
     217       27975 : CrossCompartmentWrapper::get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
     218             :                              HandleId id, MutableHandleValue vp) const
     219             : {
     220       55950 :     RootedValue receiverCopy(cx, receiver);
     221             :     {
     222           0 :         AutoRealm call(cx, wrappedObject(wrapper));
     223           0 :         if (!MarkAtoms(cx, id) || !WrapReceiver(cx, wrapper, &receiverCopy))
     224           0 :             return false;
     225             : 
     226       27975 :         if (!Wrapper::get(cx, wrapper, receiverCopy, id, vp))
     227             :             return false;
     228             :     }
     229       55950 :     return cx->compartment()->wrap(cx, vp);
     230             : }
     231             : 
     232             : bool
     233         408 : CrossCompartmentWrapper::set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
     234             :                              HandleValue receiver, ObjectOpResult& result) const
     235             : {
     236           0 :     RootedValue valCopy(cx, v);
     237           0 :     RootedValue receiverCopy(cx, receiver);
     238        4488 :     PIERCE(cx, wrapper,
     239             :            MarkAtoms(cx, id) &&
     240             :            cx->compartment()->wrap(cx, &valCopy) &&
     241             :            WrapReceiver(cx, wrapper, &receiverCopy),
     242             :            Wrapper::set(cx, wrapper, id, valCopy, receiverCopy, result),
     243             :            NOTHING);
     244             : }
     245             : 
     246             : bool
     247         163 : CrossCompartmentWrapper::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
     248             :                                                       AutoIdVector& props) const
     249             : {
     250         489 :     PIERCE(cx, wrapper,
     251             :            NOTHING,
     252             :            Wrapper::getOwnEnumerablePropertyKeys(cx, wrapper, props),
     253             :            MarkAtoms(cx, props));
     254             : }
     255             : 
     256             : /*
     257             :  * We can reify non-escaping iterator objects instead of having to wrap them. This
     258             :  * allows fast iteration over objects across a compartment boundary.
     259             :  */
     260             : static bool
     261             : CanReify(HandleObject obj)
     262             : {
     263          48 :     return obj->is<PropertyIteratorObject>();
     264             : }
     265             : 
     266             : struct AutoCloseIterator
     267             : {
     268          24 :     AutoCloseIterator(JSContext* cx, PropertyIteratorObject* obj) : obj(cx, obj) {}
     269             : 
     270           0 :     ~AutoCloseIterator() {
     271           1 :         if (obj)
     272           0 :             CloseIterator(obj);
     273          24 :     }
     274             : 
     275          48 :     void clear() { obj = nullptr; }
     276             : 
     277             :   private:
     278             :     Rooted<PropertyIteratorObject*> obj;
     279             : };
     280             : 
     281             : static JSObject*
     282          24 : Reify(JSContext* cx, JS::Compartment* origin, HandleObject objp)
     283             : {
     284           0 :     Rooted<PropertyIteratorObject*> iterObj(cx, &objp->as<PropertyIteratorObject>());
     285          48 :     NativeIterator* ni = iterObj->getNativeIterator();
     286             : 
     287          48 :     RootedObject obj(cx, ni->objectBeingIterated());
     288             :     {
     289          72 :         AutoCloseIterator close(cx, iterObj);
     290             : 
     291             :         /* Wrap the iteratee. */
     292           1 :         if (!origin->wrap(cx, &obj))
     293           0 :             return nullptr;
     294             : 
     295             :         /*
     296             :          * Wrap the elements in the iterator's snapshot.
     297             :          * N.B. the order of closing/creating iterators is important due to the
     298             :          * implicit cx->enumerators state.
     299             :          */
     300           0 :         size_t length = ni->numKeys();
     301           0 :         AutoIdVector keys(cx);
     302           0 :         if (length > 0) {
     303           1 :             if (!keys.reserve(length))
     304           0 :                 return nullptr;
     305           0 :             RootedId id(cx);
     306           0 :             RootedValue v(cx);
     307           0 :             for (size_t i = 0; i < length; ++i) {
     308           0 :                 v.setString(ni->propertiesBegin()[i]);
     309           1 :                 if (!ValueToId<CanGC>(cx, v, &id))
     310           0 :                     return nullptr;
     311           0 :                 cx->markId(id);
     312         117 :                 keys.infallibleAppend(id);
     313             :             }
     314             :         }
     315             : 
     316           0 :         close.clear();
     317          24 :         CloseIterator(iterObj);
     318             : 
     319          48 :         obj = EnumeratedIdVectorToIterator(cx, obj, keys);
     320             :     }
     321          24 :     return obj;
     322             : }
     323             : 
     324             : JSObject*
     325          24 : CrossCompartmentWrapper::enumerate(JSContext* cx, HandleObject wrapper) const
     326             : {
     327          48 :     RootedObject res(cx);
     328             :     {
     329           0 :         AutoRealm call(cx, wrappedObject(wrapper));
     330           0 :         res = Wrapper::enumerate(cx, wrapper);
     331           1 :         if (!res)
     332           0 :             return nullptr;
     333             :     }
     334             : 
     335           0 :     if (CanReify(res))
     336           1 :         return Reify(cx, cx->compartment(), res);
     337           0 :     if (!cx->compartment()->wrap(cx, &res))
     338             :         return nullptr;
     339           0 :     return res;
     340             : }
     341             : 
     342             : bool
     343       15158 : CrossCompartmentWrapper::call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const
     344             : {
     345       30316 :     RootedObject wrapped(cx, wrappedObject(wrapper));
     346             : 
     347             :     {
     348       45474 :         AutoRealm call(cx, wrapped);
     349             : 
     350           0 :         args.setCallee(ObjectValue(*wrapped));
     351           0 :         if (!cx->compartment()->wrap(cx, args.mutableThisv()))
     352           0 :             return false;
     353             : 
     354           0 :         for (size_t n = 0; n < args.length(); ++n) {
     355       31334 :             if (!cx->compartment()->wrap(cx, args[n]))
     356             :                 return false;
     357             :         }
     358             : 
     359       15158 :         if (!Wrapper::call(cx, wrapper, args))
     360             :             return false;
     361             :     }
     362             : 
     363       45474 :     return cx->compartment()->wrap(cx, args.rval());
     364             : }
     365             : 
     366             : bool
     367          52 : CrossCompartmentWrapper::construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const
     368             : {
     369         104 :     RootedObject wrapped(cx, wrappedObject(wrapper));
     370             :     {
     371         156 :         AutoRealm call(cx, wrapped);
     372             : 
     373           0 :         for (size_t n = 0; n < args.length(); ++n) {
     374           0 :             if (!cx->compartment()->wrap(cx, args[n]))
     375           0 :                 return false;
     376             :         }
     377         104 :         if (!cx->compartment()->wrap(cx, args.newTarget()))
     378             :             return false;
     379          52 :         if (!Wrapper::construct(cx, wrapper, args))
     380             :             return false;
     381             :     }
     382         156 :     return cx->compartment()->wrap(cx, args.rval());
     383             : }
     384             : 
     385             : bool
     386           0 : CrossCompartmentWrapper::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
     387             :                                     const CallArgs& srcArgs) const
     388             : {
     389           0 :     RootedObject wrapper(cx, &srcArgs.thisv().toObject());
     390           0 :     MOZ_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) ||
     391             :                !UncheckedUnwrap(wrapper)->is<CrossCompartmentWrapperObject>());
     392             : 
     393           0 :     RootedObject wrapped(cx, wrappedObject(wrapper));
     394             :     {
     395           0 :         AutoRealm call(cx, wrapped);
     396           0 :         InvokeArgs dstArgs(cx);
     397           0 :         if (!dstArgs.init(cx, srcArgs.length()))
     398           0 :             return false;
     399             : 
     400           0 :         Value* src = srcArgs.base();
     401           0 :         Value* srcend = srcArgs.array() + srcArgs.length();
     402           0 :         Value* dst = dstArgs.base();
     403             : 
     404           0 :         RootedValue source(cx);
     405           0 :         for (; src < srcend; ++src, ++dst) {
     406           0 :             source = *src;
     407           0 :             if (!cx->compartment()->wrap(cx, &source))
     408           0 :                 return false;
     409           0 :             *dst = source.get();
     410             : 
     411             :             // Handle |this| specially. When we rewrap on the other side of the
     412             :             // membrane, we might apply a same-compartment security wrapper that
     413             :             // will stymie this whole process. If that happens, unwrap the wrapper.
     414             :             // This logic can go away when same-compartment security wrappers go away.
     415           0 :             if ((src == srcArgs.base() + 1) && dst->isObject()) {
     416           0 :                 RootedObject thisObj(cx, &dst->toObject());
     417           0 :                 if (thisObj->is<WrapperObject>() &&
     418           0 :                     Wrapper::wrapperHandler(thisObj)->hasSecurityPolicy())
     419             :                 {
     420           0 :                     MOZ_ASSERT(!thisObj->is<CrossCompartmentWrapperObject>());
     421           0 :                     *dst = ObjectValue(*Wrapper::wrappedObject(thisObj));
     422             :                 }
     423             :             }
     424             :         }
     425             : 
     426           0 :         if (!CallNonGenericMethod(cx, test, impl, dstArgs))
     427             :             return false;
     428             : 
     429           0 :         srcArgs.rval().set(dstArgs.rval());
     430             :     }
     431           0 :     return cx->compartment()->wrap(cx, srcArgs.rval());
     432             : }
     433             : 
     434             : bool
     435           0 : CrossCompartmentWrapper::hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
     436             :                                      bool* bp) const
     437             : {
     438           0 :     AutoRealm call(cx, wrappedObject(wrapper));
     439           0 :     if (!cx->compartment()->wrap(cx, v))
     440             :         return false;
     441           0 :     return Wrapper::hasInstance(cx, wrapper, v, bp);
     442             : }
     443             : 
     444             : const char*
     445           0 : CrossCompartmentWrapper::className(JSContext* cx, HandleObject wrapper) const
     446             : {
     447           0 :     AutoRealm call(cx, wrappedObject(wrapper));
     448           0 :     return Wrapper::className(cx, wrapper);
     449             : }
     450             : 
     451             : JSString*
     452           0 : CrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject wrapper, bool isToSource) const
     453             : {
     454           0 :     RootedString str(cx);
     455             :     {
     456           0 :         AutoRealm call(cx, wrappedObject(wrapper));
     457           0 :         str = Wrapper::fun_toString(cx, wrapper, isToSource);
     458           0 :         if (!str)
     459           0 :             return nullptr;
     460             :     }
     461           0 :     if (!cx->compartment()->wrap(cx, &str))
     462             :         return nullptr;
     463           0 :     return str;
     464             : }
     465             : 
     466             : RegExpShared*
     467           0 : CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper) const
     468             : {
     469           0 :     RootedRegExpShared re(cx);
     470             :     {
     471           0 :         AutoRealm call(cx, wrappedObject(wrapper));
     472           0 :         re = Wrapper::regexp_toShared(cx, wrapper);
     473           0 :         if (!re)
     474           0 :             return nullptr;
     475             :     }
     476             : 
     477             :     // Get an equivalent RegExpShared associated with the current compartment.
     478           0 :     RootedAtom source(cx, re->getSource());
     479           0 :     cx->markAtom(source);
     480           0 :     return cx->zone()->regExps.get(cx, source, re->getFlags());
     481             : }
     482             : 
     483             : bool
     484           0 : CrossCompartmentWrapper::boxedValue_unbox(JSContext* cx, HandleObject wrapper, MutableHandleValue vp) const
     485             : {
     486           0 :     PIERCE(cx, wrapper,
     487             :            NOTHING,
     488             :            Wrapper::boxedValue_unbox(cx, wrapper, vp),
     489             :            cx->compartment()->wrap(cx, vp));
     490             : }
     491             : 
     492             : const CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
     493             : 
     494             : bool
     495       40259 : js::IsCrossCompartmentWrapper(const JSObject* obj)
     496             : {
     497           0 :     return IsWrapper(obj) &&
     498       64543 :            !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
     499             : }
     500             : 
     501             : static void
     502          43 : NukeRemovedCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
     503             : {
     504          43 :     MOZ_ASSERT(wrapper->is<CrossCompartmentWrapperObject>());
     505             : 
     506          43 :     NotifyGCNukeWrapper(wrapper);
     507             : 
     508          43 :     wrapper->as<ProxyObject>().nuke();
     509             : 
     510           0 :     MOZ_ASSERT(IsDeadProxyObject(wrapper));
     511          43 : }
     512             : 
     513             : JS_FRIEND_API(void)
     514          12 : js::NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
     515             : {
     516           0 :     JS::Compartment* comp = wrapper->compartment();
     517           0 :     auto ptr = comp->lookupWrapper(Wrapper::wrappedObject(wrapper));
     518          12 :     if (ptr)
     519             :         comp->removeWrapper(ptr);
     520           0 :     NukeRemovedCrossCompartmentWrapper(cx, wrapper);
     521          12 : }
     522             : 
     523             : /*
     524             :  * NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts
     525             :  * all of the cross-compartment wrappers that point to objects parented to
     526             :  * obj's global.  The snag here is that we need to avoid cutting wrappers that
     527             :  * point to the window object on page navigation (inner window destruction)
     528             :  * and only do that on tab close (outer window destruction).  Thus the
     529             :  * option of how to handle the global object.
     530             :  */
     531             : JS_FRIEND_API(bool)
     532           5 : js::NukeCrossCompartmentWrappers(JSContext* cx,
     533             :                                  const CompartmentFilter& sourceFilter,
     534             :                                  JS::Compartment* target,
     535             :                                  js::NukeReferencesToWindow nukeReferencesToWindow,
     536             :                                  js::NukeReferencesFromTarget nukeReferencesFromTarget)
     537             : {
     538           0 :     CHECK_REQUEST(cx);
     539           5 :     JSRuntime* rt = cx->runtime();
     540             : 
     541           0 :     for (CompartmentsIter c(rt); !c.done(); c.next()) {
     542           0 :         if (!sourceFilter.match(c))
     543          46 :             continue;
     544             : 
     545             :         // If the compartment matches both the source and target filter, we may
     546             :         // want to cut both incoming and outgoing wrappers.
     547           0 :         bool nukeAll = (nukeReferencesFromTarget == NukeAllReferences &&
     548         125 :                         target == c.get());
     549             : 
     550             :         // Iterate only the wrappers that have target compartment matched unless
     551             :         // |nukeAll| is true. The string wrappers that we're not interested in
     552             :         // won't be iterated, we can exclude them easily because they have
     553             :         // compartment nullptr. Use Maybe to avoid copying from conditionally
     554             :         // initializing NonStringWrapperEnum.
     555           0 :         mozilla::Maybe<Compartment::NonStringWrapperEnum> e;
     556           0 :         if (MOZ_LIKELY(!nukeAll))
     557         125 :             e.emplace(c, target);
     558             :         else
     559           0 :             e.emplace(c);
     560         193 :         for (; !e->empty(); e->popFront()) {
     561             :             // Skip debugger references because NukeCrossCompartmentWrapper()
     562             :             // doesn't know how to nuke them yet, see bug 1084626 for more
     563             :             // information.
     564           0 :             const CrossCompartmentKey& k = e->front().key();
     565           0 :             if (!k.is<JSObject*>())
     566           3 :                 continue;
     567             : 
     568         133 :             AutoWrapperRooter wobj(cx, WrapperValue(*e));
     569             : 
     570             :             // Unwrap from the wrapped object in CrossCompartmentKey instead of
     571             :             // the wrapper, this could save us a bit of time.
     572          34 :             JSObject* wrapped = UncheckedUnwrap(k.as<JSObject*>());
     573             : 
     574             :             // We never nuke script source objects, since only ever used internally by the JS
     575             :             // engine, and are expected to remain valid throughout a scripts lifetime.
     576          34 :             if (MOZ_UNLIKELY(wrapped->is<ScriptSourceObject>())) {
     577             :                 continue;
     578             :             }
     579             : 
     580             :             // We only skip nuking window references that point to a target
     581             :             // compartment, not the ones that belong to it.
     582           0 :             if (nukeReferencesToWindow == DontNukeWindowReferences &&
     583          65 :                 MOZ_LIKELY(!nukeAll) && IsWindowProxy(wrapped))
     584             :             {
     585             :                 continue;
     586             :             }
     587             : 
     588             :             // Now this is the wrapper we want to nuke.
     589           0 :             e->removeFront();
     590          31 :             NukeRemovedCrossCompartmentWrapper(cx, wobj);
     591             :         }
     592             :     }
     593             : 
     594           5 :     return true;
     595             : }
     596             : 
     597             : // Given a cross-compartment wrapper |wobj|, update it to point to
     598             : // |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
     599             : // useful even if wrapper already points to newTarget.
     600             : // This operation crashes on failure rather than leaving the heap in an
     601             : // inconsistent state.
     602             : void
     603          12 : js::RemapWrapper(JSContext* cx, JSObject* wobjArg, JSObject* newTargetArg)
     604             : {
     605           0 :     MOZ_ASSERT(!IsInsideNursery(wobjArg));
     606          12 :     MOZ_ASSERT(!IsInsideNursery(newTargetArg));
     607             : 
     608           0 :     RootedObject wobj(cx, wobjArg);
     609           0 :     RootedObject newTarget(cx, newTargetArg);
     610           0 :     MOZ_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
     611           0 :     MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
     612           0 :     JSObject* origTarget = Wrapper::wrappedObject(wobj);
     613           0 :     MOZ_ASSERT(origTarget);
     614          12 :     MOZ_ASSERT(!JS_IsDeadWrapper(origTarget),
     615             :                "We don't want a dead proxy in the wrapper map");
     616           0 :     Value origv = ObjectValue(*origTarget);
     617           0 :     Realm* wrealm = wobj->realm();
     618          24 :     JS::Compartment* wcompartment = wobj->compartment();
     619             : 
     620          24 :     AutoDisableProxyCheck adpc;
     621             : 
     622             :     // If we're mapping to a different target (as opposed to just recomputing
     623             :     // for the same target), we must not have an existing wrapper for the new
     624             :     // target, otherwise this will break.
     625          57 :     MOZ_ASSERT_IF(origTarget != newTarget,
     626             :                   !wcompartment->lookupWrapper(ObjectValue(*newTarget)));
     627             : 
     628             :     // The old value should still be in the cross-compartment wrapper map, and
     629             :     // the lookup should return wobj.
     630           0 :     WrapperMap::Ptr p = wcompartment->lookupWrapper(origv);
     631           0 :     MOZ_ASSERT(&p->value().unsafeGet()->toObject() == wobj);
     632          12 :     wcompartment->removeWrapper(p);
     633             : 
     634             :     // When we remove origv from the wrapper map, its wrapper, wobj, must
     635             :     // immediately cease to be a cross-compartment wrapper. Nuke it.
     636          12 :     NukeCrossCompartmentWrapper(cx, wobj);
     637             : 
     638             :     // First, we wrap it in the new compartment. We try to use the existing
     639             :     // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has
     640             :     // the choice to reuse |wobj| or not.
     641           0 :     RootedObject tobj(cx, newTarget);
     642           0 :     AutoRealmUnchecked ar(cx, wrealm);
     643           1 :     if (!wcompartment->rewrap(cx, &tobj, wobj))
     644           0 :         MOZ_CRASH();
     645             : 
     646             :     // If wrap() reused |wobj|, it will have overwritten it and returned with
     647             :     // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
     648             :     // will still be nuked. In the latter case, we replace |wobj| with the
     649             :     // contents of the new wrapper in |tobj|.
     650          12 :     if (tobj != wobj) {
     651             :         // Now, because we need to maintain object identity, we do a brain
     652             :         // transplant on the old object so that it contains the contents of the
     653             :         // new one.
     654           0 :         if (!JSObject::swap(cx, wobj, tobj))
     655           0 :             MOZ_CRASH();
     656             :     }
     657             : 
     658             :     // Before swapping, this wrapper came out of wrap(), which enforces the
     659             :     // invariant that the wrapper in the map points directly to the key.
     660          24 :     MOZ_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);
     661             : 
     662             :     // Update the entry in the compartment's wrapper map to point to the old
     663             :     // wrapper, which has now been updated (via reuse or swap).
     664           0 :     MOZ_ASSERT(wobj->is<WrapperObject>());
     665           1 :     if (!wcompartment->putWrapper(cx, CrossCompartmentKey(newTarget), ObjectValue(*wobj)))
     666           0 :         MOZ_CRASH();
     667          12 : }
     668             : 
     669             : // Remap all cross-compartment wrappers pointing to |oldTarget| to point to
     670             : // |newTarget|. All wrappers are recomputed.
     671             : JS_FRIEND_API(bool)
     672           7 : js::RemapAllWrappersForObject(JSContext* cx, JSObject* oldTargetArg,
     673             :                               JSObject* newTargetArg)
     674             : {
     675           0 :     MOZ_ASSERT(!IsInsideNursery(oldTargetArg));
     676           7 :     MOZ_ASSERT(!IsInsideNursery(newTargetArg));
     677             : 
     678           0 :     RootedValue origv(cx, ObjectValue(*oldTargetArg));
     679          14 :     RootedObject newTarget(cx, newTargetArg);
     680             : 
     681          21 :     AutoWrapperVector toTransplant(cx);
     682             : 
     683           0 :     for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
     684         609 :         if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) {
     685             :             // We found a wrapper. Remember and root it.
     686           1 :             if (!toTransplant.append(WrapperValue(wp)))
     687           0 :                 return false;
     688             :         }
     689             :     }
     690             : 
     691           0 :     for (const WrapperValue& v : toTransplant)
     692          24 :         RemapWrapper(cx, &v.toObject(), newTarget);
     693             : 
     694             :     return true;
     695             : }
     696             : 
     697             : JS_FRIEND_API(bool)
     698           0 : js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
     699             :                       const CompartmentFilter& targetFilter)
     700             : {
     701           0 :     bool evictedNursery = false;
     702             : 
     703           0 :     AutoWrapperVector toRecompute(cx);
     704           0 :     for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
     705             :         // Filter by source compartment.
     706           0 :         if (!sourceFilter.match(c))
     707             :             continue;
     708             : 
     709           0 :         if (!evictedNursery && c->hasNurseryAllocatedWrapperEntries(targetFilter)) {
     710           0 :             cx->runtime()->gc.evictNursery();
     711           0 :             evictedNursery = true;
     712             :         }
     713             : 
     714             :         // Iterate over the wrappers, filtering appropriately.
     715           0 :         for (Compartment::NonStringWrapperEnum e(c, targetFilter); !e.empty(); e.popFront()) {
     716             :             // Filter out non-objects.
     717           0 :             CrossCompartmentKey& k = e.front().mutableKey();
     718           0 :             if (!k.is<JSObject*>())
     719             :                 continue;
     720             : 
     721             :             // Add it to the list.
     722           0 :             if (!toRecompute.append(WrapperValue(e)))
     723           0 :                 return false;
     724             :         }
     725             :     }
     726             : 
     727             :     // Recompute all the wrappers in the list.
     728           0 :     for (const WrapperValue& v : toRecompute) {
     729           0 :         JSObject* wrapper = &v.toObject();
     730           0 :         JSObject* wrapped = Wrapper::wrappedObject(wrapper);
     731             :         RemapWrapper(cx, wrapper, wrapped);
     732             :     }
     733             : 
     734             :     return true;
     735             : }

Generated by: LCOV version 1.13-14-ga5dd952