Bug 800915 - Add infrastructure to flag security wrappers as unsafe to unwrap. r=mrbkap

This commit is contained in:
Bobby Holley 2012-11-14 09:56:25 -08:00
parent a36b720564
commit 6eebf2b4d4
4 changed files with 45 additions and 3 deletions

View File

@ -519,7 +519,7 @@ nsPIDOMWindow::~nsPIDOMWindow() {}
class nsOuterWindowProxy : public js::Wrapper
{
public:
nsOuterWindowProxy() : js::Wrapper(0) {}
nsOuterWindowProxy() : js::Wrapper(0) { setSafeToUnwrap(false); }
virtual bool isOuterWindow() {
return true;

View File

@ -160,6 +160,7 @@ js::IsCrossCompartmentWrapper(RawObject wrapper)
Wrapper::Wrapper(unsigned flags, bool hasPrototype) : DirectProxyHandler(&sWrapperFamily)
, mFlags(flags)
, mSafeToUnwrap(true)
{
setHasPrototype(hasPrototype);
}
@ -796,7 +797,9 @@ CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
template <class Base>
SecurityWrapper<Base>::SecurityWrapper(unsigned flags)
: Base(flags)
{}
{
Base::setSafeToUnwrap(false);
}
template <class Base>
bool

View File

@ -29,6 +29,7 @@ class DummyFrameGuard;
class JS_FRIEND_API(Wrapper) : public DirectProxyHandler
{
unsigned mFlags;
bool mSafeToUnwrap;
public:
enum Action {
@ -43,6 +44,15 @@ class JS_FRIEND_API(Wrapper) : public DirectProxyHandler
LAST_USED_FLAG = CROSS_COMPARTMENT
};
/*
* Wrappers can explicitly specify that they are unsafe to unwrap from a
* security perspective (as is the case for SecurityWrappers). If a wrapper
* is not safe to unwrap, operations requiring full access to the underlying
* object (via UnwrapObjectChecked) will throw. Otherwise, they will succeed.
*/
void setSafeToUnwrap(bool safe) { mSafeToUnwrap = safe; };
bool isSafeToUnwrap() { return mSafeToUnwrap; };
static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto,
JSObject *parent, Wrapper *handler);

View File

@ -291,6 +291,31 @@ GetWrappedNative(JSContext *cx, JSObject *obj)
: nullptr;
}
#ifdef DEBUG
static void
DEBUG_CheckUnwrapSafety(JSObject *obj, js::Wrapper *handler,
JSCompartment *origin, JSCompartment *target)
{
typedef FilteringWrapper<CrossCompartmentSecurityWrapper, OnlyIfSubjectIsSystem> XSOW;
if (AccessCheck::isChrome(target) || xpc::IsUniversalXPConnectEnabled(target)) {
// If the caller is chrome (or effectively so), unwrap should always be allowed.
MOZ_ASSERT(handler->isSafeToUnwrap());
} else if (WrapperFactory::IsLocationObject(obj) ||
WrapperFactory::IsComponentsObject(obj) ||
handler == &XSOW::singleton)
{
// This is an object that is restricted regardless of origin.
MOZ_ASSERT(!handler->isSafeToUnwrap());
} else {
// Otherwise, it should depend on whether the target subsumes the origin.
MOZ_ASSERT(handler->isSafeToUnwrap() == AccessCheck::subsumes(target, origin));
}
}
#else
#define DEBUG_CheckUnwrapSafety(obj, handler, origin, target) {}
#endif
JSObject *
WrapperFactory::Rewrap(JSContext *cx, JSObject *existing, JSObject *obj,
JSObject *wrappedProto, JSObject *parent,
@ -443,6 +468,8 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *existing, JSObject *obj,
}
}
DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target);
if (existing && proxyProto == wrappedProto)
return Wrapper::Renew(cx, existing, obj, wrapper);
@ -465,7 +492,9 @@ WrapperFactory::WrapForSameCompartment(JSContext *cx, JSObject *obj)
MOZ_ASSERT(wn, "Trying to wrap a dead WN!");
// The WN knows what to do.
return wn->GetSameCompartmentSecurityWrapper(cx);
JSObject *wrapper = wn->GetSameCompartmentSecurityWrapper(cx);
MOZ_ASSERT_IF(wrapper != obj, !Wrapper::wrapperHandler(wrapper)->isSafeToUnwrap());
return wrapper;
}
bool