Bug 754989 - Nuke dead cross-compartment wrappers during brain transplant (r=bholley)

This commit is contained in:
Bill McCloskey 2012-05-22 14:54:08 -07:00
parent 74fcb7d4a3
commit 520e8ad0c3
3 changed files with 49 additions and 18 deletions

View File

@ -1539,6 +1539,8 @@ JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target)
{
AssertNoGC(cx);
JS_ASSERT(origobj != target);
JS_ASSERT(!IsCrossCompartmentWrapper(origobj));
JS_ASSERT(!IsCrossCompartmentWrapper(target));
JSCompartment *destination = target->compartment();
WrapperMap &map = destination->crossCompartmentWrappers;
@ -1547,7 +1549,7 @@ JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target)
if (origobj->compartment() == destination) {
// If the original object is in the same compartment as the
// destination, then we know that we won't find wrapper in the
// destination, then we know that we won't find a wrapper in the
// destination's cross compartment map and that the same
// object will continue to work.
if (!origobj->swap(cx, target))
@ -1558,7 +1560,12 @@ JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target)
// the new compartment. If there is, we use its identity and swap
// in the contents of |target|.
newIdentity = &p->value.toObject();
// When we remove origv from the wrapper map, its wrapper, newIdentity,
// must immediately cease to be a cross-compartment wrapper. Neuter it.
map.remove(p);
NukeCrossCompartmentWrapper(newIdentity);
if (!newIdentity->swap(cx, target))
return NULL;
} else {
@ -1579,8 +1586,7 @@ JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target)
return NULL;
if (!origobj->swap(cx, newIdentityWrapper))
return NULL;
origobj->compartment()->crossCompartmentWrappers.put(ObjectValue(*newIdentity),
origv);
origobj->compartment()->crossCompartmentWrappers.put(ObjectValue(*newIdentity), origv);
}
// The new identity object might be one of several things. Return it to avoid
@ -1615,8 +1621,12 @@ RemapWrappers(JSContext *cx, JSObject *orig, JSObject *target)
JSObject *wobj = &begin->toObject();
JSCompartment *wcompartment = wobj->compartment();
WrapperMap &pmap = wcompartment->crossCompartmentWrappers;
// When we remove origv from the wrapper map, its wrapper, wobj, must
// immediately cease to be a cross-compartment wrapper. Neuter it.
JS_ASSERT(pmap.lookup(origv));
pmap.remove(origv);
NukeCrossCompartmentWrapper(wobj);
// First, we wrap it in the new compartment. This will return
// a new wrapper.
@ -1654,6 +1664,10 @@ js_TransplantObjectWithWrapper(JSContext *cx,
JSObject *targetwrapper)
{
AssertNoGC(cx);
JS_ASSERT(!IsCrossCompartmentWrapper(origobj));
JS_ASSERT(!IsCrossCompartmentWrapper(origwrapper));
JS_ASSERT(!IsCrossCompartmentWrapper(origwrapper));
JS_ASSERT(!IsCrossCompartmentWrapper(origwrapper));
JSObject *newWrapper;
JSCompartment *destination = targetobj->compartment();
@ -1669,7 +1683,12 @@ js_TransplantObjectWithWrapper(JSContext *cx,
// There is. Make the existing cross-compartment wrapper a same-
// compartment wrapper.
newWrapper = &p->value.toObject();
// When we remove origv from the wrapper map, its wrapper, newWrapper,
// must immediately cease to be a cross-compartment wrapper. Neuter it.
map.remove(p);
NukeCrossCompartmentWrapper(newWrapper);
if (!newWrapper->swap(cx, targetwrapper))
return NULL;
} else {

View File

@ -1007,6 +1007,23 @@ DeadObjectProxy::getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *rec
DeadObjectProxy DeadObjectProxy::singleton;
int DeadObjectProxy::sDeadObjectFamily;
void
js::NukeCrossCompartmentWrapper(JSObject *wrapper)
{
JS_ASSERT(IsCrossCompartmentWrapper(wrapper));
SetProxyPrivate(wrapper, NullValue());
SetProxyHandler(wrapper, &DeadObjectProxy::singleton);
if (IsFunctionProxy(wrapper)) {
wrapper->setReservedSlot(JSSLOT_PROXY_CALL, NullValue());
wrapper->setReservedSlot(JSSLOT_PROXY_CONSTRUCT, NullValue());
}
wrapper->setReservedSlot(JSSLOT_PROXY_EXTRA + 0, NullValue());
wrapper->setReservedSlot(JSSLOT_PROXY_EXTRA + 1, NullValue());
}
/*
* NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts
* all of the cross-compartment wrappers that point to objects parented to
@ -1053,17 +1070,7 @@ js::NukeChromeCrossCompartmentWrappersForGlobal(JSContext *cx, JSObject *obj,
if (&wrapped->global() == global) {
// We found a wrapper to nuke.
e.removeFront();
SetProxyPrivate(wobj, JSVAL_NULL);
SetProxyHandler(wobj, &DeadObjectProxy::singleton);
if (IsFunctionProxy(wobj)) {
wobj->setReservedSlot(JSSLOT_PROXY_CALL, JSVAL_NULL);
wobj->setReservedSlot(JSSLOT_PROXY_CONSTRUCT, JSVAL_NULL);
}
wobj->setReservedSlot(JSSLOT_PROXY_EXTRA + 0, JSVAL_NULL);
wobj->setReservedSlot(JSSLOT_PROXY_EXTRA + 1, JSVAL_NULL);
NukeCrossCompartmentWrapper(wobj);
}
}
}

View File

@ -223,16 +223,21 @@ IsWrapper(const JSObject *obj)
// stopAtOuter is true, then this returns the outer window if it was
// previously wrapped. Otherwise, this returns the first object for
// which JSObject::isWrapper returns false.
JS_FRIEND_API(JSObject *) UnwrapObject(JSObject *obj, bool stopAtOuter = true,
unsigned *flagsp = NULL);
JS_FRIEND_API(JSObject *)
UnwrapObject(JSObject *obj, bool stopAtOuter = true, unsigned *flagsp = NULL);
// Given a JSObject, returns that object stripped of wrappers. At each stage,
// the security wrapper has the opportunity to veto the unwrap. Since checked
// code should never be unwrapping outer window wrappers, we always stop at
// outer windows.
JS_FRIEND_API(JSObject *) UnwrapObjectChecked(JSContext *cx, JSObject *obj);
JS_FRIEND_API(JSObject *)
UnwrapObjectChecked(JSContext *cx, JSObject *obj);
bool IsCrossCompartmentWrapper(const JSObject *obj);
bool
IsCrossCompartmentWrapper(const JSObject *obj);
void
NukeCrossCompartmentWrapper(JSObject *wrapper);
} /* namespace js */