Bug 968460 - Use the incumbent global for nsGlobalWindow::GetCallerGlobal and remove JS_GetScriptedGlobal. r=bz
authorBobby Holley <bobbyholley@gmail.com>
Fri, 14 Feb 2014 22:39:33 -0800
changeset 169277 b271d6298ed1a8e09a915fc0951e83f2d1add2cb
parent 169276 032861ed6fcc09544180eda69106f6bbb325ff65
child 169278 da8ce1721f25beaee842856e3d33253fa93d6fb2
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersbz
bugs968460
milestone30.0a1
Bug 968460 - Use the incumbent global for nsGlobalWindow::GetCallerGlobal and remove JS_GetScriptedGlobal. r=bz
dom/base/ScriptSettings.h
dom/base/nsGlobalWindow.cpp
js/src/jsapi.cpp
js/src/jsapi.h
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -52,16 +52,24 @@ nsIGlobalObject* GetIncumbentGlobal();
 //   This defaults to null.
 // * When we push an Entry Point in preparation to run a JS-implemented WebIDL
 //   callback, we grab the subject principal at the time of invocation, and
 //   store that as the WebIDL Caller Principal.
 // * When non-null, callers can query this principal from script via an API on
 //   Components.utils.
 nsIPrincipal* GetWebIDLCallerPrincipal();
 
+// This may be used by callers that know that their incumbent global is non-
+// null (i.e. they know there have been no System Caller pushes since the
+// inner-most script execution).
+inline JSObject& IncumbentJSGlobal()
+{
+  return *GetIncumbentGlobal()->GetGlobalJSObject();
+}
+
 class ScriptSettingsStack;
 struct ScriptSettingsStackEntry {
   nsCOMPtr<nsIGlobalObject> mGlobalObject;
   bool mIsCandidateEntryPoint;
 
   ScriptSettingsStackEntry(nsIGlobalObject *aGlobal, bool aCandidate)
     : mGlobalObject(aGlobal)
     , mIsCandidateEntryPoint(aCandidate)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -7426,30 +7426,35 @@ JSObject* nsGlobalWindow::CallerGlobal()
     NS_ERROR("Please don't call this method from C++!");
 
     return nullptr;
   }
 
   // If somebody does sameOriginIframeWindow.postMessage(...), they probably
   // expect the .source attribute of the resulting message event to be |window|
   // rather than |sameOriginIframeWindow|, even though the transparent wrapper
-  // semantics of same-origin access will cause us to be in the iframe's cx at
-  // the time of the call. So we do some nasty poking in the JS engine and
-  // retrieve the global corresponding to the innermost scripted frame. Then,
-  // we verify that its principal is subsumed by the subject principal. If it
-  // isn't, something is screwy, and we want to clamp to the cx global.
-  JS::Rooted<JSObject*> scriptedGlobal(cx, JS_GetScriptedGlobal(cx));
-  JS::Rooted<JSObject*> cxGlobal(cx, JS::CurrentGlobalOrNull(cx));
-  nsIPrincipal* scriptedPrin = nsContentUtils::GetObjectPrincipal(scriptedGlobal);
-  nsIPrincipal* cxPrin = nsContentUtils::GetObjectPrincipal(cxGlobal);
-  if (!cxPrin->SubsumesConsideringDomain(scriptedPrin)) {
-    NS_WARNING("Something nasty is happening! Applying countermeasures...");
-    return cxGlobal;
-  }
-  return scriptedGlobal;
+  // semantics of same-origin access will cause us to be in the iframe's
+  // compartment at the time of the call. This means that we want the incumbent
+  // global here, rather than the global of the current compartment.
+  //
+  // There are various edge cases in which the incumbent global and the current
+  // global would not be same-origin. They include:
+  // * A privileged caller (System Principal or Expanded Principal) manipulating
+  //   less-privileged content via Xray Waivers.
+  // * An unprivileged caller invoking a cross-origin function that was exposed
+  //   to it by privileged code (i.e. Sandbox.importFunction).
+  //
+  // In these cases, we probably don't want the privileged global appearing in the
+  // .source attribute. So we fall back to the compartment global there.
+  JS::Rooted<JSObject*> incumbentGlobal(cx, &IncumbentJSGlobal());
+  JS::Rooted<JSObject*> compartmentGlobal(cx, JS::CurrentGlobalOrNull(cx));
+  nsIPrincipal* incumbentPrin = nsContentUtils::GetObjectPrincipal(incumbentGlobal);
+  nsIPrincipal* compartmentPrin = nsContentUtils::GetObjectPrincipal(compartmentGlobal);
+  return incumbentPrin->EqualsConsideringDomain(compartmentPrin) ? incumbentGlobal
+                                                                 : compartmentGlobal;
 }
 
 
 nsGlobalWindow*
 nsGlobalWindow::CallerInnerWindow()
 {
   JSContext *cx = nsContentUtils::GetCurrentJSContext();
   NS_ENSURE_TRUE(cx, nullptr);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6260,25 +6260,16 @@ JS_DecodeInterpretedFunction(JSContext *
 {
     XDRDecoder decoder(cx, data, length, principals, originPrincipals);
     RootedObject funobj(cx);
     if (!decoder.codeFunction(&funobj))
         return nullptr;
     return funobj;
 }
 
-JS_PUBLIC_API(JSObject *)
-JS_GetScriptedGlobal(JSContext *cx)
-{
-    ScriptFrameIter i(cx);
-    if (i.done())
-        return cx->global();
-    return &i.scopeChain()->global();
-}
-
 JS_PUBLIC_API(bool)
 JS_PreventExtensions(JSContext *cx, JS::HandleObject obj)
 {
     bool extensible;
     if (!JSObject::isExtensible(cx, obj, &extensible))
         return false;
     if (!extensible)
         return true;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1811,28 +1811,16 @@ JS_GetGlobalForCompartmentOrNull(JSConte
 namespace JS {
 
 extern JS_PUBLIC_API(JSObject *)
 CurrentGlobalOrNull(JSContext *cx);
 
 }
 
 /*
- * This method returns the global corresponding to the most recent scripted
- * frame, which may not match the cx's current compartment. This is extremely
- * dangerous, because it can bypass compartment security invariants in subtle
- * ways. To use it safely, the caller must perform a subsequent security
- * check. There is currently only one consumer of this function in Gecko, and
- * it should probably stay that way. If you'd like to use it, please consult
- * the XPConnect module owner first.
- */
-extern JS_PUBLIC_API(JSObject *)
-JS_GetScriptedGlobal(JSContext *cx);
-
-/*
  * Initialize the 'Reflect' object on a global object.
  */
 extern JS_PUBLIC_API(JSObject *)
 JS_InitReflect(JSContext *cx, JS::HandleObject global);
 
 #ifdef JS_HAS_CTYPES
 /*
  * Initialize the 'ctypes' object on a global variable 'obj'. The 'ctypes'