Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim: set ts=8 sts=4 et sw=4 tw=99: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "WaiveXrayWrapper.h"
8 : #include "FilteringWrapper.h"
9 : #include "XrayWrapper.h"
10 : #include "AccessCheck.h"
11 : #include "XPCWrapper.h"
12 : #include "ChromeObjectWrapper.h"
13 : #include "WrapperFactory.h"
14 :
15 : #include "xpcprivate.h"
16 : #include "XPCMaps.h"
17 : #include "mozilla/dom/BindingUtils.h"
18 : #include "jsfriendapi.h"
19 : #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
20 : #include "mozilla/Likely.h"
21 : #include "mozilla/dom/ScriptSettings.h"
22 : #include "nsContentUtils.h"
23 : #include "nsXULAppAPI.h"
24 :
25 : using namespace JS;
26 : using namespace js;
27 : using namespace mozilla;
28 :
29 : namespace xpc {
30 :
31 : // When chrome pulls a naked property across the membrane using
32 : // .wrappedJSObject, we want it to cross the membrane into the
33 : // chrome compartment without automatically being wrapped into an
34 : // X-ray wrapper. We achieve this by wrapping it into a special
35 : // transparent wrapper in the origin (non-chrome) compartment. When
36 : // an object with that special wrapper applied crosses into chrome,
37 : // we know to not apply an X-ray wrapper.
38 : const Wrapper XrayWaiver(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG);
39 :
40 : // When objects for which we waived the X-ray wrapper cross into
41 : // chrome, we wrap them into a special cross-compartment wrapper
42 : // that transitively extends the waiver to all properties we get
43 : // off it.
44 : const WaiveXrayWrapper WaiveXrayWrapper::singleton(0);
45 :
46 : bool
47 0 : WrapperFactory::IsCOW(JSObject* obj)
48 : {
49 0 : return IsWrapper(obj) &&
50 0 : Wrapper::wrapperHandler(obj) == &ChromeObjectWrapper::singleton;
51 : }
52 :
53 : JSObject*
54 0 : WrapperFactory::GetXrayWaiver(HandleObject obj)
55 : {
56 : // Object should come fully unwrapped but outerized.
57 0 : MOZ_ASSERT(obj == UncheckedUnwrap(obj));
58 0 : MOZ_ASSERT(!js::IsWindow(obj));
59 0 : XPCWrappedNativeScope* scope = ObjectScope(obj);
60 0 : MOZ_ASSERT(scope);
61 :
62 0 : if (!scope->mWaiverWrapperMap)
63 : return nullptr;
64 :
65 0 : return scope->mWaiverWrapperMap->Find(obj);
66 : }
67 :
68 : JSObject*
69 0 : WrapperFactory::CreateXrayWaiver(JSContext* cx, HandleObject obj)
70 : {
71 : // The caller is required to have already done a lookup.
72 : // NB: This implictly performs the assertions of GetXrayWaiver.
73 0 : MOZ_ASSERT(!GetXrayWaiver(obj));
74 0 : XPCWrappedNativeScope* scope = ObjectScope(obj);
75 :
76 0 : JSAutoRealm ar(cx, obj);
77 0 : JSObject* waiver = Wrapper::New(cx, obj, &XrayWaiver);
78 0 : if (!waiver)
79 : return nullptr;
80 :
81 : // Add the new waiver to the map. It's important that we only ever have
82 : // one waiver for the lifetime of the target object.
83 0 : if (!scope->mWaiverWrapperMap) {
84 : scope->mWaiverWrapperMap =
85 0 : JSObject2JSObjectMap::newMap(XPC_WRAPPER_MAP_LENGTH);
86 : }
87 0 : if (!scope->mWaiverWrapperMap->Add(cx, obj, waiver))
88 : return nullptr;
89 0 : return waiver;
90 : }
91 :
92 : JSObject*
93 0 : WrapperFactory::WaiveXray(JSContext* cx, JSObject* objArg)
94 : {
95 0 : RootedObject obj(cx, objArg);
96 0 : obj = UncheckedUnwrap(obj);
97 0 : MOZ_ASSERT(!js::IsWindow(obj));
98 :
99 0 : JSObject* waiver = GetXrayWaiver(obj);
100 0 : if (!waiver) {
101 0 : waiver = CreateXrayWaiver(cx, obj);
102 : }
103 0 : MOZ_ASSERT(JS::ObjectIsNotGray(waiver));
104 0 : return waiver;
105 : }
106 :
107 : /* static */ bool
108 0 : WrapperFactory::AllowWaiver(JS::Compartment* target, JS::Compartment* origin)
109 : {
110 0 : return CompartmentPrivate::Get(target)->allowWaivers &&
111 0 : AccessCheck::subsumes(target, origin);
112 : }
113 :
114 : /* static */ bool
115 0 : WrapperFactory::AllowWaiver(JSObject* wrapper) {
116 0 : MOZ_ASSERT(js::IsCrossCompartmentWrapper(wrapper));
117 0 : return AllowWaiver(js::GetObjectCompartment(wrapper),
118 0 : js::GetObjectCompartment(js::UncheckedUnwrap(wrapper)));
119 : }
120 :
121 : inline bool
122 0 : ShouldWaiveXray(JSContext* cx, JSObject* originalObj)
123 : {
124 : unsigned flags;
125 0 : (void) js::UncheckedUnwrap(originalObj, /* stopAtWindowProxy = */ true, &flags);
126 :
127 : // If the original object did not point through an Xray waiver, we're done.
128 0 : if (!(flags & WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG))
129 : return false;
130 :
131 : // If the original object was not a cross-compartment wrapper, that means
132 : // that the caller explicitly created a waiver. Preserve it so that things
133 : // like WaiveXrayAndWrap work.
134 0 : if (!(flags & Wrapper::CROSS_COMPARTMENT))
135 : return true;
136 :
137 : // Otherwise, this is a case of explicitly passing a wrapper across a
138 : // compartment boundary. In that case, we only want to preserve waivers
139 : // in transactions between same-origin compartments.
140 0 : JS::Compartment* oldCompartment = js::GetObjectCompartment(originalObj);
141 0 : JS::Compartment* newCompartment = js::GetContextCompartment(cx);
142 0 : bool sameOrigin = false;
143 0 : if (OriginAttributes::IsRestrictOpenerAccessForFPI()) {
144 : sameOrigin =
145 0 : AccessCheck::subsumesConsideringDomain(oldCompartment, newCompartment) &&
146 0 : AccessCheck::subsumesConsideringDomain(newCompartment, oldCompartment);
147 : } else {
148 : sameOrigin =
149 0 : AccessCheck::subsumesConsideringDomainIgnoringFPD(oldCompartment, newCompartment) &&
150 0 : AccessCheck::subsumesConsideringDomainIgnoringFPD(newCompartment, oldCompartment);
151 : }
152 : return sameOrigin;
153 : }
154 :
155 : void
156 0 : WrapperFactory::PrepareForWrapping(JSContext* cx, HandleObject scope,
157 : HandleObject objArg, HandleObject objectPassedToWrap,
158 : MutableHandleObject retObj)
159 : {
160 0 : bool waive = ShouldWaiveXray(cx, objectPassedToWrap);
161 0 : RootedObject obj(cx, objArg);
162 0 : retObj.set(nullptr);
163 : // Outerize any raw inner objects at the entry point here, so that we don't
164 : // have to worry about them for the rest of the wrapping code.
165 0 : if (js::IsWindow(obj)) {
166 0 : obj = js::ToWindowProxyIfWindow(obj);
167 0 : MOZ_ASSERT(obj);
168 : // ToWindowProxyIfWindow can return a CCW if |obj| was a
169 : // navigated-away-from Window. Strip any CCWs.
170 0 : obj = js::UncheckedUnwrap(obj);
171 0 : if (JS_IsDeadWrapper(obj)) {
172 0 : retObj.set(JS_NewDeadWrapper(cx, obj));
173 0 : return;
174 : }
175 0 : MOZ_ASSERT(js::IsWindowProxy(obj));
176 : // We crossed a compartment boundary there, so may now have a gray
177 : // object. This function is not allowed to return gray objects, so
178 : // don't do that.
179 0 : ExposeObjectToActiveJS(obj);
180 : }
181 :
182 : // If the object is a dead wrapper, return a new dead wrapper rather than
183 : // trying to wrap it for a different compartment.
184 0 : if (JS_IsDeadWrapper(obj)) {
185 0 : retObj.set(JS_NewDeadWrapper(cx, obj));
186 0 : return;
187 : }
188 :
189 : // If we've somehow gotten to this point after either the source or target
190 : // compartment has been nuked, return a DeadObjectProxy to prevent further
191 : // access.
192 : // However, we always need to provide live wrappers for ScriptSourceObjects,
193 : // since they're used for cross-compartment cloned scripts, and need to
194 : // remain accessible even after the original compartment has been nuked.
195 0 : JS::Compartment* origin = js::GetObjectCompartment(obj);
196 0 : JS::Compartment* target = js::GetObjectCompartment(scope);
197 0 : if (!JS_IsScriptSourceObject(obj) &&
198 0 : (CompartmentPrivate::Get(origin)->wasNuked ||
199 0 : CompartmentPrivate::Get(target)->wasNuked)) {
200 0 : NS_WARNING("Trying to create a wrapper into or out of a nuked compartment");
201 :
202 0 : retObj.set(JS_NewDeadWrapper(cx));
203 0 : return;
204 : }
205 :
206 :
207 : // If we've got a WindowProxy, there's nothing special that needs to be
208 : // done here, and we can move on to the next phase of wrapping. We handle
209 : // this case first to allow us to assert against wrappers below.
210 0 : if (js::IsWindowProxy(obj)) {
211 0 : retObj.set(waive ? WaiveXray(cx, obj) : obj);
212 0 : return;
213 : }
214 :
215 : // Here are the rules for wrapping:
216 : // We should never get a proxy here (the JS engine unwraps those for us).
217 0 : MOZ_ASSERT(!IsWrapper(obj));
218 :
219 : // Now, our object is ready to be wrapped, but several objects (notably
220 : // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of
221 : // those objects in a security wrapper, then we need to hand back the
222 : // wrapper for the new scope instead. Also, global objects don't move
223 : // between scopes so for those we also want to return the wrapper. So...
224 0 : if (!IS_WN_REFLECTOR(obj) || JS_IsGlobalObject(obj)) {
225 0 : retObj.set(waive ? WaiveXray(cx, obj) : obj);
226 0 : return;
227 : }
228 :
229 0 : XPCWrappedNative* wn = XPCWrappedNative::Get(obj);
230 :
231 0 : JSAutoRealm ar(cx, obj);
232 0 : XPCCallContext ccx(cx, obj);
233 0 : RootedObject wrapScope(cx, scope);
234 :
235 : {
236 0 : if (ccx.GetScriptable() && ccx.GetScriptable()->WantPreCreate()) {
237 : // We have a precreate hook. This object might enforce that we only
238 : // ever create JS object for it.
239 :
240 : // Note: this penalizes objects that only have one wrapper, but are
241 : // being accessed across compartments. We would really prefer to
242 : // replace the above code with a test that says "do you only have one
243 : // wrapper?"
244 0 : nsresult rv = wn->GetScriptable()->
245 0 : PreCreate(wn->Native(), cx, scope, wrapScope.address());
246 0 : if (NS_FAILED(rv)) {
247 0 : retObj.set(waive ? WaiveXray(cx, obj) : obj);
248 0 : return;
249 : }
250 :
251 : // If the handed back scope differs from the passed-in scope and is in
252 : // a separate compartment, then this object is explicitly requesting
253 : // that we don't create a second JS object for it: create a security
254 : // wrapper.
255 0 : if (js::GetObjectCompartment(scope) != js::GetObjectCompartment(wrapScope)) {
256 0 : retObj.set(waive ? WaiveXray(cx, obj) : obj);
257 0 : return;
258 : }
259 :
260 0 : RootedObject currentScope(cx, JS_GetGlobalForObject(cx, obj));
261 0 : if (MOZ_UNLIKELY(wrapScope != currentScope)) {
262 : // The wrapper claims it wants to be in the new scope, but
263 : // currently has a reflection that lives in the old scope. This
264 : // can mean one of two things, both of which are rare:
265 : //
266 : // 1 - The object has a PreCreate hook (we checked for it above),
267 : // but is deciding to request one-wrapper-per-scope (rather than
268 : // one-wrapper-per-native) for some reason. Usually, a PreCreate
269 : // hook indicates one-wrapper-per-native. In this case we want to
270 : // make a new wrapper in the new scope.
271 : //
272 : // 2 - We're midway through wrapper reparenting. The document has
273 : // moved to a new scope, but |wn| hasn't been moved yet, and
274 : // we ended up calling JS_WrapObject() on its JS object. In this
275 : // case, we want to return the existing wrapper.
276 : //
277 : // So we do a trick: call PreCreate _again_, but say that we're
278 : // wrapping for the old scope, rather than the new one. If (1) is
279 : // the case, then PreCreate will return the scope we pass to it
280 : // (the old scope). If (2) is the case, PreCreate will return the
281 : // scope of the document (the new scope).
282 0 : RootedObject probe(cx);
283 0 : rv = wn->GetScriptable()->
284 0 : PreCreate(wn->Native(), cx, currentScope, probe.address());
285 :
286 : // Check for case (2).
287 0 : if (probe != currentScope) {
288 0 : MOZ_ASSERT(probe == wrapScope);
289 0 : retObj.set(waive ? WaiveXray(cx, obj) : obj);
290 0 : return;
291 : }
292 :
293 : // Ok, must be case (1). Fall through and create a new wrapper.
294 : }
295 :
296 : // Nasty hack for late-breaking bug 781476. This will confuse identity checks,
297 : // but it's probably better than any of our alternatives.
298 : //
299 : // Note: We have to ignore domain here. The JS engine assumes that, given a
300 : // compartment c, if c->wrap(x) returns a cross-compartment wrapper at time t0,
301 : // it will also return a cross-compartment wrapper for any time t1 > t0 unless
302 : // an explicit transplant is performed. In particular, wrapper recomputation
303 : // assumes that recomputing a wrapper will always result in a wrapper.
304 : //
305 : // This doesn't actually pose a security issue, because we'll still compute
306 : // the correct (opaque) wrapper for the object below given the security
307 : // characteristics of the two compartments.
308 0 : if (!AccessCheck::isChrome(js::GetObjectCompartment(wrapScope)) &&
309 0 : AccessCheck::subsumes(js::GetObjectCompartment(wrapScope),
310 0 : js::GetObjectCompartment(obj)))
311 : {
312 0 : retObj.set(waive ? WaiveXray(cx, obj) : obj);
313 0 : return;
314 : }
315 : }
316 : }
317 :
318 : // This public WrapNativeToJSVal API enters the compartment of 'wrapScope'
319 : // so we don't have to.
320 0 : RootedValue v(cx);
321 : nsresult rv =
322 0 : nsXPConnect::XPConnect()->WrapNativeToJSVal(cx, wrapScope, wn->Native(), nullptr,
323 0 : &NS_GET_IID(nsISupports), false, &v);
324 0 : if (NS_FAILED(rv)) {
325 0 : return;
326 : }
327 :
328 0 : obj.set(&v.toObject());
329 0 : MOZ_ASSERT(IS_WN_REFLECTOR(obj), "bad object");
330 0 : MOZ_ASSERT(JS::ObjectIsNotGray(obj), "Should never return gray reflectors");
331 :
332 : // Because the underlying native didn't have a PreCreate hook, we had
333 : // to a new (or possibly pre-existing) XPCWN in our compartment.
334 : // This could be a problem for chrome code that passes XPCOM objects
335 : // across compartments, because the effects of QI would disappear across
336 : // compartments.
337 : //
338 : // So whenever we pull an XPCWN across compartments in this manner, we
339 : // give the destination object the union of the two native sets. We try
340 : // to do this cleverly in the common case to avoid too much overhead.
341 0 : XPCWrappedNative* newwn = XPCWrappedNative::Get(obj);
342 0 : RefPtr<XPCNativeSet> unionSet = XPCNativeSet::GetNewOrUsed(newwn->GetSet(),
343 0 : wn->GetSet(), false);
344 0 : if (!unionSet) {
345 0 : return;
346 : }
347 0 : newwn->SetSet(unionSet.forget());
348 :
349 0 : retObj.set(waive ? WaiveXray(cx, obj) : obj);
350 : }
351 :
352 : #ifdef DEBUG
353 : static void
354 0 : DEBUG_CheckUnwrapSafety(HandleObject obj, const js::Wrapper* handler,
355 : JS::Compartment* origin, JS::Compartment* target)
356 : {
357 0 : if (!JS_IsScriptSourceObject(obj) &&
358 0 : (CompartmentPrivate::Get(origin)->wasNuked || CompartmentPrivate::Get(target)->wasNuked)) {
359 : // If either compartment has already been nuked, we should have returned
360 : // a dead wrapper from our prewrap callback, and this function should
361 : // not be called.
362 0 : MOZ_ASSERT_UNREACHABLE("CheckUnwrapSafety called for a dead wrapper");
363 0 : } else if (AccessCheck::isChrome(target) || xpc::IsUniversalXPConnectEnabled(target)) {
364 : // If the caller is chrome (or effectively so), unwrap should always be allowed.
365 0 : MOZ_ASSERT(!handler->hasSecurityPolicy());
366 0 : } else if (CompartmentPrivate::Get(origin)->forcePermissiveCOWs) {
367 : // Similarly, if this is a privileged scope that has opted to make itself
368 : // accessible to the world (allowed only during automation), unwrap should
369 : // be allowed.
370 0 : MOZ_ASSERT(!handler->hasSecurityPolicy());
371 : } else {
372 : // Otherwise, it should depend on whether the target subsumes the origin.
373 0 : MOZ_ASSERT(handler->hasSecurityPolicy() == !(OriginAttributes::IsRestrictOpenerAccessForFPI() ?
374 : AccessCheck::subsumesConsideringDomain(target, origin) :
375 : AccessCheck::subsumesConsideringDomainIgnoringFPD(target, origin)));
376 : }
377 0 : }
378 : #else
379 : #define DEBUG_CheckUnwrapSafety(obj, handler, origin, target) {}
380 : #endif
381 :
382 : static const Wrapper*
383 0 : SelectWrapper(bool securityWrapper, XrayType xrayType, bool waiveXrays, JSObject* obj)
384 : {
385 : // Waived Xray uses a modified CCW that has transparent behavior but
386 : // transitively waives Xrays on arguments.
387 0 : if (waiveXrays) {
388 0 : MOZ_ASSERT(!securityWrapper);
389 : return &WaiveXrayWrapper::singleton;
390 : }
391 :
392 : // If we don't want or can't use Xrays, select a wrapper that's either
393 : // entirely transparent or entirely opaque.
394 0 : if (xrayType == NotXray) {
395 0 : if (!securityWrapper)
396 : return &CrossCompartmentWrapper::singleton;
397 0 : return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
398 : }
399 :
400 : // Ok, we're using Xray. If this isn't a security wrapper, use the permissive
401 : // version and skip the filter.
402 0 : if (!securityWrapper) {
403 0 : if (xrayType == XrayForDOMObject)
404 : return &PermissiveXrayDOM::singleton;
405 0 : else if (xrayType == XrayForJSObject)
406 : return &PermissiveXrayJS::singleton;
407 0 : MOZ_ASSERT(xrayType == XrayForOpaqueObject);
408 : return &PermissiveXrayOpaque::singleton;
409 : }
410 :
411 : // This is a security wrapper. Use the security versions and filter.
412 2 : if (xrayType == XrayForDOMObject && IdentifyCrossOriginObject(obj) != CrossOriginOpaque)
413 : return &FilteringWrapper<CrossOriginXrayWrapper,
414 : CrossOriginAccessiblePropertiesOnly>::singleton;
415 :
416 : // There's never any reason to expose other objects to non-subsuming actors.
417 : // Just use an opaque wrapper in these cases.
418 : //
419 : // In general, we don't want opaque function wrappers to be callable.
420 : // But in the case of XBL, we rely on content being able to invoke
421 : // functions exposed from the XBL scope. We could remove this exception,
422 : // if needed, by using ExportFunction to generate the content-side
423 : // representations of XBL methods.
424 0 : if (xrayType == XrayForJSObject && IsInContentXBLScope(obj))
425 : return &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton;
426 0 : return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
427 : }
428 :
429 : JSObject*
430 11493 : WrapperFactory::Rewrap(JSContext* cx, HandleObject existing, HandleObject obj)
431 : {
432 0 : MOZ_ASSERT(!IsWrapper(obj) ||
433 : GetProxyHandler(obj) == &XrayWaiver ||
434 : js::IsWindowProxy(obj),
435 : "wrapped object passed to rewrap");
436 11493 : MOZ_ASSERT(!js::IsWindow(obj));
437 11493 : MOZ_ASSERT(dom::IsJSAPIActive());
438 :
439 : // Compute the information we need to select the right wrapper.
440 0 : JS::Compartment* origin = js::GetObjectCompartment(obj);
441 11493 : JS::Compartment* target = js::GetContextCompartment(cx);
442 11493 : bool originIsChrome = AccessCheck::isChrome(origin);
443 0 : bool targetIsChrome = AccessCheck::isChrome(target);
444 0 : bool originSubsumesTarget = OriginAttributes::IsRestrictOpenerAccessForFPI() ?
445 : AccessCheck::subsumesConsideringDomain(origin, target) :
446 0 : AccessCheck::subsumesConsideringDomainIgnoringFPD(origin, target);
447 0 : bool targetSubsumesOrigin = OriginAttributes::IsRestrictOpenerAccessForFPI() ?
448 : AccessCheck::subsumesConsideringDomain(target, origin) :
449 0 : AccessCheck::subsumesConsideringDomainIgnoringFPD(target, origin);
450 0 : bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget;
451 :
452 : const Wrapper* wrapper;
453 :
454 : CompartmentPrivate* originCompartmentPrivate =
455 11493 : CompartmentPrivate::Get(origin);
456 : CompartmentPrivate* targetCompartmentPrivate =
457 11493 : CompartmentPrivate::Get(target);
458 :
459 : //
460 : // First, handle the special cases.
461 : //
462 :
463 : // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use
464 : // a vanilla CCW.
465 11493 : if (targetCompartmentPrivate->universalXPConnectEnabled) {
466 0 : CrashIfNotInAutomation();
467 0 : wrapper = &CrossCompartmentWrapper::singleton;
468 : }
469 :
470 : // Let the SpecialPowers scope make its stuff easily accessible to content.
471 11493 : else if (originCompartmentPrivate->forcePermissiveCOWs) {
472 0 : CrashIfNotInAutomation();
473 0 : wrapper = &CrossCompartmentWrapper::singleton;
474 : }
475 :
476 : // Special handling for chrome objects being exposed to content.
477 11493 : else if (originIsChrome && !targetIsChrome) {
478 : // If this is a chrome function being exposed to content, we need to allow
479 : // call (but nothing else). We allow CPOWs that purport to be function's
480 : // here, but only in the content process.
481 811 : if ((IdentifyStandardInstance(obj) == JSProto_Function ||
482 5 : (jsipc::IsCPOW(obj) && JS::IsCallable(obj) &&
483 0 : XRE_IsContentProcess())))
484 : {
485 : wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton;
486 : }
487 :
488 : // For vanilla JSObjects exposed from chrome to content, we use a wrapper
489 : // that fails silently in a few cases. We'd like to get rid of this eventually,
490 : // but in their current form they don't cause much trouble.
491 5 : else if (IdentifyStandardInstance(obj) == JSProto_Object) {
492 : wrapper = &ChromeObjectWrapper::singleton;
493 : }
494 :
495 : // Otherwise we get an opaque wrapper.
496 : else {
497 5 : wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
498 : }
499 : }
500 :
501 : //
502 : // Now, handle the regular cases.
503 : //
504 : // These are wrappers we can compute using a rule-based approach. In order
505 : // to do so, we need to compute some parameters.
506 : //
507 : else {
508 :
509 : // The wrapper is a security wrapper (protecting the wrappee) if and
510 : // only if the target does not subsume the origin.
511 11090 : bool securityWrapper = !targetSubsumesOrigin;
512 :
513 : // Xrays are warranted if either the target or the origin don't trust
514 : // each other. This is generally the case, unless the two are same-origin
515 : // and the caller has not requested same-origin Xrays.
516 : //
517 : // Xrays are a bidirectional protection, since it affords clarity to the
518 : // caller and privacy to the callee.
519 21338 : bool sameOriginXrays = originCompartmentPrivate->wantXrays ||
520 21338 : targetCompartmentPrivate->wantXrays;
521 11090 : bool wantXrays = !sameOrigin || sameOriginXrays;
522 :
523 0 : XrayType xrayType = wantXrays ? GetXrayType(obj) : NotXray;
524 :
525 : // If Xrays are warranted, the caller may waive them for non-security
526 : // wrappers (unless explicitly forbidden from doing so).
527 12496 : bool waiveXrays = wantXrays && !securityWrapper &&
528 12496 : targetCompartmentPrivate->allowWaivers &&
529 12496 : HasWaiveXrayFlag(obj);
530 :
531 0 : wrapper = SelectWrapper(securityWrapper, xrayType, waiveXrays, obj);
532 : }
533 :
534 0 : if (!targetSubsumesOrigin &&
535 405 : !originCompartmentPrivate->forcePermissiveCOWs) {
536 : // Do a belt-and-suspenders check against exposing eval()/Function() to
537 : // non-subsuming content. But don't worry about doing it in the
538 : // SpecialPowers case.
539 405 : if (JSFunction* fun = JS_GetObjectFunction(obj)) {
540 398 : if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) {
541 0 : NS_WARNING("Trying to expose eval or Function to non-subsuming content!");
542 0 : wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
543 : }
544 : }
545 : }
546 :
547 11493 : DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target);
548 :
549 11493 : if (existing)
550 0 : return Wrapper::Renew(existing, obj, wrapper);
551 :
552 0 : return Wrapper::New(cx, obj, wrapper);
553 : }
554 :
555 : // Call WaiveXrayAndWrap when you have a JS object that you don't want to be
556 : // wrapped in an Xray wrapper. cx->compartment is the compartment that will be
557 : // using the returned object. If the object to be wrapped is already in the
558 : // correct compartment, then this returns the unwrapped object.
559 : bool
560 218 : WrapperFactory::WaiveXrayAndWrap(JSContext* cx, MutableHandleValue vp)
561 : {
562 218 : if (vp.isPrimitive())
563 0 : return JS_WrapValue(cx, vp);
564 :
565 0 : RootedObject obj(cx, &vp.toObject());
566 0 : if (!WaiveXrayAndWrap(cx, &obj))
567 : return false;
568 :
569 0 : vp.setObject(*obj);
570 218 : return true;
571 : }
572 :
573 : bool
574 218 : WrapperFactory::WaiveXrayAndWrap(JSContext* cx, MutableHandleObject argObj)
575 : {
576 218 : MOZ_ASSERT(argObj);
577 0 : RootedObject obj(cx, js::UncheckedUnwrap(argObj));
578 218 : MOZ_ASSERT(!js::IsWindow(obj));
579 0 : if (js::IsObjectInContextCompartment(obj, cx)) {
580 0 : argObj.set(obj);
581 0 : return true;
582 : }
583 :
584 : // Even though waivers have no effect on access by scopes that don't subsume
585 : // the underlying object, good defense-in-depth dictates that we should avoid
586 : // handing out waivers to callers that can't use them. The transitive waiving
587 : // machinery unconditionally calls WaiveXrayAndWrap on return values from
588 : // waived functions, even though the return value might be not be same-origin
589 : // with the function. So if we find ourselves trying to create a waiver for
590 : // |cx|, we should check whether the caller has any business with waivers
591 : // to things in |obj|'s compartment.
592 218 : JS::Compartment* target = js::GetContextCompartment(cx);
593 436 : JS::Compartment* origin = js::GetObjectCompartment(obj);
594 654 : obj = AllowWaiver(target, origin) ? WaiveXray(cx, obj) : obj;
595 0 : if (!obj)
596 : return false;
597 :
598 0 : if (!JS_WrapObject(cx, &obj))
599 : return false;
600 218 : argObj.set(obj);
601 0 : return true;
602 : }
603 :
604 : /*
605 : * Calls to JS_TransplantObject* should go through these helpers here so that
606 : * waivers get fixed up properly.
607 : */
608 :
609 : static bool
610 0 : FixWaiverAfterTransplant(JSContext* cx, HandleObject oldWaiver, HandleObject newobj)
611 : {
612 0 : MOZ_ASSERT(Wrapper::wrapperHandler(oldWaiver) == &XrayWaiver);
613 0 : MOZ_ASSERT(!js::IsCrossCompartmentWrapper(newobj));
614 :
615 : // Create a waiver in the new compartment. We know there's not one already
616 : // because we _just_ transplanted, which means that |newobj| was either
617 : // created from scratch, or was previously cross-compartment wrapper (which
618 : // should have no waiver). CreateXrayWaiver asserts this.
619 0 : JSObject* newWaiver = WrapperFactory::CreateXrayWaiver(cx, newobj);
620 0 : if (!newWaiver)
621 : return false;
622 :
623 : // Update all the cross-compartment references to oldWaiver to point to
624 : // newWaiver.
625 0 : if (!js::RemapAllWrappersForObject(cx, oldWaiver, newWaiver))
626 : return false;
627 :
628 : // There should be no same-compartment references to oldWaiver, and we
629 : // just remapped all cross-compartment references. It's dead, so we can
630 : // remove it from the map.
631 0 : XPCWrappedNativeScope* scope = ObjectScope(oldWaiver);
632 0 : JSObject* key = Wrapper::wrappedObject(oldWaiver);
633 0 : MOZ_ASSERT(scope->mWaiverWrapperMap->Find(key));
634 0 : scope->mWaiverWrapperMap->Remove(key);
635 0 : return true;
636 : }
637 :
638 : JSObject*
639 6 : TransplantObject(JSContext* cx, JS::HandleObject origobj, JS::HandleObject target)
640 : {
641 12 : RootedObject oldWaiver(cx, WrapperFactory::GetXrayWaiver(origobj));
642 0 : RootedObject newIdentity(cx, JS_TransplantObject(cx, origobj, target));
643 12 : if (!newIdentity || !oldWaiver)
644 : return newIdentity;
645 :
646 0 : if (!FixWaiverAfterTransplant(cx, oldWaiver, newIdentity))
647 : return nullptr;
648 0 : return newIdentity;
649 : }
650 :
651 : JSObject*
652 0 : TransplantObjectRetainingXrayExpandos(JSContext* cx, JS::HandleObject origobj,
653 : JS::HandleObject target)
654 : {
655 : // Save the chain of objects that carry origobj's Xray expando properties
656 : // (from all compartments). TransplantObject will blow this away; we'll
657 : // restore it manually afterwards.
658 0 : RootedObject expandoChain(cx, GetXrayTraits(origobj)->detachExpandoChain(origobj));
659 :
660 0 : RootedObject newIdentity(cx, TransplantObject(cx, origobj, target));
661 :
662 : // Copy Xray expando properties to the new wrapper.
663 0 : if (!GetXrayTraits(newIdentity)->cloneExpandoChain(cx, newIdentity, expandoChain)) {
664 : // Failure here means some expandos were not copied over. The object graph
665 : // and the Xray machinery are left in a consistent state, but mysteriously
666 : // losing these expandos is too weird to allow.
667 0 : MOZ_CRASH();
668 : }
669 :
670 0 : return newIdentity;
671 : }
672 :
673 : nsIGlobalObject*
674 23330 : NativeGlobal(JSObject* obj)
675 : {
676 23330 : obj = js::GetGlobalForObjectCrossCompartment(obj);
677 :
678 : // Every global needs to hold a native as its private or be a
679 : // WebIDL object with an nsISupports DOM object.
680 23329 : MOZ_ASSERT((GetObjectClass(obj)->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS |
681 : JSCLASS_HAS_PRIVATE)) ||
682 : dom::UnwrapDOMObjectToISupports(obj));
683 :
684 23329 : nsISupports* native = dom::UnwrapDOMObjectToISupports(obj);
685 23330 : if (!native) {
686 22206 : native = static_cast<nsISupports*>(js::GetObjectPrivate(obj));
687 0 : MOZ_ASSERT(native);
688 :
689 : // In some cases (like for windows) it is a wrapped native,
690 : // in other cases (sandboxes, backstage passes) it's just
691 : // a direct pointer to the native. If it's a wrapped native
692 : // let's unwrap it first.
693 88824 : if (nsCOMPtr<nsIXPConnectWrappedNative> wn = do_QueryInterface(native)) {
694 42690 : native = wn->Native();
695 : }
696 : }
697 :
698 69991 : nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(native);
699 23331 : MOZ_ASSERT(global, "Native held by global needs to implement nsIGlobalObject!");
700 :
701 0 : return global;
702 : }
703 :
704 : } // namespace xpc
|