diff --git a/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp b/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp index b1e85635cc7..5b0ee27403a 100644 --- a/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp +++ b/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp @@ -244,6 +244,9 @@ XPC_COW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly); static JSObject * XPC_COW_WrappedObject(JSContext *cx, JSObject *obj); +static JSBool +WrapFunction(JSContext *cx, JSObject *scope, JSObject *funobj, jsval *vp); + using namespace XPCWrapper; namespace ChromeObjectWrapper { @@ -275,6 +278,10 @@ JSExtendedClass COWClass = { JSBool WrapObject(JSContext *cx, JSObject *parent, jsval v, jsval *vp) { + if (JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v))) { + return WrapFunction(cx, parent, JSVAL_TO_OBJECT(v), vp); + } + JSObject *wrapperObj = JS_NewObjectWithGivenProto(cx, &COWClass.base, NULL, parent); if (!wrapperObj) { @@ -361,9 +368,9 @@ GetWrappedObject(JSContext *cx, JSObject *wrapper) // Forward declaration for the function wrapper. JSBool -XPC_COW_RewrapForChrome(JSContext *cx, JSObject *wrapperObj, jsval *vp); +RewrapForChrome(JSContext *cx, JSObject *wrapperObj, jsval *vp); JSBool -XPC_COW_RewrapForContent(JSContext *cx, JSObject *wrapperObj, jsval *vp); +RewrapForContent(JSContext *cx, JSObject *wrapperObj, jsval *vp); // This function wrapper calls a function from untrusted content into chrome. @@ -371,57 +378,51 @@ static JSBool XPC_COW_FunctionWrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSObject *wrappedObj; - - // Allow 'this' to be either a COW, in which case we unwrap it or something - // that isn't a COW. We disallow invalid COWs that have no wrapped object. - - wrappedObj = GetWrapper(obj); - if (wrappedObj) { - wrappedObj = GetWrappedObject(cx, wrappedObj); - if (!wrappedObj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - } else { - wrappedObj = obj; - } - jsval funToCall; if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(argv[-2]), XPCWrapper::eWrappedFunctionSlot, &funToCall)) { return JS_FALSE; } + JSObject *scope = JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(funToCall)); for (uintN i = 0; i < argc; ++i) { - if (!XPC_COW_RewrapForChrome(cx, obj, &argv[i])) { + if (!JSVAL_IS_PRIMITIVE(argv[i]) && + !RewrapObject(cx, scope, JSVAL_TO_OBJECT(argv[i]), UNKNOWN, &argv[i])) { return JS_FALSE; } } - if (!JS_CallFunctionValue(cx, wrappedObj, funToCall, argc, argv, rval)) { + if (!RewrapObject(cx, scope, obj, UNKNOWN, rval) || + !JS_CallFunctionValue(cx, JSVAL_TO_OBJECT(*rval), funToCall, argc, argv, + rval)) { return JS_FALSE; } - return XPC_COW_RewrapForContent(cx, obj, rval); + return RewrapForContent(cx, obj, rval); } -JSBool -XPC_COW_WrapFunction(JSContext *cx, JSObject *outerObj, JSObject *funobj, - jsval *rval) +static JSBool +WrapFunction(JSContext *cx, JSObject *scope, JSObject *funobj, jsval *rval) { + scope = JS_GetGlobalForObject(cx, scope); jsval funobjVal = OBJECT_TO_JSVAL(funobj); JSFunction *wrappedFun = reinterpret_cast(xpc_GetJSPrivate(funobj)); JSNative native = JS_GetFunctionNative(cx, wrappedFun); - if (!native || native == XPC_COW_FunctionWrapper) { - *rval = funobjVal; - return JS_TRUE; + if (native == XPC_COW_FunctionWrapper) { + if (STOBJ_GET_PARENT(funobj) == scope) { + *rval = funobjVal; + return JS_TRUE; + } + + JS_GetReservedSlot(cx, funobj, XPCWrapper::eWrappedFunctionSlot, &funobjVal); + funobj = JSVAL_TO_OBJECT(funobjVal); } JSFunction *funWrapper = JS_NewFunction(cx, XPC_COW_FunctionWrapper, JS_GetFunctionArity(wrappedFun), 0, - JS_GetGlobalForObject(cx, outerObj), + scope, JS_GetFunctionName(wrappedFun)); if (!funWrapper) { return JS_FALSE; @@ -436,63 +437,32 @@ XPC_COW_WrapFunction(JSContext *cx, JSObject *outerObj, JSObject *funobj, } JSBool -XPC_COW_RewrapForChrome(JSContext *cx, JSObject *wrapperObj, jsval *vp) +RewrapForChrome(JSContext *cx, JSObject *wrapperObj, jsval *vp) { jsval v = *vp; if (JSVAL_IS_PRIMITIVE(v)) { return JS_TRUE; } - // We're rewrapping for chrome, so this is safe. - JSObject *obj = GetWrappedJSObject(cx, JSVAL_TO_OBJECT(v)); - if (!obj) { - *vp = JSVAL_NULL; - return JS_TRUE; - } - - XPCWrappedNative *wn; - JSBool ok; - - // Set aside the frame chain so that we'll be able to wrap this object for - // chrome's use. - JSStackFrame *fp = JS_SaveFrameChain(cx); - - if (IS_WN_WRAPPER(obj) && - (wn = (XPCWrappedNative*)xpc_GetJSPrivate(obj)) && - !nsXPCWrappedJSClass::IsWrappedJS(wn->Native())) { - // Return an explicit XPCNativeWrapper in case "chrome" code happens to be - // XBL code cloned into an untrusted context. - ok = XPCNativeWrapper::CreateExplicitWrapper(cx, wn, JS_TRUE, vp); - } else { - // Note: we're passing the wrapped chrome object as the scope for the SJOW. - ok = XPCSafeJSObjectWrapper::WrapObject(cx, GetWrappedObject(cx, wrapperObj), - *vp, vp); - } - - JS_RestoreFrameChain(cx, fp); - - return ok; + return RewrapObject(cx, JS_GetGlobalForObject(cx, GetWrappedObject(cx, wrapperObj)), + JSVAL_TO_OBJECT(v), UNKNOWN, vp); } JSBool -XPC_COW_RewrapForContent(JSContext *cx, JSObject *wrapperObj, jsval *vp) +RewrapForContent(JSContext *cx, JSObject *wrapperObj, jsval *vp) { jsval v = *vp; if (JSVAL_IS_PRIMITIVE(v)) { return JS_TRUE; } - JSObject *obj = GetWrappedJSObject(cx, JSVAL_TO_OBJECT(v)); - if (!obj) { - *vp = JSVAL_NULL; - return JS_TRUE; + JSObject *scope = JS_GetScopeChain(cx); + if (!scope) { + return JS_FALSE; } - if (JS_ObjectIsFunction(cx, obj)) { - return XPC_COW_WrapFunction(cx, wrapperObj, obj, vp); - } - - return WrapObject(cx, JS_GetScopeChain(cx), OBJECT_TO_JSVAL(obj), vp); + return RewrapObject(cx, JS_GetGlobalForObject(cx, scope), + JSVAL_TO_OBJECT(v), COW, vp); } static JSBool @@ -549,7 +519,7 @@ XPC_COW_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) } } - return XPC_COW_RewrapForChrome(cx, obj, vp) && + return RewrapForChrome(cx, obj, vp) && JS_DefinePropertyById(cx, wrappedObj, interned_id, *vp, desc.getter, desc.setter, desc.attrs); } @@ -632,7 +602,7 @@ XPC_COW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); } - if (!XPC_COW_RewrapForChrome(cx, obj, vp)) { + if (isSet && !RewrapForChrome(cx, obj, vp)) { return JS_FALSE; } @@ -643,7 +613,7 @@ XPC_COW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, return JS_FALSE; } - return XPC_COW_RewrapForContent(cx, obj, vp); + return RewrapForContent(cx, obj, vp); } static JSBool @@ -714,7 +684,8 @@ XPC_COW_NewResolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags, return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); } - return XPCWrapper::NewResolve(cx, obj, JS_TRUE, wrappedObj, id, flags, objp); + return XPCWrapper::NewResolve(cx, obj, JS_FALSE, wrappedObj, id, flags, + objp); } static JSBool @@ -743,7 +714,7 @@ XPC_COW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) return JS_FALSE; } - return XPC_COW_RewrapForContent(cx, obj, vp); + return RewrapForContent(cx, obj, vp); } static JSBool diff --git a/js/src/xpconnect/src/XPCNativeWrapper.cpp b/js/src/xpconnect/src/XPCNativeWrapper.cpp index 4d6bed0253a..d1b74db4be1 100644 --- a/js/src/xpconnect/src/XPCNativeWrapper.cpp +++ b/js/src/xpconnect/src/XPCNativeWrapper.cpp @@ -235,59 +235,33 @@ RewrapIfDeepWrapper(JSContext *cx, JSObject *obj, jsval v, jsval *rval) // Re-wrap non-primitive values if this is a deep wrapper, i.e. // if (HAS_FLAGS(flags, FLAG_DEEP). if (HAS_FLAGS(flags, FLAG_DEEP) && !primitive) { - // Unwrap a cross origin wrapper, since we're more restrictive. - if (STOBJ_GET_CLASS(nativeObj) == &XPCCrossOriginWrapper::XOWClass.base) { - if (!::JS_GetReservedSlot(cx, nativeObj, sWrappedObjSlot, - &v)) { - return JS_FALSE; - } - - // If v is primitive, allow nativeObj to remain a cross origin wrapper, - // which will fail below (since it isn't a wrapped native). - if (!JSVAL_IS_PRIMITIVE(v)) { - nativeObj = JSVAL_TO_OBJECT(v); - } - } - - XPCWrappedNative* wrappedNative = - XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, nativeObj); - if (!wrappedNative) { - return XPCSafeJSObjectWrapper::WrapObject(cx, JS_GetScopeChain(cx), - v, rval); - } - - if (HAS_FLAGS(flags, FLAG_EXPLICIT)) { -#ifdef DEBUG_XPCNativeWrapper - printf("Rewrapping for deep explicit wrapper\n"); -#endif - if (wrappedNative == XPCNativeWrapper::SafeGetWrappedNative(obj)) { - // Already wrapped, return the wrapper. - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - - // |obj| is an explicit deep wrapper. We want to construct another - // explicit deep wrapper for |v|. - - return XPCNativeWrapper::CreateExplicitWrapper(cx, wrappedNative, - JS_TRUE, rval); - } - -#ifdef DEBUG_XPCNativeWrapper - printf("Rewrapping for deep implicit wrapper\n"); -#endif - // Just using GetNewOrUsed on the return value of - // GetWrappedNativeOfJSObject will give the right thing -- the unique deep - // implicit wrapper associated with wrappedNative. - JSObject* wrapperObj = XPCNativeWrapper::GetNewOrUsed(cx, wrappedNative, - JS_GetScopeChain(cx), - nsnull); - if (!wrapperObj) { + JSObject *scope = JS_GetScopeChain(cx); + if (!scope) { return JS_FALSE; } - *rval = OBJECT_TO_JSVAL(wrapperObj); + WrapperType type = HAS_FLAGS(flags, FLAG_EXPLICIT) + ? XPCNW_EXPLICIT : XPCNW_IMPLICIT; + + if (!RewrapObject(cx, JS_GetGlobalForObject(cx, scope), + nativeObj, type, rval)) { + return JS_FALSE; + } } else { + if (!JSVAL_IS_PRIMITIVE(v)) { + JSObject *scope = JS_GetScopeChain(cx); + if (!scope) { + return JS_FALSE; + } + + // NB: Because we're not a deep wrapper, we give a hint of SJOW to + // imitate not having a wrapper at all. + if (!RewrapObject(cx, JS_GetGlobalForObject(cx, scope), + JSVAL_TO_OBJECT(v), SJOW, &v)) { + return JS_FALSE; + } + } + *rval = v; } @@ -460,6 +434,7 @@ EnsureLegalActivity(JSContext *cx, JSObject *obj, // Otherwise, we're looking at a non-system file with a handle on an // implicit wrapper. This is a bug! Deny access. NS_ERROR("Implicit native wrapper in content code"); + return JS_FALSE; #else return JS_TRUE; #endif @@ -976,49 +951,49 @@ XPCNativeWrapperCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSObject *nativeObj = JSVAL_TO_OBJECT(native); - // Unwrap a cross origin wrapper, since we're more restrictive than it is. - JSObject *wrapper; - if ((wrapper = UnwrapGeneric(cx, &XPCCrossOriginWrapper::XOWClass, nativeObj))) { - nativeObj = wrapper; - } else if ((wrapper = UnwrapGeneric(cx, &XPCSafeJSObjectWrapper::SJOWClass, nativeObj))) { - nativeObj = wrapper; + // First, if this is another type of security wrapper, unwrap it to see what + // we're really dealing with. + nativeObj = UnsafeUnwrapSecurityWrapper(cx, nativeObj); + if (!nativeObj) { + return ThrowException(NS_ERROR_INVALID_ARG, cx); + } + native = OBJECT_TO_JSVAL(nativeObj); + + // Now, figure out if we're allowed to create an XPCNativeWrapper around it. + JSObject *scope = JS_GetScopeChain(cx); + if (!scope) { + return JS_FALSE; } + XPCWrappedNativeScope *xpcscope = + XPCWrappedNativeScope::FindInJSObjectScope(cx, scope); + NS_ASSERTION(xpcscope, "what crazy scope are we in?"); + XPCWrappedNative *wrappedNative; + WrapperType type = xpcscope->GetWrapperFor(cx, nativeObj, XPCNW_EXPLICIT, + &wrappedNative); - if (XPCNativeWrapper::IsNativeWrapper(nativeObj)) { - // We're asked to wrap an already wrapped object. Re-wrap the - // object wrapped by the given wrapper. + if (type != NONE && !(type & XPCNW_EXPLICIT)) { + return ThrowException(NS_ERROR_INVALID_ARG, cx); + } -#ifdef DEBUG_XPCNativeWrapper - printf("Wrapping already wrapped object\n"); -#endif - - // It's always safe to re-wrap an object. - wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(nativeObj); - - if (!wrappedNative) { - return ThrowException(NS_ERROR_INVALID_ARG, cx); - } - - nativeObj = wrappedNative->GetFlatJSObject(); - native = OBJECT_TO_JSVAL(nativeObj); - } else { + // We might have to morph. + if (!wrappedNative) { wrappedNative = XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, nativeObj); if (!wrappedNative) { return ThrowException(NS_ERROR_INVALID_ARG, cx); } + } - // Prevent wrapping a double-wrapped JS object in an - // XPCNativeWrapper! - nsCOMPtr xpcwrappedjs = - do_QueryWrappedNative(wrappedNative); + // Prevent wrapping a double-wrapped JS object in an + // XPCNativeWrapper! + nsCOMPtr xpcwrappedjs = + do_QueryWrappedNative(wrappedNative); - if (xpcwrappedjs) { - return ThrowException(NS_ERROR_INVALID_ARG, cx); - } + if (xpcwrappedjs) { + return ThrowException(NS_ERROR_INVALID_ARG, cx); } PRBool hasStringArgs = PR_FALSE; @@ -1057,8 +1032,16 @@ XPCNativeWrapperCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, } } - return XPCNativeWrapper::CreateExplicitWrapper(cx, wrappedNative, - !hasStringArgs, rval); + if (!XPCNativeWrapper::CreateExplicitWrapper(cx, wrappedNative, + !hasStringArgs, rval)) { + return JS_FALSE; + } + + if (!(type & SOW)) { + return JS_TRUE; + } + + return SystemOnlyWrapper::MakeSOW(cx, JSVAL_TO_OBJECT(*rval)); } static void diff --git a/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp b/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp index 1811ca3684a..cd62bc73d9d 100644 --- a/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp +++ b/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp @@ -94,18 +94,18 @@ XPC_SJOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly); static JSObject * XPC_SJOW_WrappedObject(JSContext *cx, JSObject *obj); +using namespace XPCSafeJSObjectWrapper; +using namespace XPCWrapper; + static inline JSBool ThrowException(nsresult ex, JSContext *cx) { - XPCThrower::Throw(ex, cx); + DoThrowException(ex, cx); return JS_FALSE; } -using namespace XPCSafeJSObjectWrapper; -using namespace XPCWrapper; - // Find the subject and object principal. The argument // subjectPrincipal can be null if the caller doesn't care about the // subject principal, and secMgr can also be null if the caller @@ -274,8 +274,59 @@ JSExtendedClass SJOWClass = { JSBool WrapObject(JSContext *cx, JSObject *scope, jsval v, jsval *vp) { - *vp = v; - return XPC_SJOW_Construct(cx, scope, 1, vp, vp); + // This might be redundant if called from XPC_SJOW_Construct, but it should + // be cheap in that case. + JSObject *objToWrap = UnsafeUnwrapSecurityWrapper(cx, JSVAL_TO_OBJECT(v)); + if (!objToWrap) { + return ThrowException(NS_ERROR_INVALID_ARG, cx); + } + + // Prevent script created Script objects from ever being wrapped + // with XPCSafeJSObjectWrapper, and never let the eval function + // object be directly wrapped. + + if (STOBJ_GET_CLASS(objToWrap) == &js_ScriptClass || + (JS_ObjectIsFunction(cx, objToWrap) && + JS_GetFunctionFastNative(cx, JS_ValueToFunction(cx, v)) == + XPCWrapper::sEvalNative)) { + return ThrowException(NS_ERROR_INVALID_ARG, cx); + } + + XPCWrappedNativeScope *xpcscope = + XPCWrappedNativeScope::FindInJSObjectScope(cx, scope); + NS_ASSERTION(xpcscope, "what crazy scope are we in?"); + + XPCWrappedNative *wrappedNative; + WrapperType type = xpcscope->GetWrapperFor(cx, objToWrap, SJOW, + &wrappedNative); + + // NB: We allow XOW here because we're as restrictive as it is (and we know + // we're same origin here). + if (type != NONE && type != XOW && !(type & SJOW)) { + return ThrowException(NS_ERROR_INVALID_ARG, cx); + } + + SLIM_LOG_WILL_MORPH(cx, objToWrap); + if (IS_SLIM_WRAPPER(objToWrap) && !MorphSlimWrapper(cx, objToWrap)) { + return ThrowException(NS_ERROR_FAILURE, cx); + } + + JSObject *wrapperObj = + JS_NewObjectWithGivenProto(cx, &SJOWClass.base, nsnull, scope); + + if (!wrapperObj) { + // JS_NewObjectWithGivenProto already threw. + return JS_FALSE; + } + + *vp = OBJECT_TO_JSVAL(wrapperObj); + if (!JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sWrappedObjSlot, + OBJECT_TO_JSVAL(objToWrap)) || + !JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sFlagsSlot, JSVAL_ZERO)) { + return JS_FALSE; + } + + return JS_TRUE; } PRBool @@ -347,21 +398,16 @@ WrapJSValue(JSContext *cx, JSObject *obj, jsval val, jsval *rval) if (JSVAL_IS_PRIMITIVE(val)) { *rval = val; } else { + if (!RewrapObject(cx, STOBJ_GET_PARENT(obj), JSVAL_TO_OBJECT(val), SJOW, + rval)) { + return JS_FALSE; + } // Construct a new safe wrapper. Note that it doesn't matter what // parent we pass in here, the construct hook will ensure we get // the right parent for the wrapper. - JSObject *safeObj = - ::JS_ConstructObjectWithArguments(cx, &SJOWClass.base, nsnull, - nsnull, 1, &val); - if (!safeObj) { - return JS_FALSE; - } - - // Set *rval to safeObj here to ensure it doesn't get collected in - // any of the code below. - *rval = OBJECT_TO_JSVAL(safeObj); - - if (JS_GetGlobalForObject(cx, obj) != JS_GetGlobalForObject(cx, safeObj)) { + JSObject *safeObj = JSVAL_TO_OBJECT(*rval); + if (STOBJ_GET_CLASS(safeObj) == &SJOWClass.base && + JS_GetGlobalForObject(cx, obj) != JS_GetGlobalForObject(cx, safeObj)) { // Check to see if the new object we just wrapped is accessible // from the unsafe object we got the new object through. If not, // force the new wrapper to use the principal of the unsafe @@ -423,21 +469,6 @@ WrapJSValue(JSContext *cx, JSObject *obj, jsval val, jsval *rval) return ok; } -static jsval -UnwrapJSValue(JSContext *cx, jsval val) -{ - if (JSVAL_IS_PRIMITIVE(val)) { - return val; - } - - JSObject *unsafeObj = GetUnsafeObject(cx, JSVAL_TO_OBJECT(val)); - if (unsafeObj) { - return OBJECT_TO_JSVAL(unsafeObj); - } - - return val; -} - static JSBool XPC_SJOW_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { @@ -470,6 +501,16 @@ XPC_SJOW_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) return JS_FALSE; } + if (!JSVAL_IS_PRIMITIVE(*vp)) { + // Adding an object of some type to the content object, make sure it's + // properly wrapped. + JSObject *added = JSVAL_TO_OBJECT(*vp); + if (!RewrapObject(cx, JS_GetGlobalForObject(cx, unsafeObj), added, + UNKNOWN, vp)) { + return JS_FALSE; + } + } + return XPCWrapper::AddProperty(cx, obj, JS_FALSE, unsafeObj, id, vp); } @@ -571,8 +612,11 @@ XPC_SJOW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, return JS_FALSE; } - if (aIsSet) { - *vp = UnwrapJSValue(cx, *vp); + if (aIsSet && + !JSVAL_IS_PRIMITIVE(*vp) && + !RewrapObject(cx, JS_GetGlobalForObject(cx, unsafeObj), + JSVAL_TO_OBJECT(*vp), UNKNOWN, vp)) { + return JS_FALSE; } JSBool ok = aIsSet @@ -657,7 +701,8 @@ XPC_SJOW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, XPC_SJOW_toString, 0, 0) != nsnull; } - return XPCWrapper::NewResolve(cx, obj, JS_FALSE, unsafeObj, id, flags, objp); + return XPCWrapper::NewResolve(cx, obj, JS_FALSE, unsafeObj, id, flags, + objp); } static JSBool @@ -778,11 +823,21 @@ XPC_SJOW_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, { SafeCallGuard guard(cx, FindObjectPrincipals(cx, safeObj, funToCall)); + JSObject *scope = JS_GetGlobalForObject(cx, funToCall); for (uintN i = 0; i < argc; ++i) { - argv[i] = UnwrapJSValue(cx, argv[i]); + // NB: Passing NONE for a hint here. + if (!JSVAL_IS_PRIMITIVE(argv[i]) && + !RewrapObject(cx, scope, JSVAL_TO_OBJECT(argv[i]), NONE, &argv[i])) { + return JS_FALSE; + } } - if (!JS_CallFunctionValue(cx, callThisObj, OBJECT_TO_JSVAL(funToCall), + jsval v; + if (!RewrapObject(cx, scope, callThisObj, NONE, &v)) { + return JS_FALSE; + } + + if (!JS_CallFunctionValue(cx, JSVAL_TO_OBJECT(v), OBJECT_TO_JSVAL(funToCall), argc, argv, rval)) { return JS_FALSE; } @@ -814,55 +869,18 @@ XPC_SJOW_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return JS_TRUE; } - JSObject *objToWrap = JSVAL_TO_OBJECT(argv[0]); - - // Prevent script created Script objects from ever being wrapped - // with XPCSafeJSObjectWrapper, and never let the eval function - // object be directly wrapped. - - if (STOBJ_GET_CLASS(objToWrap) == &js_ScriptClass || - (::JS_ObjectIsFunction(cx, objToWrap) && - ::JS_GetFunctionFastNative(cx, ::JS_ValueToFunction(cx, argv[0])) == - XPCWrapper::sEvalNative)) { + JSObject *objToWrap = UnsafeUnwrapSecurityWrapper(cx, JSVAL_TO_OBJECT(argv[0])); + if (!objToWrap) { return ThrowException(NS_ERROR_INVALID_ARG, cx); } - SLIM_LOG_WILL_MORPH(cx, objToWrap); - if (IS_SLIM_WRAPPER(objToWrap) && !MorphSlimWrapper(cx, objToWrap)) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - // Check that the caller can access the unsafe object. if (!CanCallerAccess(cx, nsnull, objToWrap)) { // CanCallerAccess() already threw for us. return JS_FALSE; } - JSObject *unsafeObj = GetUnsafeObject(cx, objToWrap); - - if (unsafeObj) { - // We're asked to wrap an already wrapped object. Re-wrap the - // object wrapped by the given wrapper. - - objToWrap = unsafeObj; - } - - JSObject *wrapperObj = - JS_NewObjectWithGivenProto(cx, &SJOWClass.base, nsnull, scope); - - if (!wrapperObj) { - // JS_NewObjectWithGivenProto already threw. - return JS_FALSE; - } - - *rval = OBJECT_TO_JSVAL(wrapperObj); - if (!JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sWrappedObjSlot, - OBJECT_TO_JSVAL(objToWrap)) || - !JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sFlagsSlot, JSVAL_ZERO)) { - return JS_FALSE; - } - - return JS_TRUE; + return WrapObject(cx, scope, OBJECT_TO_JSVAL(objToWrap), rval); } static JSBool @@ -885,7 +903,21 @@ XPC_SJOW_Create(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return JS_FALSE; } - if (!JS_CallFunctionValue(cx, obj, OBJECT_TO_JSVAL(callee), + JSObject *scope = JS_GetGlobalForObject(cx, unsafeObj); + for (uintN i = 0; i < argc; ++i) { + // NB: Passing NONE for a hint here. + if (!JSVAL_IS_PRIMITIVE(argv[i]) && + !RewrapObject(cx, scope, JSVAL_TO_OBJECT(argv[i]), NONE, &argv[i])) { + return JS_FALSE; + } + } + + jsval v; + if (!RewrapObject(cx, scope, obj, NONE, &v)) { + return JS_FALSE; + } + + if (!JS_CallFunctionValue(cx, JSVAL_TO_OBJECT(v), OBJECT_TO_JSVAL(unsafeObj), argc, argv, rval)) { return JS_FALSE; } @@ -944,20 +976,6 @@ XPC_SJOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly) return nsnull; } - JSObject *tmp = - XPCWrapper::UnwrapGeneric(cx, &XPCCrossOriginWrapper::XOWClass, - unsafeObj); - if (tmp) { - unsafeObj = tmp; - - // Repeat the CanCallerAccess check because the XOW is parented to our - // scope's global object which makes the above CanCallerAccess call lie. - if (!CanCallerAccess(cx, nsnull, unsafeObj)) { - // CanCallerAccess() already threw for us. - return nsnull; - } - } - // Create our dummy SJOW. JSObject *wrapperIter = JS_NewObjectWithGivenProto(cx, &SJOWClass.base, nsnull, diff --git a/js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp b/js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp index e95d8d449cf..37de6fd9143 100644 --- a/js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp +++ b/js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp @@ -555,7 +555,7 @@ XPC_SOW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, return JS_FALSE; } - return NewResolve(cx, obj, JS_TRUE, wrappedObj, id, flags, objp); + return NewResolve(cx, obj, JS_FALSE, wrappedObj, id, flags, objp); } static JSBool diff --git a/js/src/xpconnect/src/XPCWrapper.cpp b/js/src/xpconnect/src/XPCWrapper.cpp index afc12b2853c..61cbdbe4448 100644 --- a/js/src/xpconnect/src/XPCWrapper.cpp +++ b/js/src/xpconnect/src/XPCWrapper.cpp @@ -181,19 +181,11 @@ JSBool RewrapObject(JSContext *cx, JSObject *scope, JSObject *obj, WrapperType hint, jsval *vp) { - if (IsSecurityWrapper(obj)) { - jsval v; - JS_GetReservedSlot(cx, obj, sWrappedObjSlot, &v); - NS_ASSERTION(!JSVAL_IS_PRIMITIVE(v), "bad object"); - obj = JSVAL_TO_OBJECT(v); - } else if (XPCNativeWrapper::IsNativeWrapper(obj)) { - XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj); - if (!wn) { - *vp = JSVAL_NULL; - return JS_TRUE; - } - - obj = wn->GetFlatJSObject(); + obj = UnsafeUnwrapSecurityWrapper(cx, obj); + if (!obj) { + // A wrapper wrapping NULL (such as XPCNativeWrapper.prototype). + *vp = JSVAL_NULL; + return JS_TRUE; } XPCWrappedNativeScope *nativescope = @@ -210,6 +202,28 @@ RewrapObject(JSContext *cx, JSObject *scope, JSObject *obj, WrapperType hint, return CreateWrapperFromType(cx, scope, wn, answer, vp); } +JSObject * +UnsafeUnwrapSecurityWrapper(JSContext *cx, JSObject *obj) +{ + if (IsSecurityWrapper(obj)) { + jsval v; + JS_GetReservedSlot(cx, obj, sWrappedObjSlot, &v); + NS_ASSERTION(!JSVAL_IS_PRIMITIVE(v), "bad object"); + return JSVAL_TO_OBJECT(v); + } + + if (XPCNativeWrapper::IsNativeWrapper(obj)) { + XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj); + if (!wn) { + return nsnull; + } + + return wn->GetFlatJSObject(); + } + + return obj; +} + JSBool CreateWrapperFromType(JSContext *cx, JSObject *scope, XPCWrappedNative *wn, WrapperType hint, jsval *vp) diff --git a/js/src/xpconnect/src/XPCWrapper.h b/js/src/xpconnect/src/XPCWrapper.h index a605a04ce5b..3566a971da2 100644 --- a/js/src/xpconnect/src/XPCWrapper.h +++ b/js/src/xpconnect/src/XPCWrapper.h @@ -424,6 +424,9 @@ JSBool RewrapObject(JSContext *cx, JSObject *scope, JSObject *obj, WrapperType hint, jsval *vp); +JSObject * +UnsafeUnwrapSecurityWrapper(JSContext *cx, JSObject *obj); + JSBool CreateWrapperFromType(JSContext *cx, JSObject *scope, XPCWrappedNative *wn, WrapperType hint, jsval *vp); @@ -463,11 +466,14 @@ Enumerate(JSContext *cx, JSObject *wrapperObj, JSObject *innerObj); * Resolves a property (that may be) defined on |innerObj| onto * |wrapperObj|. This will also resolve random, page-defined objects * and is therefore unsuitable for cross-origin resolution. + * + * If |caller| is not NONE, then we will call the proper WrapObject + * hook for any getters or setters about to be lifted onto + * |wrapperObj|. */ JSBool -NewResolve(JSContext *cx, JSObject *wrapperObj, - JSBool preserveVal, JSObject *innerObj, - jsval id, uintN flags, JSObject **objp); +NewResolve(JSContext *cx, JSObject *wrapperObj, JSBool preserveVal, + JSObject *innerObj, jsval id, uintN flags, JSObject **objp); /** * Resolve a native property named id from innerObj onto wrapperObj. The diff --git a/js/src/xpconnect/src/xpcwrappednativescope.cpp b/js/src/xpconnect/src/xpcwrappednativescope.cpp index 97fd3234792..85517b4f096 100644 --- a/js/src/xpconnect/src/xpcwrappednativescope.cpp +++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp @@ -1053,15 +1053,19 @@ XPCWrappedNativeScope::GetWrapperFor(JSContext *cx, JSObject *obj, XPCCrossOriginWrapper::ClassNeedsXOW(obj->getClass()->name); // Is other a chrome object? + JSObject *obj2; + XPCWrappedNative *wrapper = + XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj, nsnull, &obj2); if(principalEqual || obj->isSystem()) - return (hint & XPCNW) ? hint : wantsXOW ? SJOW : NONE; + { + if(hint & XPCNW) + return (wrapper || obj2) ? hint : NONE; + return wantsXOW ? SJOW : NONE; + } // Other isn't a chrome object: we need to wrap it in a SJOW or an // XPCNW. - JSObject *obj2; - XPCWrappedNative *wrapper = - XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj, nsnull, &obj2); if(!wrapper && !obj2) hint = SJOW; @@ -1105,9 +1109,13 @@ XPCWrappedNativeScope::GetWrapperFor(JSContext *cx, JSObject *obj, // any other types of wrapper than the hint. if(!wrapper && !obj2) { - NS_ASSERTION(principalEqual, +#if 0 + // XXX Re-enable these assertions when we have a better mochitest + // solution than UniversalXPConnect. + NS_ASSERTION(principalEqual || hint == COW, "touching non-wrappednative object cross origin?"); - NS_ASSERTION(hint == SJOW || hint == UNKNOWN, "bad hint"); + NS_ASSERTION(hint == SJOW || hint == COW || hint == UNKNOWN, "bad hint"); +#endif return hint; } @@ -1132,7 +1140,10 @@ XPCWrappedNativeScope::GetWrapperFor(JSContext *cx, JSObject *obj, if(!principalEqual || XPCCrossOriginWrapper::ClassNeedsXOW(obj->getClass()->name)) { - NS_ASSERTION(hint != SJOW, "shouldn't have a SJOW for cross origin access?"); + // NB: We want to assert that hint is not SJOW here, but it can + // be because of shallow XPCNativeWrappers. In that case, XOW is + // the right return value because XPCNativeWrappers are meant for + // chrome, and we're in content which shouldn't expect SJOWs. return (hint & XPCNW) ? XPCNW_EXPLICIT : XOW; } diff --git a/js/src/xpconnect/tests/chrome/Makefile.in b/js/src/xpconnect/tests/chrome/Makefile.in index 5fe0c107635..918ab0c470c 100644 --- a/js/src/xpconnect/tests/chrome/Makefile.in +++ b/js/src/xpconnect/tests/chrome/Makefile.in @@ -48,6 +48,7 @@ _CHROME_FILES = \ bug503926.xul \ test_bug503926.xul \ test_bug533596.xul \ + test_wrappers.xul \ $(NULL) libs:: $(_CHROME_FILES) diff --git a/js/src/xpconnect/tests/chrome/bug503926.xul b/js/src/xpconnect/tests/chrome/bug503926.xul index 31afa8ab765..39c7b290528 100644 --- a/js/src/xpconnect/tests/chrome/bug503926.xul +++ b/js/src/xpconnect/tests/chrome/bug503926.xul @@ -18,9 +18,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=503926 var passed = false; var obj = { QueryInterface: function() { passed = true; } } try { document.documentElement.appendChild(obj); } catch (e) {} - var isDialog = location.hash != 'iframe'; - var outer = isDialog ? opener.wrappedJSObject : top.wrappedJSObject; - outer.ok(passed, "chrome/chrome test passed"); + var isDialog = location.hash != '#iframe'; + var outer = XPCNativeWrapper.unwrap(isDialog ? opener : top); + outer.ok(passed, "chrome/chrome test passed: " + (isDialog ? "dialog" : "iframe")); if (isDialog) close(); ]]> diff --git a/js/src/xpconnect/tests/chrome/test_bug503926.xul b/js/src/xpconnect/tests/chrome/test_bug503926.xul index e2032e8b73e..0ee851a41a4 100644 --- a/js/src/xpconnect/tests/chrome/test_bug503926.xul +++ b/js/src/xpconnect/tests/chrome/test_bug503926.xul @@ -15,10 +15,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=503926 Mozilla Bug 503926 - + diff --git a/js/src/xpconnect/tests/mochitest/Makefile.in b/js/src/xpconnect/tests/mochitest/Makefile.in index a9572f2fd23..c1d158b7224 100644 --- a/js/src/xpconnect/tests/mochitest/Makefile.in +++ b/js/src/xpconnect/tests/mochitest/Makefile.in @@ -48,6 +48,7 @@ _TEST_FILES = bug500931_helper.html \ inner.html \ bug92773_helper.html \ bug504877_helper.html \ + chrome_wrappers_helper.html \ test_bug92773.html \ test_bug361111.xul \ test_bug384632.html \ diff --git a/js/src/xpconnect/tests/mochitest/chrome_wrappers_helper.html b/js/src/xpconnect/tests/mochitest/chrome_wrappers_helper.html new file mode 100644 index 00000000000..8303fa5f9d6 --- /dev/null +++ b/js/src/xpconnect/tests/mochitest/chrome_wrappers_helper.html @@ -0,0 +1,21 @@ + + + + + + + diff --git a/js/src/xpconnect/tests/mochitest/test_cows.html b/js/src/xpconnect/tests/mochitest/test_cows.html index b4726782b36..299c46fea67 100644 --- a/js/src/xpconnect/tests/mochitest/test_cows.html +++ b/js/src/xpconnect/tests/mochitest/test_cows.html @@ -148,6 +148,13 @@ function COWTests() { todo(false, "COWed functions should not raise " + e); } + try { + var obj = { + get prop() { return { __exposedProps__: {}, test: "FAIL" } } + }; + ok(getCOW(obj).prop.test != "FAIL", "getting prop.test should throw"); + } catch (e) {} + try { var objWithFunc = {__exposedProps__: {foo: 'r'}, foo: function foo() { return 5; }};