From 8dc6a0e55f6c7968916ef36e257bb5018d329405 Mon Sep 17 00:00:00 2001 From: "mrbkap@gmail.com" Date: Wed, 15 Aug 2007 14:09:27 -0700 Subject: [PATCH] Don't wrap everything that comes out of a wrapped function, if that function is same-origin. bug 390946, r+sr+a=jst --- .../xpconnect/src/XPCCrossOriginWrapper.cpp | 80 +++++++++++++------ js/src/xpconnect/src/XPCWrapper.cpp | 78 +++++++++--------- 2 files changed, 92 insertions(+), 66 deletions(-) diff --git a/js/src/xpconnect/src/XPCCrossOriginWrapper.cpp b/js/src/xpconnect/src/XPCCrossOriginWrapper.cpp index ba3a2e3e458a..02752a7363b1 100644 --- a/js/src/xpconnect/src/XPCCrossOriginWrapper.cpp +++ b/js/src/xpconnect/src/XPCCrossOriginWrapper.cpp @@ -264,11 +264,14 @@ IsWrapperSameOrigin(JSContext *cx, JSObject *wrappedObj) return ssm->CheckSameOriginPrincipal(subjectPrin, objectPrin); } +static JSBool +WrapSameOriginProp(JSContext *cx, JSObject *outerObj, jsval *vp); + static JSBool XPC_XOW_FunctionWrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSObject *wrappedObj; + JSObject *wrappedObj, *outerObj = obj; obj = GetWrapper(cx, obj); if (!obj || (wrappedObj = GetWrappedObject(cx, obj)) == nsnull) { @@ -286,6 +289,11 @@ XPC_XOW_FunctionWrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); } + nsresult rv = IsWrapperSameOrigin(cx, JSVAL_TO_OBJECT(funToCall)); + if (NS_FAILED(rv) && rv != NS_ERROR_DOM_PROP_ACCESS_DENIED) { + return ThrowException(rv, cx); + } + JSNative native = JS_GetFunctionNative(cx, fun); NS_ASSERTION(native, "How'd we get here with a scripted function?"); @@ -299,29 +307,55 @@ XPC_XOW_FunctionWrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return JS_FALSE; } + if (NS_SUCCEEDED(rv)) { + return WrapSameOriginProp(cx, outerObj, rval); + } + return XPC_XOW_RewrapIfNeeded(cx, obj, rval); } +static JSBool +WrapSameOriginProp(JSContext *cx, JSObject *outerObj, jsval *vp) +{ + // Don't call XPC_XOW_RewrapIfNeeded for same origin properties. We only + // need to wrap window, document and location. + if (JSVAL_IS_PRIMITIVE(*vp)) { + return JS_TRUE; + } + + JSObject *wrappedObj = JSVAL_TO_OBJECT(*vp); + const char *name = JS_GET_CLASS(cx, wrappedObj)->name; + if (XPC_XOW_ClassNeedsXOW(name)) { + return XPC_XOW_WrapObject(cx, JS_GetGlobalForObject(cx, outerObj), vp); + } + + if (JS_ObjectIsFunction(cx, wrappedObj) && + JS_GetFunctionNative(cx, reinterpret_cast + (JS_GetPrivate(cx, wrappedObj))) == + XPCWrapper::sEvalNative) { + return XPC_XOW_WrapFunction(cx, outerObj, wrappedObj, vp); + } + + return JS_TRUE; +} + JSBool XPC_XOW_WrapFunction(JSContext *cx, JSObject *outerObj, JSObject *funobj, jsval *rval) { jsval funobjVal = OBJECT_TO_JSVAL(funobj); - JSNative native = JS_GetFunctionNative(cx, JS_ValueToFunction(cx, funobjVal)); + JSFunction *wrappedFun = reinterpret_cast(JS_GetPrivate(cx, funobj)); + JSNative native = JS_GetFunctionNative(cx, wrappedFun); if (!native || native == XPC_XOW_FunctionWrapper) { *rval = funobjVal; return JS_TRUE; } - JSFunction *wrappedFun = JS_ValueToFunction(cx, OBJECT_TO_JSVAL(funobj)); - NS_ASSERTION(wrappedFun, "We were told this was a function"); - JSFunction *funWrapper = JS_NewFunction(cx, XPC_XOW_FunctionWrapper, JS_GetFunctionArity(wrappedFun), 0, JS_GetGlobalForObject(cx, outerObj), - "Wrapped function"); - // XXX JS_GetFunctionName(wrappedFun)); + JS_GetFunctionName(wrappedFun)); if (!funWrapper) { return JS_FALSE; } @@ -366,6 +400,7 @@ XPC_XOW_WrapObject(JSContext *cx, JSObject *parent, jsval *vp) XPCWrappedNative *wn; if (!JSVAL_IS_OBJECT(*vp) || !(wrappedObj = JSVAL_TO_OBJECT(*vp)) || + JS_GET_CLASS(cx, wrappedObj) == &sXPC_XOW_JSClass.base || !(wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wrappedObj))) { return JS_TRUE; } @@ -515,6 +550,17 @@ XPC_XOW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, return JS_TRUE; } + // Don't do anything if we already resolved to a wrapped function in + // NewResolve. In practice, this means that this is a wrapped eval + // function. + jsval v = *vp; + if (!JSVAL_IS_PRIMITIVE(v) && + JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v)) && + JS_GetFunctionNative(cx, JS_ValueToFunction(cx, v)) == + XPC_XOW_FunctionWrapper) { + return JS_TRUE; + } + XPCCallContext ccx(JS_CALLER, cx); if (!ccx.IsValid()) { return ThrowException(NS_ERROR_FAILURE, cx); @@ -609,25 +655,7 @@ XPC_XOW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, } } - // Don't call XPC_XOW_RewrapIfNeeded for same origin properties. We only - // need to wrap window, document and location. - if (JSVAL_IS_PRIMITIVE(*vp)) { - return JS_TRUE; - } - - wrappedObj = JSVAL_TO_OBJECT(*vp); - if (JS_ObjectIsFunction(cx, wrappedObj) && - JS_GetFunctionNative(cx, JS_ValueToFunction(cx, *vp)) == - XPCWrapper::sEvalNative) { - return XPC_XOW_WrapFunction(cx, obj, wrappedObj, vp); - } - - const char *name = JS_GET_CLASS(cx, wrappedObj)->name; - if (XPC_XOW_ClassNeedsXOW(name)) { - return XPC_XOW_WrapObject(cx, JS_GetGlobalForObject(cx, obj), vp); - } - - return JS_TRUE; + return WrapSameOriginProp(cx, obj, vp); } JS_STATIC_DLL_CALLBACK(JSBool) diff --git a/js/src/xpconnect/src/XPCWrapper.cpp b/js/src/xpconnect/src/XPCWrapper.cpp index 2ee9bdbfe5d4..565e5a400b64 100644 --- a/js/src/xpconnect/src/XPCWrapper.cpp +++ b/js/src/xpconnect/src/XPCWrapper.cpp @@ -140,58 +140,56 @@ XPCWrapper::NewResolve(JSContext *cx, JSObject *wrapperObj, JSObject *innerObj, jsval id, uintN flags, JSObject **objp, JSBool preserveVal) { - jschar *chars = nsnull; - size_t length; - JSBool hasProp, ok; jsval v = JSVAL_VOID; - if (JSVAL_IS_STRING(id)) { - JSString *str = JSVAL_TO_STRING(id); - - chars = ::JS_GetStringChars(str); - length = ::JS_GetStringLength(str); - - ok = ::JS_HasUCProperty(cx, innerObj, chars, length, &hasProp); - if (preserveVal && ok && hasProp) { - ok = ::JS_LookupUCProperty(cx, innerObj, chars, length, &v); - } - } else if (JSVAL_IS_INT(id)) { - ok = ::JS_HasElement(cx, innerObj, JSVAL_TO_INT(id), &hasProp); - if (preserveVal && ok && hasProp) { - ok = ::JS_LookupElement(cx, innerObj, JSVAL_TO_INT(id), &v); - } - } else { - // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=381662 - // A non-string and non-int id is being resolved. We don't deal - // with those yet, return early. - - return ThrowException(NS_ERROR_INVALID_ARG, cx); + jsid interned_id; + if (!::JS_ValueToId(cx, id, &interned_id)) { + return JS_FALSE; } - if (!ok || !hasProp) { - // An error occured, or the property was not found. Return - // early. This is safe even in the case of a set operation since - // if the property doesn't exist there's no chance of a setter - // being called or any other code being run as a result of the - // set. + JSProperty *prop; + JSObject *innerObjp; + if (!OBJ_LOOKUP_PROPERTY(cx, innerObj, interned_id, &innerObjp, &prop)) { + return JS_FALSE; + } - return ok; + if (!prop) { + // Nothing to define. + return JS_TRUE; + } + + JSBool isXOW = (JS_GET_CLASS(cx, wrapperObj) == &sXPC_XOW_JSClass.base); + if (preserveVal || isXOW) { + JSScopeProperty *sprop = reinterpret_cast(prop); + if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(innerObjp))) { + v = OBJ_GET_SLOT(cx, innerObjp, sprop->slot); + } + } + + OBJ_DROP_PROPERTY(cx, innerObjp, prop); + + // Hack alert: we only do this for same-origin calls on XOWs: we want + // to preserve 'eval' function wrapper on the wrapper object itself + // to preserve eval's identity. + if (!preserveVal && isXOW && !JSVAL_IS_PRIMITIVE(v)) { + JSObject *obj = JSVAL_TO_OBJECT(v); + if (JS_ObjectIsFunction(cx, obj)) { + JSFunction *fun = reinterpret_cast(JS_GetPrivate(cx, obj)); + if (JS_GetFunctionNative(cx, fun) == sEvalNative && + !WrapFunction(cx, wrapperObj, obj, &v, JS_FALSE)) { + return JS_FALSE; + } + } } jsval oldSlotVal; if (!::JS_GetReservedSlot(cx, wrapperObj, sResolvingSlot, &oldSlotVal) || - !::JS_SetReservedSlot(cx, wrapperObj, sResolvingSlot, - BOOLEAN_TO_JSVAL(JS_TRUE))) { + !::JS_SetReservedSlot(cx, wrapperObj, sResolvingSlot, JSVAL_TRUE)) { return JS_FALSE; } - if (chars) { - ok = ::JS_DefineUCProperty(cx, wrapperObj, chars, length, v, - nsnull, nsnull, JSPROP_ENUMERATE); - } else { - ok = ::JS_DefineElement(cx, wrapperObj, JSVAL_TO_INT(id), v, - nsnull, nsnull, JSPROP_ENUMERATE); - } + JSBool ok = OBJ_DEFINE_PROPERTY(cx, wrapperObj, interned_id, v, nsnull, + nsnull, JSPROP_ENUMERATE, nsnull); if (ok && (ok = ::JS_SetReservedSlot(cx, wrapperObj, sResolvingSlot, oldSlotVal))) {