js::NukeCrossCompartmentWrappers(JSContext* cx, const CompartmentFilter& sourceFilter, const CompartmentFilter& targetFilter, js::NukeReferencesToWindow nukeReferencesToWindow, js::NukeReferencesFromTarget nukeReferencesFromTarget) { CHECK_REQUEST(cx); JSRuntime* rt = cx->runtime(); EvictAllNurseries(rt); for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { if (!sourceFilter.match(c)) continue; // If the compartment matches both the source and target filter, we may // want to cut both incoming and outgoing wrappers. bool nukeAll = (nukeReferencesFromTarget == NukeAllReferences && targetFilter.match(c)); // Iterate the wrappers looking for anything interesting. for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { // Some cross-compartment wrappers are for strings. We're not // interested in those. const CrossCompartmentKey& k = e.front().key(); if (!k.is<JSObject*>()) continue; AutoWrapperRooter wobj(cx, WrapperValue(e)); JSObject* wrapped = UncheckedUnwrap(wobj); // We never nuke script source objects, since only ever used internally by the JS // engine, and are expected to remain valid throughout a scripts lifetime. if (MOZ_UNLIKELY(wrapped->is<ScriptSourceObject>())) { continue; } // We only skip nuking window references that point to a target // compartment, not the ones that belong to it. if (nukeReferencesToWindow == DontNukeWindowReferences && MOZ_LIKELY(!nukeAll) && IsWindowProxy(wrapped)) { continue; } if (MOZ_UNLIKELY(nukeAll) || targetFilter.match(wrapped->compartment())) { // We found a wrapper to nuke. e.removeFront(); NukeCrossCompartmentWrapper(cx, wobj); } } } return true; }
js::NukeCrossCompartmentWrappers(JSContext* cx, const CompartmentFilter& sourceFilter, const CompartmentFilter& targetFilter, js::NukeReferencesToWindow nukeReferencesToWindow) { CHECK_REQUEST(cx); JSRuntime* rt = cx->runtime(); rt->gc.evictNursery(JS::gcreason::EVICT_NURSERY); // Iterate through scopes looking for system cross compartment wrappers // that point to an object that shares a global with obj. for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { if (!sourceFilter.match(c)) continue; // Iterate the wrappers looking for anything interesting. for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { // Some cross-compartment wrappers are for strings. We're not // interested in those. const CrossCompartmentKey& k = e.front().key(); if (!k.is<JSObject*>()) continue; AutoWrapperRooter wobj(cx, WrapperValue(e)); JSObject* wrapped = UncheckedUnwrap(wobj); if (nukeReferencesToWindow == DontNukeWindowReferences && IsWindowProxy(wrapped)) { continue; } if (targetFilter.match(wrapped->compartment())) { // We found a wrapper to nuke. e.removeFront(); NukeRemovedCrossCompartmentWrapper(cx, wobj); } } } return true; }
js::NukeCrossCompartmentWrappers(JSContext* cx, const CompartmentFilter& sourceFilter, const CompartmentFilter& targetFilter, js::NukeReferencesToWindow nukeReferencesToWindow) { CHECK_REQUEST(cx); JSRuntime *rt = cx->runtime; // Iterate through scopes looking for system cross compartment wrappers // that point to an object that shares a global with obj. for (CompartmentsIter c(rt); !c.done(); c.next()) { if (!sourceFilter.match(c)) continue; // Iterate the wrappers looking for anything interesting. WrapperMap &pmap = c->crossCompartmentWrappers; for (WrapperMap::Enum e(pmap); !e.empty(); e.popFront()) { // Some cross-compartment wrappers are for strings. We're not // interested in those. const CrossCompartmentKey &k = e.front().key; if (k.kind != CrossCompartmentKey::ObjectWrapper) continue; JSObject *wobj = &e.front().value.get().toObject(); JSObject *wrapped = UnwrapObject(wobj); if (nukeReferencesToWindow == DontNukeWindowReferences && wrapped->getClass()->ext.innerObject) continue; if (targetFilter.match(wrapped->compartment())) { // We found a wrapper to nuke. e.removeFront(); NukeCrossCompartmentWrapper(wobj); } } } return JS_TRUE; }
js::RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter, const CompartmentFilter &targetFilter) { AutoMaybeTouchDeadCompartments agc(cx); AutoWrapperVector toRecompute(cx); for (CompartmentsIter c(cx->runtime); !c.done(); c.next()) { // Filter by source compartment. if (!sourceFilter.match(c)) continue; // Iterate over the wrappers, filtering appropriately. WrapperMap &pmap = c->crossCompartmentWrappers; for (WrapperMap::Enum e(pmap); !e.empty(); e.popFront()) { // Filter out non-objects. const CrossCompartmentKey &k = e.front().key; if (k.kind != CrossCompartmentKey::ObjectWrapper) continue; // Filter by target compartment. if (!targetFilter.match(k.wrapped->compartment())) continue; // Add it to the list. if (!toRecompute.append(WrapperValue(e))) return false; } } // Recompute all the wrappers in the list. for (WrapperValue *begin = toRecompute.begin(), *end = toRecompute.end(); begin != end; ++begin) { JSObject *wrapper = &begin->toObject(); JSObject *wrapped = Wrapper::wrappedObject(wrapper); if (!RemapWrapper(cx, wrapper, wrapped)) MOZ_CRASH(); } return true; }
js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter, const CompartmentFilter& targetFilter) { // Drop any nursery-allocated wrappers. EvictAllNurseries(cx->runtime()); AutoWrapperVector toRecompute(cx); for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { // Filter by source compartment. if (!sourceFilter.match(c)) continue; // Iterate over the wrappers, filtering appropriately. for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { // Filter out non-objects. CrossCompartmentKey& k = e.front().mutableKey(); if (!k.is<JSObject*>()) continue; // Filter by target compartment. if (!targetFilter.match(k.compartment())) continue; // Add it to the list. if (!toRecompute.append(WrapperValue(e))) return false; } } // Recompute all the wrappers in the list. for (const WrapperValue& v : toRecompute) { JSObject* wrapper = &v.toObject(); JSObject* wrapped = Wrapper::wrappedObject(wrapper); RemapWrapper(cx, wrapper, wrapped); } return true; }
js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter, const CompartmentFilter& targetFilter) { AutoWrapperVector toRecompute(cx); for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { // Filter by source compartment. if (!sourceFilter.match(c)) continue; // Iterate over the wrappers, filtering appropriately. for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { // Filter out non-objects. const CrossCompartmentKey& k = e.front().key(); if (k.kind != CrossCompartmentKey::ObjectWrapper) continue; // Filter by target compartment. if (!targetFilter.match(static_cast<JSObject*>(k.wrapped)->compartment())) continue; // Add it to the list. if (!toRecompute.append(WrapperValue(e))) return false; } } // Recompute all the wrappers in the list. for (const WrapperValue& v : toRecompute) { JSObject* wrapper = &v.toObject(); JSObject* wrapped = Wrapper::wrappedObject(wrapper); if (!RemapWrapper(cx, wrapper, wrapped)) MOZ_CRASH(); } return true; }
JS_FRIEND_API bool js::RecomputeWrappers( JSContext* cx, const CompartmentFilter& sourceFilter, const CompartmentFilter& targetFilter) { bool evictedNursery = false; AutoWrapperVector toRecompute(cx); for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) { // Filter by source compartment. if (!sourceFilter.match(c)) { continue; } if (!evictedNursery && c->hasNurseryAllocatedWrapperEntries(targetFilter)) { cx->runtime()->gc.evictNursery(); evictedNursery = true; } // Iterate over the wrappers, filtering appropriately. for (Compartment::NonStringWrapperEnum e(c, targetFilter); !e.empty(); e.popFront()) { // Filter out non-objects. CrossCompartmentKey& k = e.front().mutableKey(); if (!k.is<JSObject*>()) { continue; } // Add it to the list. if (!toRecompute.append(WrapperValue(e))) { return false; } } } // Recompute all the wrappers in the list. for (const WrapperValue& v : toRecompute) { JSObject* wrapper = &v.toObject(); JSObject* wrapped = Wrapper::wrappedObject(wrapper); RemapWrapper(cx, wrapper, wrapped); } return true; }
/* * NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts * all of the cross-compartment wrappers that point to an object in the |target| * realm. The snag here is that we need to avoid cutting wrappers that point to * the window object on page navigation (inner window destruction) and only do * that on tab close (outer window destruction). Thus the option of how to * handle the global object. */ JS_FRIEND_API bool js::NukeCrossCompartmentWrappers( JSContext* cx, const CompartmentFilter& sourceFilter, JS::Realm* target, js::NukeReferencesToWindow nukeReferencesToWindow, js::NukeReferencesFromTarget nukeReferencesFromTarget) { CHECK_THREAD(cx); JSRuntime* rt = cx->runtime(); // If we're nuking all wrappers into the target realm, prevent us from // creating new wrappers for it in the future. if (nukeReferencesFromTarget == NukeAllReferences) { target->nukedIncomingWrappers = true; } for (CompartmentsIter c(rt); !c.done(); c.next()) { if (!sourceFilter.match(c)) { continue; } // If the realm matches both the source and target filter, we may want to // cut outgoing wrappers too, if we nuked all realms in the compartment. bool nukeAll = (nukeReferencesFromTarget == NukeAllReferences && target->compartment() == c.get() && NukedAllRealms(c.get())); // Iterate only the wrappers that have target compartment matched unless // |nukeAll| is true. The string wrappers that we're not interested in // won't be iterated, we can exclude them easily because they have // compartment nullptr. Use Maybe to avoid copying from conditionally // initializing NonStringWrapperEnum. mozilla::Maybe<Compartment::NonStringWrapperEnum> e; if (MOZ_LIKELY(!nukeAll)) { e.emplace(c, target->compartment()); } else { e.emplace(c); c.get()->nukedOutgoingWrappers = true; } for (; !e->empty(); e->popFront()) { // Skip debugger references because NukeCrossCompartmentWrapper() // doesn't know how to nuke them yet, see bug 1084626 for more // information. const CrossCompartmentKey& k = e->front().key(); if (!k.is<JSObject*>()) { continue; } AutoWrapperRooter wobj(cx, WrapperValue(*e)); // Unwrap from the wrapped object in CrossCompartmentKey instead of // the wrapper, this could save us a bit of time. JSObject* wrapped = UncheckedUnwrap(k.as<JSObject*>()); // Don't nuke wrappers for objects in other realms in the target // compartment unless nukeAll is set because in that case we want to nuke // all outgoing wrappers for the current compartment. if (!nukeAll && wrapped->nonCCWRealm() != target) { continue; } // We never nuke ScriptSourceObjects, since they are only ever used // internally by the JS engine, and are expected to remain valid // throughout a script's lifetime. if (MOZ_UNLIKELY(wrapped->is<ScriptSourceObject>())) { continue; } // We only skip nuking window references that point to a target // compartment, not the ones that belong to it. if (nukeReferencesToWindow == DontNukeWindowReferences && MOZ_LIKELY(!nukeAll) && IsWindowProxy(wrapped)) { continue; } // Now this is the wrapper we want to nuke. e->removeFront(); NukeRemovedCrossCompartmentWrapper(cx, wobj); } } return true; }
js::NukeCrossCompartmentWrappers(JSContext* cx, const CompartmentFilter& sourceFilter, JSCompartment* target, js::NukeReferencesToWindow nukeReferencesToWindow, js::NukeReferencesFromTarget nukeReferencesFromTarget) { CHECK_REQUEST(cx); JSRuntime* rt = cx->runtime(); for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { if (!sourceFilter.match(c)) continue; // If the compartment matches both the source and target filter, we may // want to cut both incoming and outgoing wrappers. bool nukeAll = (nukeReferencesFromTarget == NukeAllReferences && target == c.get()); // Iterate only the wrappers that have target compartment matched unless // |nukeAll| is true. The string wrappers that we're not interested in // won't be iterated, we can exclude them easily because they have // compartment nullptr. Use Maybe to avoid copying from conditionally // initializing NonStringWrapperEnum. mozilla::Maybe<JSCompartment::NonStringWrapperEnum> e; if (MOZ_LIKELY(!nukeAll)) e.emplace(c, target); else e.emplace(c); for (; !e->empty(); e->popFront()) { // Skip debugger references because NukeCrossCompartmentWrapper() // doesn't know how to nuke them yet, see bug 1084626 for more // information. const CrossCompartmentKey& k = e->front().key(); if (!k.is<JSObject*>()) continue; AutoWrapperRooter wobj(cx, WrapperValue(*e)); // Unwrap from the wrapped object in CrossCompartmentKey instead of // the wrapper, this could save us a bit of time. JSObject* wrapped = UncheckedUnwrap(k.as<JSObject*>()); // We never nuke script source objects, since only ever used internally by the JS // engine, and are expected to remain valid throughout a scripts lifetime. if (MOZ_UNLIKELY(wrapped->is<ScriptSourceObject>())) { continue; } // We only skip nuking window references that point to a target // compartment, not the ones that belong to it. if (nukeReferencesToWindow == DontNukeWindowReferences && MOZ_LIKELY(!nukeAll) && IsWindowProxy(wrapped)) { continue; } // Now this is the wrapper we want to nuke. e->removeFront(); NukeRemovedCrossCompartmentWrapper(cx, wobj); } } return true; }