1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/xpconnect/src/XPCCallContext.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,389 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* Call context. */ 1.11 + 1.12 +#include "xpcprivate.h" 1.13 +#include "jswrapper.h" 1.14 + 1.15 +using namespace mozilla; 1.16 +using namespace xpc; 1.17 +using namespace JS; 1.18 + 1.19 +#define IS_TEAROFF_CLASS(clazz) ((clazz) == &XPC_WN_Tearoff_JSClass) 1.20 + 1.21 +XPCCallContext::XPCCallContext(XPCContext::LangType callerLanguage, 1.22 + JSContext* cx, 1.23 + HandleObject obj /* = nullptr */, 1.24 + HandleObject funobj /* = nullptr */, 1.25 + HandleId name /* = JSID_VOID */, 1.26 + unsigned argc /* = NO_ARGS */, 1.27 + jsval *argv /* = nullptr */, 1.28 + jsval *rval /* = nullptr */) 1.29 + : mAr(cx), 1.30 + mState(INIT_FAILED), 1.31 + mXPC(nsXPConnect::XPConnect()), 1.32 + mXPCContext(nullptr), 1.33 + mJSContext(cx), 1.34 + mCallerLanguage(callerLanguage), 1.35 + mFlattenedJSObject(cx), 1.36 + mWrapper(nullptr), 1.37 + mTearOff(nullptr), 1.38 + mName(cx) 1.39 +{ 1.40 + MOZ_ASSERT(cx); 1.41 + MOZ_ASSERT(cx == XPCJSRuntime::Get()->GetJSContextStack()->Peek()); 1.42 + 1.43 + if (!mXPC) 1.44 + return; 1.45 + 1.46 + mXPCContext = XPCContext::GetXPCContext(mJSContext); 1.47 + mPrevCallerLanguage = mXPCContext->SetCallingLangType(mCallerLanguage); 1.48 + 1.49 + // hook into call context chain. 1.50 + mPrevCallContext = XPCJSRuntime::Get()->SetCallContext(this); 1.51 + 1.52 + mState = HAVE_CONTEXT; 1.53 + 1.54 + if (!obj) 1.55 + return; 1.56 + 1.57 + mMethodIndex = 0xDEAD; 1.58 + 1.59 + mState = HAVE_OBJECT; 1.60 + 1.61 + mTearOff = nullptr; 1.62 + 1.63 + // If the object is a security wrapper, GetWrappedNativeOfJSObject can't 1.64 + // handle it. Do special handling here to make cross-origin Xrays work. 1.65 + JSObject *unwrapped = js::CheckedUnwrap(obj, /* stopAtOuter = */ false); 1.66 + if (!unwrapped) { 1.67 + mWrapper = UnwrapThisIfAllowed(obj, funobj, argc); 1.68 + if (!mWrapper) { 1.69 + JS_ReportError(mJSContext, "Permission denied to call method on |this|"); 1.70 + mState = INIT_FAILED; 1.71 + return; 1.72 + } 1.73 + } else { 1.74 + const js::Class *clasp = js::GetObjectClass(unwrapped); 1.75 + if (IS_WN_CLASS(clasp)) { 1.76 + mWrapper = XPCWrappedNative::Get(unwrapped); 1.77 + } else if (IS_TEAROFF_CLASS(clasp)) { 1.78 + mTearOff = (XPCWrappedNativeTearOff*)js::GetObjectPrivate(unwrapped); 1.79 + mWrapper = XPCWrappedNative::Get(js::GetObjectParent(unwrapped)); 1.80 + } 1.81 + } 1.82 + if (mWrapper) { 1.83 + mFlattenedJSObject = mWrapper->GetFlatJSObject(); 1.84 + 1.85 + if (mTearOff) 1.86 + mScriptableInfo = nullptr; 1.87 + else 1.88 + mScriptableInfo = mWrapper->GetScriptableInfo(); 1.89 + } else { 1.90 + MOZ_ASSERT(!mFlattenedJSObject, "What object do we have?"); 1.91 + } 1.92 + 1.93 + if (!JSID_IS_VOID(name)) 1.94 + SetName(name); 1.95 + 1.96 + if (argc != NO_ARGS) 1.97 + SetArgsAndResultPtr(argc, argv, rval); 1.98 + 1.99 + CHECK_STATE(HAVE_OBJECT); 1.100 +} 1.101 + 1.102 +// static 1.103 +JSContext * 1.104 +XPCCallContext::GetDefaultJSContext() 1.105 +{ 1.106 + // This is slightly questionable. If called without an explicit 1.107 + // JSContext (generally a call to a wrappedJS) we will use the JSContext 1.108 + // on the top of the JSContext stack - if there is one - *before* 1.109 + // falling back on the safe JSContext. 1.110 + // This is good AND bad because it makes calls from JS -> native -> JS 1.111 + // have JS stack 'continuity' for purposes of stack traces etc. 1.112 + // Note: this *is* what the pre-XPCCallContext xpconnect did too. 1.113 + 1.114 + XPCJSContextStack* stack = XPCJSRuntime::Get()->GetJSContextStack(); 1.115 + JSContext *topJSContext = stack->Peek(); 1.116 + 1.117 + return topJSContext ? topJSContext : stack->GetSafeJSContext(); 1.118 +} 1.119 + 1.120 +void 1.121 +XPCCallContext::SetName(jsid name) 1.122 +{ 1.123 + CHECK_STATE(HAVE_OBJECT); 1.124 + 1.125 + mName = name; 1.126 + 1.127 + if (mTearOff) { 1.128 + mSet = nullptr; 1.129 + mInterface = mTearOff->GetInterface(); 1.130 + mMember = mInterface->FindMember(mName); 1.131 + mStaticMemberIsLocal = true; 1.132 + if (mMember && !mMember->IsConstant()) 1.133 + mMethodIndex = mMember->GetIndex(); 1.134 + } else { 1.135 + mSet = mWrapper ? mWrapper->GetSet() : nullptr; 1.136 + 1.137 + if (mSet && 1.138 + mSet->FindMember(mName, &mMember, &mInterface, 1.139 + mWrapper->HasProto() ? 1.140 + mWrapper->GetProto()->GetSet() : 1.141 + nullptr, 1.142 + &mStaticMemberIsLocal)) { 1.143 + if (mMember && !mMember->IsConstant()) 1.144 + mMethodIndex = mMember->GetIndex(); 1.145 + } else { 1.146 + mMember = nullptr; 1.147 + mInterface = nullptr; 1.148 + mStaticMemberIsLocal = false; 1.149 + } 1.150 + } 1.151 + 1.152 + mState = HAVE_NAME; 1.153 +} 1.154 + 1.155 +void 1.156 +XPCCallContext::SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member, 1.157 + bool isSetter) 1.158 +{ 1.159 + CHECK_STATE(HAVE_CONTEXT); 1.160 + 1.161 + // We are going straight to the method info and need not do a lookup 1.162 + // by id. 1.163 + 1.164 + // don't be tricked if method is called with wrong 'this' 1.165 + if (mTearOff && mTearOff->GetInterface() != iface) 1.166 + mTearOff = nullptr; 1.167 + 1.168 + mSet = nullptr; 1.169 + mInterface = iface; 1.170 + mMember = member; 1.171 + mMethodIndex = mMember->GetIndex() + (isSetter ? 1 : 0); 1.172 + mName = mMember->GetName(); 1.173 + 1.174 + if (mState < HAVE_NAME) 1.175 + mState = HAVE_NAME; 1.176 +} 1.177 + 1.178 +void 1.179 +XPCCallContext::SetArgsAndResultPtr(unsigned argc, 1.180 + jsval *argv, 1.181 + jsval *rval) 1.182 +{ 1.183 + CHECK_STATE(HAVE_OBJECT); 1.184 + 1.185 + if (mState < HAVE_NAME) { 1.186 + mSet = nullptr; 1.187 + mInterface = nullptr; 1.188 + mMember = nullptr; 1.189 + mStaticMemberIsLocal = false; 1.190 + } 1.191 + 1.192 + mArgc = argc; 1.193 + mArgv = argv; 1.194 + mRetVal = rval; 1.195 + 1.196 + mState = HAVE_ARGS; 1.197 +} 1.198 + 1.199 +nsresult 1.200 +XPCCallContext::CanCallNow() 1.201 +{ 1.202 + nsresult rv; 1.203 + 1.204 + if (!HasInterfaceAndMember()) 1.205 + return NS_ERROR_UNEXPECTED; 1.206 + if (mState < HAVE_ARGS) 1.207 + return NS_ERROR_UNEXPECTED; 1.208 + 1.209 + if (!mTearOff) { 1.210 + mTearOff = mWrapper->FindTearOff(mInterface, false, &rv); 1.211 + if (!mTearOff || mTearOff->GetInterface() != mInterface) { 1.212 + mTearOff = nullptr; 1.213 + return NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED; 1.214 + } 1.215 + } 1.216 + 1.217 + // Refresh in case FindTearOff extended the set 1.218 + mSet = mWrapper->GetSet(); 1.219 + 1.220 + mState = READY_TO_CALL; 1.221 + return NS_OK; 1.222 +} 1.223 + 1.224 +void 1.225 +XPCCallContext::SystemIsBeingShutDown() 1.226 +{ 1.227 + // XXX This is pretty questionable since the per thread cleanup stuff 1.228 + // can be making this call on one thread for call contexts on another 1.229 + // thread. 1.230 + NS_WARNING("Shutting Down XPConnect even through there is a live XPCCallContext"); 1.231 + mXPCContext = nullptr; 1.232 + mState = SYSTEM_SHUTDOWN; 1.233 + if (mPrevCallContext) 1.234 + mPrevCallContext->SystemIsBeingShutDown(); 1.235 +} 1.236 + 1.237 +XPCCallContext::~XPCCallContext() 1.238 +{ 1.239 + if (mXPCContext) { 1.240 + mXPCContext->SetCallingLangType(mPrevCallerLanguage); 1.241 + 1.242 + DebugOnly<XPCCallContext*> old = XPCJSRuntime::Get()->SetCallContext(mPrevCallContext); 1.243 + MOZ_ASSERT(old == this, "bad pop from per thread data"); 1.244 + } 1.245 +} 1.246 + 1.247 +/* readonly attribute nsISupports Callee; */ 1.248 +NS_IMETHODIMP 1.249 +XPCCallContext::GetCallee(nsISupports * *aCallee) 1.250 +{ 1.251 + nsCOMPtr<nsISupports> rval = mWrapper ? mWrapper->GetIdentityObject() : nullptr; 1.252 + rval.forget(aCallee); 1.253 + return NS_OK; 1.254 +} 1.255 + 1.256 +/* readonly attribute uint16_t CalleeMethodIndex; */ 1.257 +NS_IMETHODIMP 1.258 +XPCCallContext::GetCalleeMethodIndex(uint16_t *aCalleeMethodIndex) 1.259 +{ 1.260 + *aCalleeMethodIndex = mMethodIndex; 1.261 + return NS_OK; 1.262 +} 1.263 + 1.264 +/* readonly attribute nsIXPConnectWrappedNative CalleeWrapper; */ 1.265 +NS_IMETHODIMP 1.266 +XPCCallContext::GetCalleeWrapper(nsIXPConnectWrappedNative * *aCalleeWrapper) 1.267 +{ 1.268 + nsCOMPtr<nsIXPConnectWrappedNative> rval = mWrapper; 1.269 + rval.forget(aCalleeWrapper); 1.270 + return NS_OK; 1.271 +} 1.272 + 1.273 +/* readonly attribute XPCNativeInterface CalleeInterface; */ 1.274 +NS_IMETHODIMP 1.275 +XPCCallContext::GetCalleeInterface(nsIInterfaceInfo * *aCalleeInterface) 1.276 +{ 1.277 + nsCOMPtr<nsIInterfaceInfo> rval = mInterface->GetInterfaceInfo(); 1.278 + rval.forget(aCalleeInterface); 1.279 + return NS_OK; 1.280 +} 1.281 + 1.282 +/* readonly attribute nsIClassInfo CalleeClassInfo; */ 1.283 +NS_IMETHODIMP 1.284 +XPCCallContext::GetCalleeClassInfo(nsIClassInfo * *aCalleeClassInfo) 1.285 +{ 1.286 + nsCOMPtr<nsIClassInfo> rval = mWrapper ? mWrapper->GetClassInfo() : nullptr; 1.287 + rval.forget(aCalleeClassInfo); 1.288 + return NS_OK; 1.289 +} 1.290 + 1.291 +/* readonly attribute JSContextPtr JSContext; */ 1.292 +NS_IMETHODIMP 1.293 +XPCCallContext::GetJSContext(JSContext * *aJSContext) 1.294 +{ 1.295 + JS_AbortIfWrongThread(JS_GetRuntime(mJSContext)); 1.296 + *aJSContext = mJSContext; 1.297 + return NS_OK; 1.298 +} 1.299 + 1.300 +/* readonly attribute uint32_t Argc; */ 1.301 +NS_IMETHODIMP 1.302 +XPCCallContext::GetArgc(uint32_t *aArgc) 1.303 +{ 1.304 + *aArgc = (uint32_t) mArgc; 1.305 + return NS_OK; 1.306 +} 1.307 + 1.308 +/* readonly attribute JSValPtr ArgvPtr; */ 1.309 +NS_IMETHODIMP 1.310 +XPCCallContext::GetArgvPtr(jsval * *aArgvPtr) 1.311 +{ 1.312 + *aArgvPtr = mArgv; 1.313 + return NS_OK; 1.314 +} 1.315 + 1.316 +NS_IMETHODIMP 1.317 +XPCCallContext::GetPreviousCallContext(nsAXPCNativeCallContext **aResult) 1.318 +{ 1.319 + NS_ENSURE_ARG_POINTER(aResult); 1.320 + *aResult = GetPrevCallContext(); 1.321 + return NS_OK; 1.322 +} 1.323 + 1.324 +NS_IMETHODIMP 1.325 +XPCCallContext::GetLanguage(uint16_t *aResult) 1.326 +{ 1.327 + NS_ENSURE_ARG_POINTER(aResult); 1.328 + *aResult = GetCallerLanguage(); 1.329 + return NS_OK; 1.330 +} 1.331 + 1.332 +XPCWrappedNative* 1.333 +XPCCallContext::UnwrapThisIfAllowed(HandleObject obj, HandleObject fun, unsigned argc) 1.334 +{ 1.335 + // We should only get here for objects that aren't safe to unwrap. 1.336 + MOZ_ASSERT(!js::CheckedUnwrap(obj)); 1.337 + MOZ_ASSERT(js::IsObjectInContextCompartment(obj, mJSContext)); 1.338 + 1.339 + // We can't do anything here without a function. 1.340 + if (!fun) 1.341 + return nullptr; 1.342 + 1.343 + // Determine if we're allowed to unwrap the security wrapper to invoke the 1.344 + // method. 1.345 + // 1.346 + // We have the Interface and Member that this corresponds to, but 1.347 + // unfortunately our access checks are based on the object class name and 1.348 + // property name. So we cheat a little bit here - we verify that the object 1.349 + // does indeed implement the method's Interface, and then just check that we 1.350 + // can successfully access property with method's name from the object. 1.351 + 1.352 + // First, get the XPCWN out of the underlying object. We should have a wrapper 1.353 + // here, potentially an outer window proxy, and then an XPCWN. 1.354 + MOZ_ASSERT(js::IsWrapper(obj)); 1.355 + RootedObject unwrapped(mJSContext, js::UncheckedUnwrap(obj, /* stopAtOuter = */ false)); 1.356 +#ifdef DEBUG 1.357 + JS::Rooted<JSObject*> wrappedObj(mJSContext, js::Wrapper::wrappedObject(obj)); 1.358 + MOZ_ASSERT(unwrapped == JS_ObjectToInnerObject(mJSContext, wrappedObj)); 1.359 +#endif 1.360 + 1.361 + // Make sure we have an XPCWN, and grab it. 1.362 + if (!IS_WN_REFLECTOR(unwrapped)) 1.363 + return nullptr; 1.364 + XPCWrappedNative *wn = XPCWrappedNative::Get(unwrapped); 1.365 + 1.366 + // Next, get the call info off the function object. 1.367 + XPCNativeInterface *interface; 1.368 + XPCNativeMember *member; 1.369 + XPCNativeMember::GetCallInfo(fun, &interface, &member); 1.370 + 1.371 + // To be extra safe, make sure that the underlying native implements the 1.372 + // interface before unwrapping. Even if we didn't check this, we'd still 1.373 + // theoretically fail during tearoff lookup for mismatched methods. 1.374 + if (!wn->HasInterfaceNoQI(*interface->GetIID())) 1.375 + return nullptr; 1.376 + 1.377 + // See if the access is permitted. 1.378 + // 1.379 + // NB: This calculation of SET vs GET is a bit wonky, but that's what 1.380 + // XPC_WN_GetterSetter does. 1.381 + bool set = argc && argc != NO_ARGS && member->IsWritableAttribute(); 1.382 + js::Wrapper::Action act = set ? js::Wrapper::SET : js::Wrapper::GET; 1.383 + js::Wrapper *handler = js::Wrapper::wrapperHandler(obj); 1.384 + bool ignored; 1.385 + JS::Rooted<jsid> id(mJSContext, member->GetName()); 1.386 + if (!handler->enter(mJSContext, obj, id, act, &ignored)) 1.387 + return nullptr; 1.388 + 1.389 + // Ok, this call is safe. 1.390 + return wn; 1.391 +} 1.392 +