Bug 1144973 - Ensure that StringifySavedFrameStack puts its stack string in the cx's compartment. r=bz
authorNick Fitzgerald <fitzgen@gmail.com>
Thu, 19 Mar 2015 10:29:00 -0400
changeset 263604 f3988f82aefcca4eb63824a7eef19dc092903beb
parent 263603 a77ec216cb3e6c3cd05956240c023446a99c48b1
child 263605 1b66d8f67cf787406b7cced622755b765693713e
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1144973
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1144973 - Ensure that StringifySavedFrameStack puts its stack string in the cx's compartment. r=bz
js/src/vm/SavedStacks.cpp
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -628,59 +628,67 @@ GetSavedFrameParent(JSContext *cx, Handl
     else
         parentp.set(nullptr);
     return SavedFrameResult::Ok;
 }
 
 JS_PUBLIC_API(bool)
 StringifySavedFrameStack(JSContext *cx, HandleObject stack, MutableHandleString stringp)
 {
-    AutoMaybeEnterFrameCompartment ac(cx, stack);
-    bool skippedAsync;
-    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack, skippedAsync));
-    if (!frame) {
-        stringp.set(cx->runtime()->emptyString);
-        return true;
-    }
-
     js::StringBuffer sb(cx);
-    DebugOnly<JSSubsumesOp> subsumes = cx->runtime()->securityCallbacks->subsumes;
-    DebugOnly<JSPrincipals *> principals = cx->compartment()->principals;
-
-    js::RootedSavedFrame parent(cx);
-    do {
-        MOZ_ASSERT_IF(subsumes, (*subsumes)(principals, frame->getPrincipals()));
 
-        if (!frame->isSelfHosted()) {
-            RootedString asyncCause(cx, frame->getAsyncCause());
-            if (!asyncCause && skippedAsync) {
-                asyncCause.set(cx->names().Async);
-            }
-            js::RootedAtom name(cx, frame->getFunctionDisplayName());
-            if ((asyncCause && (!sb.append(asyncCause) || !sb.append('*')))
-                || (name && !sb.append(name))
-                || !sb.append('@')
-                || !sb.append(frame->getSource())
-                || !sb.append(':')
-                || !NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
-                || !sb.append(':')
-                || !NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
-                || !sb.append('\n'))
-            {
-                return false;
-            }
+    // Enter a new block to constrain the scope of possibly entering the stack's
+    // compartment. This ensures that when we finish the StringBuffer, we are
+    // back in the cx's original compartment, and fulfill our contract with
+    // callers to place the output string in the cx's current compartment.
+    {
+        AutoMaybeEnterFrameCompartment ac(cx, stack);
+        bool skippedAsync;
+        js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack, skippedAsync));
+        if (!frame) {
+            stringp.set(cx->runtime()->emptyString);
+            return true;
         }
 
-        parent = frame->getParent();
-        frame = js::GetFirstSubsumedFrame(cx, parent, skippedAsync);
-    } while (frame);
+        DebugOnly<JSSubsumesOp> subsumes = cx->runtime()->securityCallbacks->subsumes;
+        DebugOnly<JSPrincipals *> principals = cx->compartment()->principals;
+
+        js::RootedSavedFrame parent(cx);
+        do {
+            MOZ_ASSERT_IF(subsumes, (*subsumes)(principals, frame->getPrincipals()));
+
+            if (!frame->isSelfHosted()) {
+                RootedString asyncCause(cx, frame->getAsyncCause());
+                if (!asyncCause && skippedAsync)
+                    asyncCause.set(cx->names().Async);
+
+                js::RootedAtom name(cx, frame->getFunctionDisplayName());
+                if ((asyncCause && (!sb.append(asyncCause) || !sb.append('*')))
+                    || (name && !sb.append(name))
+                    || !sb.append('@')
+                    || !sb.append(frame->getSource())
+                    || !sb.append(':')
+                    || !NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
+                    || !sb.append(':')
+                    || !NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
+                    || !sb.append('\n'))
+                {
+                    return false;
+                }
+            }
+
+            parent = frame->getParent();
+            frame = js::GetFirstSubsumedFrame(cx, parent, skippedAsync);
+        } while (frame);
+    }
 
     JSString *str = sb.finishString();
     if (!str)
         return false;
+    assertSameCompartment(cx, str);
     stringp.set(str);
     return true;
 }
 
 } /* namespace JS */
 
 namespace js {