/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
staticbool WrapReceiver(JSContext* cx, HandleObject wrapper,
MutableHandleValue receiver) { // Usually the receiver is the wrapper and we can just unwrap it. If the // wrapped object is also a wrapper, things are more complicated and we // fall back to the slow path (it calls UncheckedUnwrap to unwrap all // wrappers). if (ObjectValue(*wrapper) == receiver) {
JSObject* wrapped = Wrapper::wrappedObject(wrapper); if (!IsWrapper(wrapped)) {
MOZ_ASSERT(wrapped->compartment() == cx->compartment());
MOZ_ASSERT(!IsWindow(wrapped));
receiver.setObject(*wrapped); returntrue;
}
}
// Handle |this| specially. When we rewrap on the other side of the // membrane, we might apply a same-compartment security wrapper that // will stymie this whole process. If that happens, unwrap the wrapper. // This logic can go away when same-compartment security wrappers go away. if ((src == srcArgs.base() + 1) && dst->isObject()) {
RootedObject thisObj(cx, &dst->toObject()); if (thisObj->is<WrapperObject>() &&
Wrapper::wrapperHandler(thisObj)->hasSecurityPolicy()) {
MOZ_ASSERT(!thisObj->is<CrossCompartmentWrapperObject>());
*dst = ObjectValue(*Wrapper::wrappedObject(thisObj));
}
}
}
if (!CallNonGenericMethod(cx, test, impl, dstArgs)) { returnfalse;
}
// Get an equivalent RegExpShared associated with the current compartment.
Rooted<JSAtom*> source(cx, re->getSource());
cx->markAtom(source); return cx->zone()->regExps().get(cx, source, re->getFlags());
}
// Returns true iff all realms in the compartment have been nuked. staticbool NukedAllRealms(JS::Compartment* comp) { for (RealmsInCompartmentIter realm(comp); !realm.done(); realm.next()) { if (!realm->nukedIncomingWrappers) { returnfalse;
}
} returntrue;
}
/* * NukeCrossCompartmentWrappers invalidates all cross-compartment wrappers * that point to objects in the |target| realm. * * There is some complexity in targeting to preserve semantics which requires * the filtering and behavioural options: * * - |sourceFilter| limits the compartments searched for source pointers * - |nukeReferencesToWindow| will, if set to DontNukeWindowReferences skip * wrappers whose target is the window proxy of the target realm. * - |nukeReferencesFromTarget| will, when set to NukeAllReferences, disallow * the creation of new wrappers to the target realm. This option can also * allow more wrappers to be cleaned up transitively.
*/
JS_PUBLIC_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. Use Maybe to avoid copying from conditionally // initializing ObjectWrapperEnum.
mozilla::Maybe<Compartment::ObjectWrapperEnum> e; if (MOZ_LIKELY(!nukeAll)) {
e.emplace(c, target->compartment());
} else {
e.emplace(c);
c.get()->nukedOutgoingWrappers = true;
} for (; !e->empty(); e->popFront()) {
JSObject* key = e->front().key();
AutoWrapperRooter wobj(cx, WrapperValue(*e));
// Unwrap from the wrapped object in key instead of the wrapper, this // could save us a bit of time.
JSObject* wrapped = UncheckedUnwrap(key);
// 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 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);
}
}
returntrue;
}
JS_PUBLIC_API bool js::AllowNewWrapper(JS::Compartment* target, JSObject* obj) { // Disallow creating new wrappers if we nuked the object realm or target // compartment.
MOZ_ASSERT(obj->compartment() != target);
if (target->nukedOutgoingWrappers ||
obj->nonCCWRealm()->nukedIncomingWrappers) { returnfalse;
}
// Given a cross-compartment wrapper |wobj|, update it to point to // |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be // useful even if wrapper already points to newTarget. // This operation crashes on failure rather than leaving the heap in an // inconsistent state. void js::RemapWrapper(JSContext* cx, JSObject* wobjArg,
JSObject* newTargetArg) {
RootedObject wobj(cx, wobjArg);
RootedObject newTarget(cx, newTargetArg);
MOZ_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
JSObject* origTarget = Wrapper::wrappedObject(wobj);
MOZ_ASSERT(origTarget);
JS::Compartment* wcompartment = wobj->compartment();
MOZ_ASSERT(wcompartment != newTarget->compartment());
AutoDisableProxyCheck adpc;
// If we're mapping to a different target (as opposed to just recomputing // for the same target), we must not have an existing wrapper for the new // target, otherwise this will break.
MOZ_ASSERT_IF(origTarget != newTarget,
!wcompartment->lookupWrapper(newTarget));
// The old value should still be in the cross-compartment wrapper map, and // the lookup should return wobj.
ObjectWrapperMap::Ptr p = wcompartment->lookupWrapper(origTarget);
MOZ_ASSERT(*p->value().unsafeGet() == wobj);
wcompartment->removeWrapper(p);
// When we remove origv from the wrapper map, its wrapper, wobj, must // immediately cease to be a cross-compartment wrapper. Nuke it.
NukeCrossCompartmentWrapper(cx, wobj);
// If the target is a dead wrapper, and we're just fixing wrappers for // it, then we're done now that the CCW is a dead wrapper. if (JS_IsDeadWrapper(origTarget)) {
MOZ_RELEASE_ASSERT(origTarget == newTarget); return;
}
js::RemapDeadWrapper(cx, wobj, newTarget);
}
// Given a dead proxy object |wobj|, turn it into a cross-compartment wrapper // pointing at |newTarget|. // This operation crashes on failure rather than leaving the heap in an // inconsistent state. void js::RemapDeadWrapper(JSContext* cx, HandleObject wobj,
HandleObject newTarget) {
MOZ_ASSERT(IsDeadProxyObject(wobj));
MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
// These are not exposed. Doing this would require updating the // FinalizationObservers data structures.
MOZ_ASSERT(!newTarget->is<FinalizationRecordObject>());
AutoDisableProxyCheck adpc;
// wobj is not a cross-compartment wrapper, so we can use nonCCWRealm.
Realm* wrealm = wobj->nonCCWRealm();
// First, we wrap it in the new compartment. We try to use the existing // wrapper, |wobj|, since it's been nuked anyway. The rewrap() function has // the choice to reuse |wobj| or not.
RootedObject tobj(cx, newTarget);
AutoRealmUnchecked ar(cx, wrealm);
AutoEnterOOMUnsafeRegion oomUnsafe;
JS::Compartment* wcompartment = wobj->compartment(); if (!wcompartment->rewrap(cx, &tobj, wobj)) {
oomUnsafe.crash("js::RemapWrapper");
}
// If rewrap() reused |wobj|, it will have overwritten it and returned with // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj| // will still be nuked. In the latter case, we replace |wobj| with the // contents of the new wrapper in |tobj|. if (tobj != wobj) { // Now, because we need to maintain object identity, we do a brain // transplant on the old object so that it contains the contents of the // new one.
JSObject::swap(cx, wobj, tobj, oomUnsafe);
}
if (!wobj->is<WrapperObject>()) {
MOZ_ASSERT(js::IsDOMRemoteProxyObject(wobj) || IsDeadProxyObject(wobj)); return;
}
// Before swapping, this wrapper came out of rewrap(), which enforces the // invariant that the wrapper in the map points directly to the key.
MOZ_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);
// Update the entry in the compartment's wrapper map to point to the old // wrapper, which has now been updated (via reuse or swap). if (!wcompartment->putWrapper(cx, newTarget, wobj)) {
oomUnsafe.crash("js::RemapWrapper");
}
}
// Remap all cross-compartment wrappers pointing to |oldTarget| to point to // |newTarget|. All wrappers are recomputed.
JS_PUBLIC_API bool js::RemapAllWrappersForObject(JSContext* cx,
HandleObject oldTarget,
HandleObject newTarget) {
AutoWrapperVector toTransplant(cx);
for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) { if (ObjectWrapperMap::Ptr wp = c->lookupWrapper(oldTarget)) { // We found a wrapper. Remember and root it. if (!toTransplant.append(WrapperValue(wp))) { returnfalse;
}
}
}
for (const WrapperValue& v : toTransplant) {
RemapWrapper(cx, v, newTarget);
}
AutoWrapperVector toRecompute(cx); for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) { // Filter by source compartment. if (!sourceFilter.match(c)) { continue;
}
if (!evictedNursery &&
c->hasNurseryAllocatedObjectWrapperEntries(targetFilter)) {
cx->runtime()->gc.evictNursery();
evictedNursery = true;
}
// Iterate over object wrappers, filtering appropriately. for (Compartment::ObjectWrapperEnum e(c, targetFilter); !e.empty();
e.popFront()) { // Don't remap wrappers to finalization record objects. These are used // internally and are not exposed.
JSObject* wrapper = *e.front().value().unsafeGet(); if (Wrapper::wrappedObject(wrapper)->is<FinalizationRecordObject>()) { continue;
}
// Add the wrapper to the list. if (!toRecompute.append(WrapperValue(e))) { returnfalse;
}
}
}
// Recompute all the wrappers in the list. for (const WrapperValue& wrapper : toRecompute) {
JSObject* wrapped = Wrapper::wrappedObject(wrapper);
RemapWrapper(cx, wrapper, wrapped);
}
returntrue;
}
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.