1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/xpconnect/wrappers/WrapperFactory.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,627 @@ 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 +#include "WaiveXrayWrapper.h" 1.11 +#include "FilteringWrapper.h" 1.12 +#include "XrayWrapper.h" 1.13 +#include "AccessCheck.h" 1.14 +#include "XPCWrapper.h" 1.15 +#include "ChromeObjectWrapper.h" 1.16 +#include "WrapperFactory.h" 1.17 + 1.18 +#include "xpcprivate.h" 1.19 +#include "XPCMaps.h" 1.20 +#include "mozilla/dom/BindingUtils.h" 1.21 +#include "jsfriendapi.h" 1.22 +#include "mozilla/Likely.h" 1.23 +#include "nsContentUtils.h" 1.24 + 1.25 +using namespace JS; 1.26 +using namespace js; 1.27 +using namespace mozilla; 1.28 + 1.29 +namespace xpc { 1.30 + 1.31 +// When chrome pulls a naked property across the membrane using 1.32 +// .wrappedJSObject, we want it to cross the membrane into the 1.33 +// chrome compartment without automatically being wrapped into an 1.34 +// X-ray wrapper. We achieve this by wrapping it into a special 1.35 +// transparent wrapper in the origin (non-chrome) compartment. When 1.36 +// an object with that special wrapper applied crosses into chrome, 1.37 +// we know to not apply an X-ray wrapper. 1.38 +Wrapper XrayWaiver(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG); 1.39 + 1.40 +// When objects for which we waived the X-ray wrapper cross into 1.41 +// chrome, we wrap them into a special cross-compartment wrapper 1.42 +// that transitively extends the waiver to all properties we get 1.43 +// off it. 1.44 +WaiveXrayWrapper WaiveXrayWrapper::singleton(0); 1.45 + 1.46 +bool 1.47 +WrapperFactory::IsCOW(JSObject *obj) 1.48 +{ 1.49 + return IsWrapper(obj) && 1.50 + Wrapper::wrapperHandler(obj) == &ChromeObjectWrapper::singleton; 1.51 +} 1.52 + 1.53 +JSObject * 1.54 +WrapperFactory::GetXrayWaiver(HandleObject obj) 1.55 +{ 1.56 + // Object should come fully unwrapped but outerized. 1.57 + MOZ_ASSERT(obj == UncheckedUnwrap(obj)); 1.58 + MOZ_ASSERT(!js::GetObjectClass(obj)->ext.outerObject); 1.59 + XPCWrappedNativeScope *scope = GetObjectScope(obj); 1.60 + MOZ_ASSERT(scope); 1.61 + 1.62 + if (!scope->mWaiverWrapperMap) 1.63 + return nullptr; 1.64 + 1.65 + JSObject* xrayWaiver = scope->mWaiverWrapperMap->Find(obj); 1.66 + if (xrayWaiver) 1.67 + JS::ExposeObjectToActiveJS(xrayWaiver); 1.68 + 1.69 + return xrayWaiver; 1.70 +} 1.71 + 1.72 +JSObject * 1.73 +WrapperFactory::CreateXrayWaiver(JSContext *cx, HandleObject obj) 1.74 +{ 1.75 + // The caller is required to have already done a lookup. 1.76 + // NB: This implictly performs the assertions of GetXrayWaiver. 1.77 + MOZ_ASSERT(!GetXrayWaiver(obj)); 1.78 + XPCWrappedNativeScope *scope = GetObjectScope(obj); 1.79 + 1.80 + JSAutoCompartment ac(cx, obj); 1.81 + JSObject *waiver = Wrapper::New(cx, obj, 1.82 + JS_GetGlobalForObject(cx, obj), 1.83 + &XrayWaiver); 1.84 + if (!waiver) 1.85 + return nullptr; 1.86 + 1.87 + // Add the new waiver to the map. It's important that we only ever have 1.88 + // one waiver for the lifetime of the target object. 1.89 + if (!scope->mWaiverWrapperMap) { 1.90 + scope->mWaiverWrapperMap = 1.91 + JSObject2JSObjectMap::newMap(XPC_WRAPPER_MAP_SIZE); 1.92 + MOZ_ASSERT(scope->mWaiverWrapperMap); 1.93 + } 1.94 + if (!scope->mWaiverWrapperMap->Add(cx, obj, waiver)) 1.95 + return nullptr; 1.96 + return waiver; 1.97 +} 1.98 + 1.99 +JSObject * 1.100 +WrapperFactory::WaiveXray(JSContext *cx, JSObject *objArg) 1.101 +{ 1.102 + RootedObject obj(cx, objArg); 1.103 + obj = UncheckedUnwrap(obj); 1.104 + MOZ_ASSERT(!js::IsInnerObject(obj)); 1.105 + 1.106 + JSObject *waiver = GetXrayWaiver(obj); 1.107 + if (waiver) 1.108 + return waiver; 1.109 + return CreateXrayWaiver(cx, obj); 1.110 +} 1.111 + 1.112 +// DoubleWrap is called from PrepareForWrapping to maintain the state that 1.113 +// we're supposed to waive Xray wrappers for the given on. On entrance, it 1.114 +// expects |cx->compartment != obj->compartment()|. The returned object will 1.115 +// be in the same compartment as |obj|. 1.116 +JSObject * 1.117 +WrapperFactory::DoubleWrap(JSContext *cx, HandleObject obj, unsigned flags) 1.118 +{ 1.119 + if (flags & WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG) { 1.120 + JSAutoCompartment ac(cx, obj); 1.121 + return WaiveXray(cx, obj); 1.122 + } 1.123 + return obj; 1.124 +} 1.125 + 1.126 +JSObject * 1.127 +WrapperFactory::PrepareForWrapping(JSContext *cx, HandleObject scope, 1.128 + HandleObject objArg, unsigned flags) 1.129 +{ 1.130 + RootedObject obj(cx, objArg); 1.131 + // Outerize any raw inner objects at the entry point here, so that we don't 1.132 + // have to worry about them for the rest of the wrapping code. 1.133 + if (js::IsInnerObject(obj)) { 1.134 + JSAutoCompartment ac(cx, obj); 1.135 + obj = JS_ObjectToOuterObject(cx, obj); 1.136 + NS_ENSURE_TRUE(obj, nullptr); 1.137 + // The outerization hook wraps, which means that we can end up with a 1.138 + // CCW here if |obj| was a navigated-away-from inner. Strip any CCWs. 1.139 + obj = js::UncheckedUnwrap(obj); 1.140 + MOZ_ASSERT(js::IsOuterObject(obj)); 1.141 + } 1.142 + 1.143 + // If we've got an outer window, there's nothing special that needs to be 1.144 + // done here, and we can move on to the next phase of wrapping. We handle 1.145 + // this case first to allow us to assert against wrappers below. 1.146 + if (js::IsOuterObject(obj)) 1.147 + return DoubleWrap(cx, obj, flags); 1.148 + 1.149 + // Here are the rules for wrapping: 1.150 + // We should never get a proxy here (the JS engine unwraps those for us). 1.151 + MOZ_ASSERT(!IsWrapper(obj)); 1.152 + 1.153 + // If the object being wrapped is a prototype for a standard class and the 1.154 + // wrapper does not subsumes the wrappee, use the one from the content 1.155 + // compartment. This is generally safer all-around, and in the COW case this 1.156 + // lets us safely take advantage of things like .forEach() via the 1.157 + // ChromeObjectWrapper machinery. 1.158 + // 1.159 + // If the prototype chain of chrome object |obj| looks like this: 1.160 + // 1.161 + // obj => foo => bar => chromeWin.StandardClass.prototype 1.162 + // 1.163 + // The prototype chain of COW(obj) looks lke this: 1.164 + // 1.165 + // COW(obj) => COW(foo) => COW(bar) => contentWin.StandardClass.prototype 1.166 + // 1.167 + // NB: We now remap all non-subsuming access of standard prototypes. 1.168 + // 1.169 + // NB: We need to ignore domain here so that the security relationship we 1.170 + // compute here can't change over time. See the comment above the other 1.171 + // subsumes call below. 1.172 + bool subsumes = AccessCheck::subsumes(js::GetContextCompartment(cx), 1.173 + js::GetObjectCompartment(obj)); 1.174 + XrayType xrayType = GetXrayType(obj); 1.175 + if (!subsumes && xrayType == NotXray) { 1.176 + JSProtoKey key = JSProto_Null; 1.177 + { 1.178 + JSAutoCompartment ac(cx, obj); 1.179 + key = IdentifyStandardPrototype(obj); 1.180 + } 1.181 + if (key != JSProto_Null) { 1.182 + RootedObject homeProto(cx); 1.183 + if (!JS_GetClassPrototype(cx, key, &homeProto)) 1.184 + return nullptr; 1.185 + MOZ_ASSERT(homeProto); 1.186 + // No need to double-wrap here. We should never have waivers to 1.187 + // COWs. 1.188 + return homeProto; 1.189 + } 1.190 + } 1.191 + 1.192 + // Now, our object is ready to be wrapped, but several objects (notably 1.193 + // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of 1.194 + // those objects in a security wrapper, then we need to hand back the 1.195 + // wrapper for the new scope instead. Also, global objects don't move 1.196 + // between scopes so for those we also want to return the wrapper. So... 1.197 + if (!IS_WN_REFLECTOR(obj) || !js::GetObjectParent(obj)) 1.198 + return DoubleWrap(cx, obj, flags); 1.199 + 1.200 + XPCWrappedNative *wn = XPCWrappedNative::Get(obj); 1.201 + 1.202 + JSAutoCompartment ac(cx, obj); 1.203 + XPCCallContext ccx(JS_CALLER, cx, obj); 1.204 + RootedObject wrapScope(cx, scope); 1.205 + 1.206 + { 1.207 + if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) { 1.208 + // We have a precreate hook. This object might enforce that we only 1.209 + // ever create JS object for it. 1.210 + 1.211 + // Note: this penalizes objects that only have one wrapper, but are 1.212 + // being accessed across compartments. We would really prefer to 1.213 + // replace the above code with a test that says "do you only have one 1.214 + // wrapper?" 1.215 + nsresult rv = wn->GetScriptableInfo()->GetCallback()-> 1.216 + PreCreate(wn->Native(), cx, scope, wrapScope.address()); 1.217 + NS_ENSURE_SUCCESS(rv, DoubleWrap(cx, obj, flags)); 1.218 + 1.219 + // If the handed back scope differs from the passed-in scope and is in 1.220 + // a separate compartment, then this object is explicitly requesting 1.221 + // that we don't create a second JS object for it: create a security 1.222 + // wrapper. 1.223 + if (js::GetObjectCompartment(scope) != js::GetObjectCompartment(wrapScope)) 1.224 + return DoubleWrap(cx, obj, flags); 1.225 + 1.226 + RootedObject currentScope(cx, JS_GetGlobalForObject(cx, obj)); 1.227 + if (MOZ_UNLIKELY(wrapScope != currentScope)) { 1.228 + // The wrapper claims it wants to be in the new scope, but 1.229 + // currently has a reflection that lives in the old scope. This 1.230 + // can mean one of two things, both of which are rare: 1.231 + // 1.232 + // 1 - The object has a PreCreate hook (we checked for it above), 1.233 + // but is deciding to request one-wrapper-per-scope (rather than 1.234 + // one-wrapper-per-native) for some reason. Usually, a PreCreate 1.235 + // hook indicates one-wrapper-per-native. In this case we want to 1.236 + // make a new wrapper in the new scope. 1.237 + // 1.238 + // 2 - We're midway through wrapper reparenting. The document has 1.239 + // moved to a new scope, but |wn| hasn't been moved yet, and 1.240 + // we ended up calling JS_WrapObject() on its JS object. In this 1.241 + // case, we want to return the existing wrapper. 1.242 + // 1.243 + // So we do a trick: call PreCreate _again_, but say that we're 1.244 + // wrapping for the old scope, rather than the new one. If (1) is 1.245 + // the case, then PreCreate will return the scope we pass to it 1.246 + // (the old scope). If (2) is the case, PreCreate will return the 1.247 + // scope of the document (the new scope). 1.248 + RootedObject probe(cx); 1.249 + rv = wn->GetScriptableInfo()->GetCallback()-> 1.250 + PreCreate(wn->Native(), cx, currentScope, probe.address()); 1.251 + 1.252 + // Check for case (2). 1.253 + if (probe != currentScope) { 1.254 + MOZ_ASSERT(probe == wrapScope); 1.255 + return DoubleWrap(cx, obj, flags); 1.256 + } 1.257 + 1.258 + // Ok, must be case (1). Fall through and create a new wrapper. 1.259 + } 1.260 + 1.261 + // Nasty hack for late-breaking bug 781476. This will confuse identity checks, 1.262 + // but it's probably better than any of our alternatives. 1.263 + // 1.264 + // Note: We have to ignore domain here. The JS engine assumes that, given a 1.265 + // compartment c, if c->wrap(x) returns a cross-compartment wrapper at time t0, 1.266 + // it will also return a cross-compartment wrapper for any time t1 > t0 unless 1.267 + // an explicit transplant is performed. In particular, wrapper recomputation 1.268 + // assumes that recomputing a wrapper will always result in a wrapper. 1.269 + // 1.270 + // This doesn't actually pose a security issue, because we'll still compute 1.271 + // the correct (opaque) wrapper for the object below given the security 1.272 + // characteristics of the two compartments. 1.273 + if (!AccessCheck::isChrome(js::GetObjectCompartment(wrapScope)) && 1.274 + AccessCheck::subsumes(js::GetObjectCompartment(wrapScope), 1.275 + js::GetObjectCompartment(obj))) 1.276 + { 1.277 + return DoubleWrap(cx, obj, flags); 1.278 + } 1.279 + } 1.280 + } 1.281 + 1.282 + // This public WrapNativeToJSVal API enters the compartment of 'wrapScope' 1.283 + // so we don't have to. 1.284 + RootedValue v(cx); 1.285 + nsresult rv = 1.286 + nsXPConnect::XPConnect()->WrapNativeToJSVal(cx, wrapScope, wn->Native(), nullptr, 1.287 + &NS_GET_IID(nsISupports), false, &v); 1.288 + NS_ENSURE_SUCCESS(rv, nullptr); 1.289 + 1.290 + obj.set(&v.toObject()); 1.291 + MOZ_ASSERT(IS_WN_REFLECTOR(obj), "bad object"); 1.292 + 1.293 + // Because the underlying native didn't have a PreCreate hook, we had 1.294 + // to a new (or possibly pre-existing) XPCWN in our compartment. 1.295 + // This could be a problem for chrome code that passes XPCOM objects 1.296 + // across compartments, because the effects of QI would disappear across 1.297 + // compartments. 1.298 + // 1.299 + // So whenever we pull an XPCWN across compartments in this manner, we 1.300 + // give the destination object the union of the two native sets. We try 1.301 + // to do this cleverly in the common case to avoid too much overhead. 1.302 + XPCWrappedNative *newwn = XPCWrappedNative::Get(obj); 1.303 + XPCNativeSet *unionSet = XPCNativeSet::GetNewOrUsed(newwn->GetSet(), 1.304 + wn->GetSet(), false); 1.305 + if (!unionSet) 1.306 + return nullptr; 1.307 + newwn->SetSet(unionSet); 1.308 + 1.309 + return DoubleWrap(cx, obj, flags); 1.310 +} 1.311 + 1.312 +#ifdef DEBUG 1.313 +static void 1.314 +DEBUG_CheckUnwrapSafety(HandleObject obj, js::Wrapper *handler, 1.315 + JSCompartment *origin, JSCompartment *target) 1.316 +{ 1.317 + if (AccessCheck::isChrome(target) || xpc::IsUniversalXPConnectEnabled(target)) { 1.318 + // If the caller is chrome (or effectively so), unwrap should always be allowed. 1.319 + MOZ_ASSERT(!handler->hasSecurityPolicy()); 1.320 + } else if (handler == &FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>::singleton) { 1.321 + // We explicitly use a SecurityWrapper to protect privileged callers from 1.322 + // less-privileged objects that they should never see. Skip the check in 1.323 + // this case. 1.324 + } else { 1.325 + // Otherwise, it should depend on whether the target subsumes the origin. 1.326 + MOZ_ASSERT(handler->hasSecurityPolicy() == !AccessCheck::subsumesConsideringDomain(target, origin)); 1.327 + } 1.328 +} 1.329 +#else 1.330 +#define DEBUG_CheckUnwrapSafety(obj, handler, origin, target) {} 1.331 +#endif 1.332 + 1.333 +static Wrapper * 1.334 +SelectWrapper(bool securityWrapper, bool wantXrays, XrayType xrayType, 1.335 + bool waiveXrays, bool originIsXBLScope) 1.336 +{ 1.337 + // Waived Xray uses a modified CCW that has transparent behavior but 1.338 + // transitively waives Xrays on arguments. 1.339 + if (waiveXrays) { 1.340 + MOZ_ASSERT(!securityWrapper); 1.341 + return &WaiveXrayWrapper::singleton; 1.342 + } 1.343 + 1.344 + // If we don't want or can't use Xrays, select a wrapper that's either 1.345 + // entirely transparent or entirely opaque. 1.346 + if (!wantXrays || xrayType == NotXray) { 1.347 + if (!securityWrapper) 1.348 + return &CrossCompartmentWrapper::singleton; 1.349 + // In general, we don't want opaque function wrappers to be callable. 1.350 + // But in the case of XBL, we rely on content being able to invoke 1.351 + // functions exposed from the XBL scope. We could remove this exception, 1.352 + // if needed, by using ExportFunction to generate the content-side 1.353 + // representations of XBL methods. 1.354 + else if (originIsXBLScope) 1.355 + return &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton; 1.356 + return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; 1.357 + } 1.358 + 1.359 + // Ok, we're using Xray. If this isn't a security wrapper, use the permissive 1.360 + // version and skip the filter. 1.361 + if (!securityWrapper) { 1.362 + if (xrayType == XrayForWrappedNative) 1.363 + return &PermissiveXrayXPCWN::singleton; 1.364 + else if (xrayType == XrayForDOMObject) 1.365 + return &PermissiveXrayDOM::singleton; 1.366 + MOZ_ASSERT(xrayType == XrayForJSObject); 1.367 + return &PermissiveXrayJS::singleton; 1.368 + } 1.369 + 1.370 + // This is a security wrapper. Use the security versions and filter. 1.371 + if (xrayType == XrayForWrappedNative) 1.372 + return &FilteringWrapper<SecurityXrayXPCWN, 1.373 + CrossOriginAccessiblePropertiesOnly>::singleton; 1.374 + else if (xrayType == XrayForDOMObject) 1.375 + return &FilteringWrapper<SecurityXrayDOM, 1.376 + CrossOriginAccessiblePropertiesOnly>::singleton; 1.377 + // There's never any reason to expose pure JS objects to non-subsuming actors. 1.378 + // Just use an opaque wrapper in this case. 1.379 + MOZ_ASSERT(xrayType == XrayForJSObject); 1.380 + return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; 1.381 +} 1.382 + 1.383 +JSObject * 1.384 +WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj, 1.385 + HandleObject wrappedProto, HandleObject parent, 1.386 + unsigned flags) 1.387 +{ 1.388 + MOZ_ASSERT(!IsWrapper(obj) || 1.389 + GetProxyHandler(obj) == &XrayWaiver || 1.390 + js::GetObjectClass(obj)->ext.innerObject, 1.391 + "wrapped object passed to rewrap"); 1.392 + MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj)), "trying to wrap a holder"); 1.393 + MOZ_ASSERT(!js::IsInnerObject(obj)); 1.394 + // We sometimes end up here after nsContentUtils has been shut down but before 1.395 + // XPConnect has been shut down, so check the context stack the roundabout way. 1.396 + MOZ_ASSERT(XPCJSRuntime::Get()->GetJSContextStack()->Peek() == cx); 1.397 + 1.398 + // Compute the information we need to select the right wrapper. 1.399 + JSCompartment *origin = js::GetObjectCompartment(obj); 1.400 + JSCompartment *target = js::GetContextCompartment(cx); 1.401 + bool originIsChrome = AccessCheck::isChrome(origin); 1.402 + bool targetIsChrome = AccessCheck::isChrome(target); 1.403 + bool originSubsumesTarget = AccessCheck::subsumesConsideringDomain(origin, target); 1.404 + bool targetSubsumesOrigin = AccessCheck::subsumesConsideringDomain(target, origin); 1.405 + bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget; 1.406 + XrayType xrayType = GetXrayType(obj); 1.407 + bool waiveXrayFlag = flags & WAIVE_XRAY_WRAPPER_FLAG; 1.408 + 1.409 + Wrapper *wrapper; 1.410 + CompartmentPrivate *targetdata = EnsureCompartmentPrivate(target); 1.411 + 1.412 + // 1.413 + // First, handle the special cases. 1.414 + // 1.415 + 1.416 + // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use 1.417 + // a vanilla CCW. 1.418 + if (xpc::IsUniversalXPConnectEnabled(target)) { 1.419 + wrapper = &CrossCompartmentWrapper::singleton; 1.420 + } 1.421 + 1.422 + // If this is a chrome object being exposed to content without Xrays, use 1.423 + // a COW. 1.424 + else if (originIsChrome && !targetIsChrome && xrayType == NotXray) { 1.425 + wrapper = &ChromeObjectWrapper::singleton; 1.426 + } 1.427 + 1.428 + // Normally, a non-xrayable non-waived content object that finds itself in 1.429 + // a privileged scope is wrapped with a CrossCompartmentWrapper, even though 1.430 + // the lack of a waiver _really_ should give it an opaque wrapper. This is 1.431 + // a bit too entrenched to change for content-chrome, but we can at least fix 1.432 + // it for XBL scopes. 1.433 + // 1.434 + // See bug 843829. 1.435 + else if (targetSubsumesOrigin && !originSubsumesTarget && 1.436 + !waiveXrayFlag && xrayType == NotXray && 1.437 + IsXBLScope(target)) 1.438 + { 1.439 + wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>::singleton; 1.440 + } 1.441 + 1.442 + // 1.443 + // Now, handle the regular cases. 1.444 + // 1.445 + // These are wrappers we can compute using a rule-based approach. In order 1.446 + // to do so, we need to compute some parameters. 1.447 + // 1.448 + else { 1.449 + 1.450 + // The wrapper is a security wrapper (protecting the wrappee) if and 1.451 + // only if the target does not subsume the origin. 1.452 + bool securityWrapper = !targetSubsumesOrigin; 1.453 + 1.454 + // Xrays are warranted if either the target or the origin don't trust 1.455 + // each other. This is generally the case, unless the two are same-origin 1.456 + // and the caller has not requested same-origin Xrays. 1.457 + // 1.458 + // Xrays are a bidirectional protection, since it affords clarity to the 1.459 + // caller and privacy to the callee. 1.460 + bool wantXrays = !(sameOrigin && !targetdata->wantXrays); 1.461 + 1.462 + // If Xrays are warranted, the caller may waive them for non-security 1.463 + // wrappers. 1.464 + bool waiveXrays = wantXrays && !securityWrapper && waiveXrayFlag; 1.465 + 1.466 + // We have slightly different behavior for the case when the object 1.467 + // being wrapped is in an XBL scope. 1.468 + bool originIsXBLScope = IsXBLScope(origin); 1.469 + 1.470 + wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays, 1.471 + originIsXBLScope); 1.472 + } 1.473 + 1.474 + if (!targetSubsumesOrigin) { 1.475 + // Do a belt-and-suspenders check against exposing eval()/Function() to 1.476 + // non-subsuming content. 1.477 + JSFunction *fun = JS_GetObjectFunction(obj); 1.478 + if (fun) { 1.479 + if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { 1.480 + JS_ReportError(cx, "Permission denied to expose eval or Function to non-subsuming content"); 1.481 + return nullptr; 1.482 + } 1.483 + } 1.484 + } 1.485 + 1.486 + DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target); 1.487 + 1.488 + if (existing) 1.489 + return Wrapper::Renew(cx, existing, obj, wrapper); 1.490 + 1.491 + return Wrapper::New(cx, obj, parent, wrapper); 1.492 +} 1.493 + 1.494 +// Call WaiveXrayAndWrap when you have a JS object that you don't want to be 1.495 +// wrapped in an Xray wrapper. cx->compartment is the compartment that will be 1.496 +// using the returned object. If the object to be wrapped is already in the 1.497 +// correct compartment, then this returns the unwrapped object. 1.498 +bool 1.499 +WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleValue vp) 1.500 +{ 1.501 + if (vp.isPrimitive()) 1.502 + return JS_WrapValue(cx, vp); 1.503 + 1.504 + RootedObject obj(cx, &vp.toObject()); 1.505 + if (!WaiveXrayAndWrap(cx, &obj)) 1.506 + return false; 1.507 + 1.508 + vp.setObject(*obj); 1.509 + return true; 1.510 +} 1.511 + 1.512 +bool 1.513 +WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleObject argObj) 1.514 +{ 1.515 + MOZ_ASSERT(argObj); 1.516 + RootedObject obj(cx, js::UncheckedUnwrap(argObj)); 1.517 + MOZ_ASSERT(!js::IsInnerObject(obj)); 1.518 + if (js::IsObjectInContextCompartment(obj, cx)) { 1.519 + argObj.set(obj); 1.520 + return true; 1.521 + } 1.522 + 1.523 + // Even though waivers have no effect on access by scopes that don't subsume 1.524 + // the underlying object, good defense-in-depth dictates that we should avoid 1.525 + // handing out waivers to callers that can't use them. The transitive waiving 1.526 + // machinery unconditionally calls WaiveXrayAndWrap on return values from 1.527 + // waived functions, even though the return value might be not be same-origin 1.528 + // with the function. So if we find ourselves trying to create a waiver for 1.529 + // |cx|, we should check whether the caller has any business with waivers 1.530 + // to things in |obj|'s compartment. 1.531 + JSCompartment *target = js::GetContextCompartment(cx); 1.532 + JSCompartment *origin = js::GetObjectCompartment(obj); 1.533 + obj = AccessCheck::subsumes(target, origin) ? WaiveXray(cx, obj) : obj; 1.534 + if (!obj) 1.535 + return false; 1.536 + 1.537 + if (!JS_WrapObject(cx, &obj)) 1.538 + return false; 1.539 + argObj.set(obj); 1.540 + return true; 1.541 +} 1.542 + 1.543 +bool 1.544 +WrapperFactory::XrayWrapperNotShadowing(JSObject *wrapper, jsid id) 1.545 +{ 1.546 + ResolvingId *rid = ResolvingId::getResolvingIdFromWrapper(wrapper); 1.547 + return rid->isXrayShadowing(id); 1.548 +} 1.549 + 1.550 +/* 1.551 + * Calls to JS_TransplantObject* should go through these helpers here so that 1.552 + * waivers get fixed up properly. 1.553 + */ 1.554 + 1.555 +static bool 1.556 +FixWaiverAfterTransplant(JSContext *cx, HandleObject oldWaiver, HandleObject newobj) 1.557 +{ 1.558 + MOZ_ASSERT(Wrapper::wrapperHandler(oldWaiver) == &XrayWaiver); 1.559 + MOZ_ASSERT(!js::IsCrossCompartmentWrapper(newobj)); 1.560 + 1.561 + // Create a waiver in the new compartment. We know there's not one already 1.562 + // because we _just_ transplanted, which means that |newobj| was either 1.563 + // created from scratch, or was previously cross-compartment wrapper (which 1.564 + // should have no waiver). CreateXrayWaiver asserts this. 1.565 + JSObject *newWaiver = WrapperFactory::CreateXrayWaiver(cx, newobj); 1.566 + if (!newWaiver) 1.567 + return false; 1.568 + 1.569 + // Update all the cross-compartment references to oldWaiver to point to 1.570 + // newWaiver. 1.571 + if (!js::RemapAllWrappersForObject(cx, oldWaiver, newWaiver)) 1.572 + return false; 1.573 + 1.574 + // There should be no same-compartment references to oldWaiver, and we 1.575 + // just remapped all cross-compartment references. It's dead, so we can 1.576 + // remove it from the map. 1.577 + XPCWrappedNativeScope *scope = GetObjectScope(oldWaiver); 1.578 + JSObject *key = Wrapper::wrappedObject(oldWaiver); 1.579 + MOZ_ASSERT(scope->mWaiverWrapperMap->Find(key)); 1.580 + scope->mWaiverWrapperMap->Remove(key); 1.581 + return true; 1.582 +} 1.583 + 1.584 +JSObject * 1.585 +TransplantObject(JSContext *cx, JS::HandleObject origobj, JS::HandleObject target) 1.586 +{ 1.587 + RootedObject oldWaiver(cx, WrapperFactory::GetXrayWaiver(origobj)); 1.588 + RootedObject newIdentity(cx, JS_TransplantObject(cx, origobj, target)); 1.589 + if (!newIdentity || !oldWaiver) 1.590 + return newIdentity; 1.591 + 1.592 + if (!FixWaiverAfterTransplant(cx, oldWaiver, newIdentity)) 1.593 + return nullptr; 1.594 + return newIdentity; 1.595 +} 1.596 + 1.597 +nsIGlobalObject * 1.598 +GetNativeForGlobal(JSObject *obj) 1.599 +{ 1.600 + MOZ_ASSERT(JS_IsGlobalObject(obj)); 1.601 + if (!MaybeGetObjectScope(obj)) 1.602 + return nullptr; 1.603 + 1.604 + // Every global needs to hold a native as its private or be a 1.605 + // WebIDL object with an nsISupports DOM object. 1.606 + MOZ_ASSERT((GetObjectClass(obj)->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS | 1.607 + JSCLASS_HAS_PRIVATE)) || 1.608 + dom::UnwrapDOMObjectToISupports(obj)); 1.609 + 1.610 + nsISupports *native = dom::UnwrapDOMObjectToISupports(obj); 1.611 + if (!native) { 1.612 + native = static_cast<nsISupports *>(js::GetObjectPrivate(obj)); 1.613 + MOZ_ASSERT(native); 1.614 + 1.615 + // In some cases (like for windows) it is a wrapped native, 1.616 + // in other cases (sandboxes, backstage passes) it's just 1.617 + // a direct pointer to the native. If it's a wrapped native 1.618 + // let's unwrap it first. 1.619 + if (nsCOMPtr<nsIXPConnectWrappedNative> wn = do_QueryInterface(native)) { 1.620 + native = wn->Native(); 1.621 + } 1.622 + } 1.623 + 1.624 + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(native); 1.625 + MOZ_ASSERT(global, "Native held by global needs to implement nsIGlobalObject!"); 1.626 + 1.627 + return global; 1.628 +} 1.629 + 1.630 +}