js/xpconnect/src/XPCCallContext.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /* Call context. */
michael@0 8
michael@0 9 #include "xpcprivate.h"
michael@0 10 #include "jswrapper.h"
michael@0 11
michael@0 12 using namespace mozilla;
michael@0 13 using namespace xpc;
michael@0 14 using namespace JS;
michael@0 15
michael@0 16 #define IS_TEAROFF_CLASS(clazz) ((clazz) == &XPC_WN_Tearoff_JSClass)
michael@0 17
michael@0 18 XPCCallContext::XPCCallContext(XPCContext::LangType callerLanguage,
michael@0 19 JSContext* cx,
michael@0 20 HandleObject obj /* = nullptr */,
michael@0 21 HandleObject funobj /* = nullptr */,
michael@0 22 HandleId name /* = JSID_VOID */,
michael@0 23 unsigned argc /* = NO_ARGS */,
michael@0 24 jsval *argv /* = nullptr */,
michael@0 25 jsval *rval /* = nullptr */)
michael@0 26 : mAr(cx),
michael@0 27 mState(INIT_FAILED),
michael@0 28 mXPC(nsXPConnect::XPConnect()),
michael@0 29 mXPCContext(nullptr),
michael@0 30 mJSContext(cx),
michael@0 31 mCallerLanguage(callerLanguage),
michael@0 32 mFlattenedJSObject(cx),
michael@0 33 mWrapper(nullptr),
michael@0 34 mTearOff(nullptr),
michael@0 35 mName(cx)
michael@0 36 {
michael@0 37 MOZ_ASSERT(cx);
michael@0 38 MOZ_ASSERT(cx == XPCJSRuntime::Get()->GetJSContextStack()->Peek());
michael@0 39
michael@0 40 if (!mXPC)
michael@0 41 return;
michael@0 42
michael@0 43 mXPCContext = XPCContext::GetXPCContext(mJSContext);
michael@0 44 mPrevCallerLanguage = mXPCContext->SetCallingLangType(mCallerLanguage);
michael@0 45
michael@0 46 // hook into call context chain.
michael@0 47 mPrevCallContext = XPCJSRuntime::Get()->SetCallContext(this);
michael@0 48
michael@0 49 mState = HAVE_CONTEXT;
michael@0 50
michael@0 51 if (!obj)
michael@0 52 return;
michael@0 53
michael@0 54 mMethodIndex = 0xDEAD;
michael@0 55
michael@0 56 mState = HAVE_OBJECT;
michael@0 57
michael@0 58 mTearOff = nullptr;
michael@0 59
michael@0 60 // If the object is a security wrapper, GetWrappedNativeOfJSObject can't
michael@0 61 // handle it. Do special handling here to make cross-origin Xrays work.
michael@0 62 JSObject *unwrapped = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
michael@0 63 if (!unwrapped) {
michael@0 64 mWrapper = UnwrapThisIfAllowed(obj, funobj, argc);
michael@0 65 if (!mWrapper) {
michael@0 66 JS_ReportError(mJSContext, "Permission denied to call method on |this|");
michael@0 67 mState = INIT_FAILED;
michael@0 68 return;
michael@0 69 }
michael@0 70 } else {
michael@0 71 const js::Class *clasp = js::GetObjectClass(unwrapped);
michael@0 72 if (IS_WN_CLASS(clasp)) {
michael@0 73 mWrapper = XPCWrappedNative::Get(unwrapped);
michael@0 74 } else if (IS_TEAROFF_CLASS(clasp)) {
michael@0 75 mTearOff = (XPCWrappedNativeTearOff*)js::GetObjectPrivate(unwrapped);
michael@0 76 mWrapper = XPCWrappedNative::Get(js::GetObjectParent(unwrapped));
michael@0 77 }
michael@0 78 }
michael@0 79 if (mWrapper) {
michael@0 80 mFlattenedJSObject = mWrapper->GetFlatJSObject();
michael@0 81
michael@0 82 if (mTearOff)
michael@0 83 mScriptableInfo = nullptr;
michael@0 84 else
michael@0 85 mScriptableInfo = mWrapper->GetScriptableInfo();
michael@0 86 } else {
michael@0 87 MOZ_ASSERT(!mFlattenedJSObject, "What object do we have?");
michael@0 88 }
michael@0 89
michael@0 90 if (!JSID_IS_VOID(name))
michael@0 91 SetName(name);
michael@0 92
michael@0 93 if (argc != NO_ARGS)
michael@0 94 SetArgsAndResultPtr(argc, argv, rval);
michael@0 95
michael@0 96 CHECK_STATE(HAVE_OBJECT);
michael@0 97 }
michael@0 98
michael@0 99 // static
michael@0 100 JSContext *
michael@0 101 XPCCallContext::GetDefaultJSContext()
michael@0 102 {
michael@0 103 // This is slightly questionable. If called without an explicit
michael@0 104 // JSContext (generally a call to a wrappedJS) we will use the JSContext
michael@0 105 // on the top of the JSContext stack - if there is one - *before*
michael@0 106 // falling back on the safe JSContext.
michael@0 107 // This is good AND bad because it makes calls from JS -> native -> JS
michael@0 108 // have JS stack 'continuity' for purposes of stack traces etc.
michael@0 109 // Note: this *is* what the pre-XPCCallContext xpconnect did too.
michael@0 110
michael@0 111 XPCJSContextStack* stack = XPCJSRuntime::Get()->GetJSContextStack();
michael@0 112 JSContext *topJSContext = stack->Peek();
michael@0 113
michael@0 114 return topJSContext ? topJSContext : stack->GetSafeJSContext();
michael@0 115 }
michael@0 116
michael@0 117 void
michael@0 118 XPCCallContext::SetName(jsid name)
michael@0 119 {
michael@0 120 CHECK_STATE(HAVE_OBJECT);
michael@0 121
michael@0 122 mName = name;
michael@0 123
michael@0 124 if (mTearOff) {
michael@0 125 mSet = nullptr;
michael@0 126 mInterface = mTearOff->GetInterface();
michael@0 127 mMember = mInterface->FindMember(mName);
michael@0 128 mStaticMemberIsLocal = true;
michael@0 129 if (mMember && !mMember->IsConstant())
michael@0 130 mMethodIndex = mMember->GetIndex();
michael@0 131 } else {
michael@0 132 mSet = mWrapper ? mWrapper->GetSet() : nullptr;
michael@0 133
michael@0 134 if (mSet &&
michael@0 135 mSet->FindMember(mName, &mMember, &mInterface,
michael@0 136 mWrapper->HasProto() ?
michael@0 137 mWrapper->GetProto()->GetSet() :
michael@0 138 nullptr,
michael@0 139 &mStaticMemberIsLocal)) {
michael@0 140 if (mMember && !mMember->IsConstant())
michael@0 141 mMethodIndex = mMember->GetIndex();
michael@0 142 } else {
michael@0 143 mMember = nullptr;
michael@0 144 mInterface = nullptr;
michael@0 145 mStaticMemberIsLocal = false;
michael@0 146 }
michael@0 147 }
michael@0 148
michael@0 149 mState = HAVE_NAME;
michael@0 150 }
michael@0 151
michael@0 152 void
michael@0 153 XPCCallContext::SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member,
michael@0 154 bool isSetter)
michael@0 155 {
michael@0 156 CHECK_STATE(HAVE_CONTEXT);
michael@0 157
michael@0 158 // We are going straight to the method info and need not do a lookup
michael@0 159 // by id.
michael@0 160
michael@0 161 // don't be tricked if method is called with wrong 'this'
michael@0 162 if (mTearOff && mTearOff->GetInterface() != iface)
michael@0 163 mTearOff = nullptr;
michael@0 164
michael@0 165 mSet = nullptr;
michael@0 166 mInterface = iface;
michael@0 167 mMember = member;
michael@0 168 mMethodIndex = mMember->GetIndex() + (isSetter ? 1 : 0);
michael@0 169 mName = mMember->GetName();
michael@0 170
michael@0 171 if (mState < HAVE_NAME)
michael@0 172 mState = HAVE_NAME;
michael@0 173 }
michael@0 174
michael@0 175 void
michael@0 176 XPCCallContext::SetArgsAndResultPtr(unsigned argc,
michael@0 177 jsval *argv,
michael@0 178 jsval *rval)
michael@0 179 {
michael@0 180 CHECK_STATE(HAVE_OBJECT);
michael@0 181
michael@0 182 if (mState < HAVE_NAME) {
michael@0 183 mSet = nullptr;
michael@0 184 mInterface = nullptr;
michael@0 185 mMember = nullptr;
michael@0 186 mStaticMemberIsLocal = false;
michael@0 187 }
michael@0 188
michael@0 189 mArgc = argc;
michael@0 190 mArgv = argv;
michael@0 191 mRetVal = rval;
michael@0 192
michael@0 193 mState = HAVE_ARGS;
michael@0 194 }
michael@0 195
michael@0 196 nsresult
michael@0 197 XPCCallContext::CanCallNow()
michael@0 198 {
michael@0 199 nsresult rv;
michael@0 200
michael@0 201 if (!HasInterfaceAndMember())
michael@0 202 return NS_ERROR_UNEXPECTED;
michael@0 203 if (mState < HAVE_ARGS)
michael@0 204 return NS_ERROR_UNEXPECTED;
michael@0 205
michael@0 206 if (!mTearOff) {
michael@0 207 mTearOff = mWrapper->FindTearOff(mInterface, false, &rv);
michael@0 208 if (!mTearOff || mTearOff->GetInterface() != mInterface) {
michael@0 209 mTearOff = nullptr;
michael@0 210 return NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED;
michael@0 211 }
michael@0 212 }
michael@0 213
michael@0 214 // Refresh in case FindTearOff extended the set
michael@0 215 mSet = mWrapper->GetSet();
michael@0 216
michael@0 217 mState = READY_TO_CALL;
michael@0 218 return NS_OK;
michael@0 219 }
michael@0 220
michael@0 221 void
michael@0 222 XPCCallContext::SystemIsBeingShutDown()
michael@0 223 {
michael@0 224 // XXX This is pretty questionable since the per thread cleanup stuff
michael@0 225 // can be making this call on one thread for call contexts on another
michael@0 226 // thread.
michael@0 227 NS_WARNING("Shutting Down XPConnect even through there is a live XPCCallContext");
michael@0 228 mXPCContext = nullptr;
michael@0 229 mState = SYSTEM_SHUTDOWN;
michael@0 230 if (mPrevCallContext)
michael@0 231 mPrevCallContext->SystemIsBeingShutDown();
michael@0 232 }
michael@0 233
michael@0 234 XPCCallContext::~XPCCallContext()
michael@0 235 {
michael@0 236 if (mXPCContext) {
michael@0 237 mXPCContext->SetCallingLangType(mPrevCallerLanguage);
michael@0 238
michael@0 239 DebugOnly<XPCCallContext*> old = XPCJSRuntime::Get()->SetCallContext(mPrevCallContext);
michael@0 240 MOZ_ASSERT(old == this, "bad pop from per thread data");
michael@0 241 }
michael@0 242 }
michael@0 243
michael@0 244 /* readonly attribute nsISupports Callee; */
michael@0 245 NS_IMETHODIMP
michael@0 246 XPCCallContext::GetCallee(nsISupports * *aCallee)
michael@0 247 {
michael@0 248 nsCOMPtr<nsISupports> rval = mWrapper ? mWrapper->GetIdentityObject() : nullptr;
michael@0 249 rval.forget(aCallee);
michael@0 250 return NS_OK;
michael@0 251 }
michael@0 252
michael@0 253 /* readonly attribute uint16_t CalleeMethodIndex; */
michael@0 254 NS_IMETHODIMP
michael@0 255 XPCCallContext::GetCalleeMethodIndex(uint16_t *aCalleeMethodIndex)
michael@0 256 {
michael@0 257 *aCalleeMethodIndex = mMethodIndex;
michael@0 258 return NS_OK;
michael@0 259 }
michael@0 260
michael@0 261 /* readonly attribute nsIXPConnectWrappedNative CalleeWrapper; */
michael@0 262 NS_IMETHODIMP
michael@0 263 XPCCallContext::GetCalleeWrapper(nsIXPConnectWrappedNative * *aCalleeWrapper)
michael@0 264 {
michael@0 265 nsCOMPtr<nsIXPConnectWrappedNative> rval = mWrapper;
michael@0 266 rval.forget(aCalleeWrapper);
michael@0 267 return NS_OK;
michael@0 268 }
michael@0 269
michael@0 270 /* readonly attribute XPCNativeInterface CalleeInterface; */
michael@0 271 NS_IMETHODIMP
michael@0 272 XPCCallContext::GetCalleeInterface(nsIInterfaceInfo * *aCalleeInterface)
michael@0 273 {
michael@0 274 nsCOMPtr<nsIInterfaceInfo> rval = mInterface->GetInterfaceInfo();
michael@0 275 rval.forget(aCalleeInterface);
michael@0 276 return NS_OK;
michael@0 277 }
michael@0 278
michael@0 279 /* readonly attribute nsIClassInfo CalleeClassInfo; */
michael@0 280 NS_IMETHODIMP
michael@0 281 XPCCallContext::GetCalleeClassInfo(nsIClassInfo * *aCalleeClassInfo)
michael@0 282 {
michael@0 283 nsCOMPtr<nsIClassInfo> rval = mWrapper ? mWrapper->GetClassInfo() : nullptr;
michael@0 284 rval.forget(aCalleeClassInfo);
michael@0 285 return NS_OK;
michael@0 286 }
michael@0 287
michael@0 288 /* readonly attribute JSContextPtr JSContext; */
michael@0 289 NS_IMETHODIMP
michael@0 290 XPCCallContext::GetJSContext(JSContext * *aJSContext)
michael@0 291 {
michael@0 292 JS_AbortIfWrongThread(JS_GetRuntime(mJSContext));
michael@0 293 *aJSContext = mJSContext;
michael@0 294 return NS_OK;
michael@0 295 }
michael@0 296
michael@0 297 /* readonly attribute uint32_t Argc; */
michael@0 298 NS_IMETHODIMP
michael@0 299 XPCCallContext::GetArgc(uint32_t *aArgc)
michael@0 300 {
michael@0 301 *aArgc = (uint32_t) mArgc;
michael@0 302 return NS_OK;
michael@0 303 }
michael@0 304
michael@0 305 /* readonly attribute JSValPtr ArgvPtr; */
michael@0 306 NS_IMETHODIMP
michael@0 307 XPCCallContext::GetArgvPtr(jsval * *aArgvPtr)
michael@0 308 {
michael@0 309 *aArgvPtr = mArgv;
michael@0 310 return NS_OK;
michael@0 311 }
michael@0 312
michael@0 313 NS_IMETHODIMP
michael@0 314 XPCCallContext::GetPreviousCallContext(nsAXPCNativeCallContext **aResult)
michael@0 315 {
michael@0 316 NS_ENSURE_ARG_POINTER(aResult);
michael@0 317 *aResult = GetPrevCallContext();
michael@0 318 return NS_OK;
michael@0 319 }
michael@0 320
michael@0 321 NS_IMETHODIMP
michael@0 322 XPCCallContext::GetLanguage(uint16_t *aResult)
michael@0 323 {
michael@0 324 NS_ENSURE_ARG_POINTER(aResult);
michael@0 325 *aResult = GetCallerLanguage();
michael@0 326 return NS_OK;
michael@0 327 }
michael@0 328
michael@0 329 XPCWrappedNative*
michael@0 330 XPCCallContext::UnwrapThisIfAllowed(HandleObject obj, HandleObject fun, unsigned argc)
michael@0 331 {
michael@0 332 // We should only get here for objects that aren't safe to unwrap.
michael@0 333 MOZ_ASSERT(!js::CheckedUnwrap(obj));
michael@0 334 MOZ_ASSERT(js::IsObjectInContextCompartment(obj, mJSContext));
michael@0 335
michael@0 336 // We can't do anything here without a function.
michael@0 337 if (!fun)
michael@0 338 return nullptr;
michael@0 339
michael@0 340 // Determine if we're allowed to unwrap the security wrapper to invoke the
michael@0 341 // method.
michael@0 342 //
michael@0 343 // We have the Interface and Member that this corresponds to, but
michael@0 344 // unfortunately our access checks are based on the object class name and
michael@0 345 // property name. So we cheat a little bit here - we verify that the object
michael@0 346 // does indeed implement the method's Interface, and then just check that we
michael@0 347 // can successfully access property with method's name from the object.
michael@0 348
michael@0 349 // First, get the XPCWN out of the underlying object. We should have a wrapper
michael@0 350 // here, potentially an outer window proxy, and then an XPCWN.
michael@0 351 MOZ_ASSERT(js::IsWrapper(obj));
michael@0 352 RootedObject unwrapped(mJSContext, js::UncheckedUnwrap(obj, /* stopAtOuter = */ false));
michael@0 353 #ifdef DEBUG
michael@0 354 JS::Rooted<JSObject*> wrappedObj(mJSContext, js::Wrapper::wrappedObject(obj));
michael@0 355 MOZ_ASSERT(unwrapped == JS_ObjectToInnerObject(mJSContext, wrappedObj));
michael@0 356 #endif
michael@0 357
michael@0 358 // Make sure we have an XPCWN, and grab it.
michael@0 359 if (!IS_WN_REFLECTOR(unwrapped))
michael@0 360 return nullptr;
michael@0 361 XPCWrappedNative *wn = XPCWrappedNative::Get(unwrapped);
michael@0 362
michael@0 363 // Next, get the call info off the function object.
michael@0 364 XPCNativeInterface *interface;
michael@0 365 XPCNativeMember *member;
michael@0 366 XPCNativeMember::GetCallInfo(fun, &interface, &member);
michael@0 367
michael@0 368 // To be extra safe, make sure that the underlying native implements the
michael@0 369 // interface before unwrapping. Even if we didn't check this, we'd still
michael@0 370 // theoretically fail during tearoff lookup for mismatched methods.
michael@0 371 if (!wn->HasInterfaceNoQI(*interface->GetIID()))
michael@0 372 return nullptr;
michael@0 373
michael@0 374 // See if the access is permitted.
michael@0 375 //
michael@0 376 // NB: This calculation of SET vs GET is a bit wonky, but that's what
michael@0 377 // XPC_WN_GetterSetter does.
michael@0 378 bool set = argc && argc != NO_ARGS && member->IsWritableAttribute();
michael@0 379 js::Wrapper::Action act = set ? js::Wrapper::SET : js::Wrapper::GET;
michael@0 380 js::Wrapper *handler = js::Wrapper::wrapperHandler(obj);
michael@0 381 bool ignored;
michael@0 382 JS::Rooted<jsid> id(mJSContext, member->GetName());
michael@0 383 if (!handler->enter(mJSContext, obj, id, act, &ignored))
michael@0 384 return nullptr;
michael@0 385
michael@0 386 // Ok, this call is safe.
michael@0 387 return wn;
michael@0 388 }
michael@0 389

mercurial