diff --git a/caps/include/nsScriptSecurityManager.h b/caps/include/nsScriptSecurityManager.h index a1883f1362b..a0b897eeb96 100644 --- a/caps/include/nsScriptSecurityManager.h +++ b/caps/include/nsScriptSecurityManager.h @@ -409,8 +409,11 @@ private: // Returns null if a principal cannot be found; generally callers // should error out at that point. static nsIPrincipal* - doGetObjectPrincipal(JSContext *cx, JSObject *obj, - PRBool aAllowShortCircuit = PR_FALSE); + doGetObjectPrincipal(JSContext *cx, JSObject *obj +#ifdef DEBUG + , PRBool aAllowShortCircuit = PR_TRUE +#endif + ); // Returns null if a principal cannot be found. Note that rv can be NS_OK // when this happens -- this means that there was no JS running. diff --git a/caps/src/nsScriptSecurityManager.cpp b/caps/src/nsScriptSecurityManager.cpp index 151a18654fb..11b2f4954a6 100644 --- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -57,6 +57,8 @@ #include "nsDOMError.h" #include "nsDOMCID.h" #include "jsdbgapi.h" +#include "jsarena.h" +#include "jsfun.h" #include "nsIXPConnect.h" #include "nsIXPCSecurityManager.h" #include "nsTextFormatter.h" @@ -96,6 +98,14 @@ nsIXPConnect *nsScriptSecurityManager::sXPConnect = nsnull; nsIStringBundle *nsScriptSecurityManager::sStrBundle = nsnull; JSRuntime *nsScriptSecurityManager::sRuntime = 0; +// Info we need about the JSClasses used by XPConnects wrapped +// natives, to avoid having to QI to nsIXPConnectWrappedNative all the +// time when doing security checks. +static const JSClass *sXPCWrappedNativeJSClass; +static JSGetObjectOps sXPCWrappedNativeGetObjOps1; +static JSGetObjectOps sXPCWrappedNativeGetObjOps2; + + /////////////////////////// // Convenience Functions // /////////////////////////// @@ -1875,7 +1885,7 @@ nsScriptSecurityManager::GetCertificatePrincipal(const nsACString& aCertFingerpr aPrettyName, aCertificate, aURI, PR_TRUE, result); } - + nsresult nsScriptSecurityManager::DoGetCertificatePrincipal(const nsACString& aCertFingerprint, const nsACString& aSubjectName, @@ -2286,8 +2296,11 @@ nsScriptSecurityManager::GetObjectPrincipal(JSContext *aCx, JSObject *aObj, // static nsIPrincipal* -nsScriptSecurityManager::doGetObjectPrincipal(JSContext *aCx, JSObject *aObj, - PRBool aAllowShortCircuit) +nsScriptSecurityManager::doGetObjectPrincipal(JSContext *aCx, JSObject *aObj +#ifdef DEBUG + , PRBool aAllowShortCircuit +#endif + ) { NS_ASSERTION(aCx && aObj, "Bad call to doGetObjectPrincipal()!"); nsIPrincipal* result = nsnull; @@ -2296,58 +2309,125 @@ nsScriptSecurityManager::doGetObjectPrincipal(JSContext *aCx, JSObject *aObj, JSObject* origObj = aObj; #endif - do - { - const JSClass *jsClass = JS_GetClass(aCx, aObj); + const JSClass *jsClass = JS_GET_CLASS(aCx, aObj); - if (jsClass && !(~jsClass->flags & (JSCLASS_HAS_PRIVATE | - JSCLASS_PRIVATE_IS_NSISUPPORTS))) - { - // No need to refcount |priv| here. + // A common case seen in this code is that we enter this function + // with aObj being a Function object, whose parent is a Call + // object. Neither of those have object principals, so we can skip + // those objects here before we enter the below loop. That way we + // avoid wasting time checking properties of their classes etc in + // the loop. + + if (jsClass == &js_FunctionClass) { + aObj = JS_GetParent(aCx, aObj); + + if (!aObj) + return nsnull; + + jsClass = JS_GET_CLASS(aCx, aObj); + + if (jsClass == &js_CallClass) { + aObj = JS_GetParent(aCx, aObj); + + if (!aObj) + return nsnull; + + jsClass = JS_GET_CLASS(aCx, aObj); + } + } + + do { + // Note: jsClass is set before this loop, and also at the + // *end* of this loop. + + // NOTE: These class and getObjectOps hook checks better match + // what IS_WRAPPER_CLASS() does in xpconnect! + if (jsClass == sXPCWrappedNativeJSClass || + jsClass->getObjectOps == sXPCWrappedNativeGetObjOps1 || + jsClass->getObjectOps == sXPCWrappedNativeGetObjOps2) { + nsIXPConnectWrappedNative *xpcWrapper = + (nsIXPConnectWrappedNative *)JS_GetPrivate(aCx, aObj); + + if (xpcWrapper) { + nsISupports *native = xpcWrapper->Native(); + char ch = jsClass->name[0]; + + // XXXjst: Ideally this code would simply call into + // xpcWrapper->GetObjectPrincipal() and use that if we + // find a principal through that. If not, we can fall + // back to the below code. See bug 317240. + + // For classes that are unlikely to be window object + // classes (Window, ModalContentWindow, and + // ChromeWindow), check if the native pointer is an + // nsINode. Note that this is merely a performance + // optimization, so missing this optimization is + // non-critical and must result in us finding the same + // principal that we would have gotten by asking the + // nsINode here. + if (ch != 'W' && ch != 'M' && ch != 'C' +#ifdef DEBUG + && aAllowShortCircuit +#endif + ) { + nsCOMPtr node = do_QueryInterface(native); + + if (node) { + result = node->NodePrincipal(); + + // NodePrincipal() *always* returns a + // principal. + + break; + } + } + + // If not, check if it points to an + // nsIScriptObjectPrincipal + nsCOMPtr objPrin = + do_QueryInterface(native); + if (objPrin) { + result = objPrin->GetPrincipal(); + + if (result) { + break; + } + } + } + } else if (!(~jsClass->flags & (JSCLASS_HAS_PRIVATE | + JSCLASS_PRIVATE_IS_NSISUPPORTS))) { nsISupports *priv = (nsISupports *)JS_GetPrivate(aCx, aObj); - /* - * If it's a wrapped native (as most - * JSCLASS_PRIVATE_IS_NSISUPPORTS objects are in mozilla), - * check the underlying native instead. - */ - nsCOMPtr xpcWrapper = +#ifdef DEBUG + if (aAllowShortCircuit) { + nsCOMPtr xpcWrapper = + do_QueryInterface(priv); + + NS_ASSERTION(!xpcWrapper, + "Uh, an nsIXPConnectWrappedNative with the " + "wrong JSClass or getObjectOps hooks!"); + } +#endif + + nsCOMPtr objPrin = do_QueryInterface(priv); - if (NS_LIKELY(xpcWrapper != nsnull)) - { - if (NS_UNLIKELY(aAllowShortCircuit)) - { - result = xpcWrapper->GetObjectPrincipal(); - } - else - { - nsCOMPtr objPrin; - objPrin = do_QueryWrappedNative(xpcWrapper); - if (objPrin) - { - result = objPrin->GetPrincipal(); - } - } - } - else - { - nsCOMPtr objPrin; - objPrin = do_QueryInterface(priv); - if (objPrin) - { - result = objPrin->GetPrincipal(); - } - } + if (objPrin) { + result = objPrin->GetPrincipal(); - if (result) - { - break; + if (result) { + break; + } } } aObj = JS_GetParent(aCx, aObj); - } while (aObj); + + if (!aObj) + break; + + jsClass = JS_GET_CLASS(aCx, aObj); + } while (1); NS_ASSERTION(!aAllowShortCircuit || result == doGetObjectPrincipal(aCx, origObj, PR_FALSE), @@ -3232,6 +3312,10 @@ nsresult nsScriptSecurityManager::Init() #endif JS_SetCheckObjectAccessCallback(sRuntime, CheckObjectAccess); + sXPConnect->GetXPCWrappedNativeJSClassInfo(&sXPCWrappedNativeJSClass, + &sXPCWrappedNativeGetObjOps1, + &sXPCWrappedNativeGetObjOps2); + // For now, assert that no callback was set previously NS_ASSERTION(!oldCallback, "Someone already set a JS CheckObjectAccess callback"); return NS_OK; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index cc3850a6622..3b331874f1b 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -102,7 +102,7 @@ struct JSFunction { : (fun)->nargs) extern JSClass js_ArgumentsClass; -extern JSClass js_CallClass; +extern JS_FRIEND_DATA(JSClass) js_CallClass; /* JS_FRIEND_DATA so that VALUE_IS_FUNCTION is callable from the shell. */ extern JS_FRIEND_DATA(JSClass) js_FunctionClass; diff --git a/js/src/xpconnect/idl/nsIXPConnect.idl b/js/src/xpconnect/idl/nsIXPConnect.idl index 0a92519db12..0bddfaee130 100644 --- a/js/src/xpconnect/idl/nsIXPConnect.idl +++ b/js/src/xpconnect/idl/nsIXPConnect.idl @@ -62,6 +62,8 @@ [ptr] native JSObjectPtr(JSObject); [ptr] native JSValPtr(jsval); native JSVal(jsval); +[ptr] native JSClassConstPtr(const JSClass); + native JSGetObjectOps(JSGetObjectOps); native JSID(jsid); [ptr] native voidPtrPtr(void*); [ptr] native nsScriptObjectTracerPtr(nsScriptObjectTracer); @@ -206,7 +208,7 @@ interface nsIXPConnectWrappedNative : nsIXPConnectJSObjectHolder * here does NOT indicate system principal or no principals at all, just * that this wrapper doesn't have an intrinsic one. */ - virtual nsIPrincipal* GetObjectPrincipal() const = 0; + virtual nsIPrincipal* GetObjectPrincipal() const = 0; #endif protected: @@ -445,7 +447,7 @@ interface nsIXPCFunctionThisTranslator : nsISupports { 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } } %} -[uuid(9f45711b-bf4f-4af6-a71d-d165f042986d)] +[uuid(256a41a5-10e6-40a1-8392-8baa4adfabe2)] interface nsIXPConnect : nsISupports { %{ C++ @@ -751,4 +753,14 @@ interface nsIXPConnect : nsISupports */ [noscript,notxpcom] void noteJSContext(in JSContextPtr aJSContext, in nsCCTraversalCallbackRef aCb); + + /** + * Get the JSClass and JSGetObjectOps pointers to use for + * identifying JSObjects that hold nsIXPConnectWrappedNative + * pointers in their private date. See IS_WRAPPER_CLASS in + * xpcprivate.h for details. + */ + void GetXPCWrappedNativeJSClassInfo(out JSClassConstPtr clazz, + out JSGetObjectOps ops1, + out JSGetObjectOps ops2); }; diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 7e6a554eb37..7931c745aa8 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -1861,6 +1861,23 @@ nsXPConnect::EvalInSandboxObject(const nsAString& source, JSContext *cx, #endif /* XPCONNECT_STANDALONE */ } +/* void GetXPCWrappedNativeJSClassInfo(out JSClassConstPtr clazz, out JSObjectOpsConstPtr ops1, out JSObjectOpsConstPtr ops2); */ +NS_IMETHODIMP +nsXPConnect::GetXPCWrappedNativeJSClassInfo(const JSClass **clazz, + JSGetObjectOps *ops1, + JSGetObjectOps *ops2) +{ + // Expose the JSClass and JSGetObjectOps pointers used by + // IS_WRAPPER_CLASS(). If that macro ever changes, this function + // needs to stay in sync. + + *clazz = &XPC_WN_NoHelper_JSClass.base; + *ops1 = XPC_WN_GetObjectOpsNoCall; + *ops2 = XPC_WN_GetObjectOpsWithCall; + + return NS_OK; +} + /* nsIXPConnectJSObjectHolder getWrappedNativePrototype (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsIClassInfo aClassInfo); */ NS_IMETHODIMP nsXPConnect::GetWrappedNativePrototype(JSContext * aJSContext, diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 869aea1f4c1..22f734549fa 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -251,6 +251,14 @@ extern const char XPC_XPCONNECT_CONTRACTID[]; *dest = result; \ return (result || !src) ? NS_OK : NS_ERROR_OUT_OF_MEMORY + +// NOTE!!! +// +// If this ever changes, +// nsScriptSecurityManager::doGetObjectPrincipal() *must* be updated +// also! +// +// NOTE!!! #define IS_WRAPPER_CLASS(clazz) \ ((clazz) == &XPC_WN_NoHelper_JSClass.base || \ (clazz)->getObjectOps == XPC_WN_GetObjectOpsNoCall || \