зеркало из https://github.com/mozilla/gecko-dev.git
Bug 735544 - Allow exception stacks to cross compartment boundaries. r=luke
This commit is contained in:
Родитель
80710b8eea
Коммит
ca16564715
|
@ -23,7 +23,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=246699
|
|||
**/
|
||||
function hasStack(e)
|
||||
{
|
||||
return e instanceof Error && /inciteCaps/.test(e.stack);
|
||||
return e.constructor.name === "Error" && /inciteCaps/.test(e.stack);
|
||||
}
|
||||
|
||||
function inciteCaps(f)
|
||||
|
|
|
@ -290,11 +290,42 @@ struct SuppressErrorsGuard
|
|||
}
|
||||
};
|
||||
|
||||
struct AppendArg {
|
||||
struct AppendWrappedArg {
|
||||
JSContext *cx;
|
||||
Vector<Value> &values;
|
||||
AppendArg(Vector<Value> &values) : values(values) {}
|
||||
AppendWrappedArg(JSContext *cx, Vector<Value> &values)
|
||||
: cx(cx),
|
||||
values(values)
|
||||
{}
|
||||
|
||||
bool operator()(unsigned, Value *vp) {
|
||||
return values.append(*vp);
|
||||
Value v = *vp;
|
||||
|
||||
/*
|
||||
* Try to wrap.
|
||||
*
|
||||
* If wrap() fails, there's a good chance that it's because we're
|
||||
* already in the process of throwing a native stack limit exception.
|
||||
*
|
||||
* This causes wrap() to throw, but it can't actually create an exception
|
||||
* because we're already making one here, and cx->generatingError is true.
|
||||
* So it returns false without an exception set on the stack. If we propagate
|
||||
* that, it constitutes an uncatchable exception.
|
||||
*
|
||||
* So we just ignore exceptions. If wrap actually does set a pending
|
||||
* exception, or if the caller sloppily left an exception on cx (which the
|
||||
* e4x parser does), it doesn't matter - it will be overwritten shortly.
|
||||
*
|
||||
* NB: In the sloppy e4x case, one might thing we should clear the
|
||||
* exception before calling wrap(). But wrap() has to be ok with pending
|
||||
* exceptions, since it wraps exception objects during cross-compartment
|
||||
* unwinding.
|
||||
*/
|
||||
if (!cx->compartment->wrap(cx, &v))
|
||||
v = JSVAL_VOID;
|
||||
|
||||
/* Append the value. */
|
||||
return values.append(v);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -315,16 +346,14 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
|
|||
{
|
||||
SuppressErrorsGuard seg(cx);
|
||||
for (FrameRegsIter i(cx); !i.done(); ++i) {
|
||||
/*
|
||||
* An exception object stores stack values from 'fp' which may be
|
||||
* in a different compartment from 'exnObject'. Engine compartment
|
||||
* invariants require such values to be wrapped. A simpler solution
|
||||
* is to just cut off the backtrace at compartment boundaries.
|
||||
* Also, avoid exposing values from different security principals.
|
||||
*/
|
||||
StackFrame *fp = i.fp();
|
||||
if (fp->compartment() != cx->compartment)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Ask the crystal CAPS ball whether we can see values across
|
||||
* compartment boundaries.
|
||||
*
|
||||
* NB: 'fp' may point to cross-compartment values that require wrapping.
|
||||
*/
|
||||
if (checkAccess && fp->isNonEvalFunctionFrame()) {
|
||||
Value v = NullValue();
|
||||
jsid callerid = ATOM_TO_JSID(cx->runtime->atomState.callerAtom);
|
||||
|
@ -338,7 +367,7 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
|
|||
if (fp->isNonEvalFunctionFrame()) {
|
||||
frame.funName = fp->fun()->atom ? fp->fun()->atom : cx->runtime->emptyString;
|
||||
frame.argc = fp->numActualArgs();
|
||||
if (!fp->forEachCanonicalActualArg(AppendArg(values)))
|
||||
if (!fp->forEachCanonicalActualArg(AppendWrappedArg(cx, values)))
|
||||
return false;
|
||||
} else {
|
||||
frame.funName = NULL;
|
||||
|
@ -591,7 +620,7 @@ ValueToShortSource(JSContext *cx, const Value &v)
|
|||
* memory, for too many classes (see Mozilla bug 166743).
|
||||
*/
|
||||
char buf[100];
|
||||
JS_snprintf(buf, sizeof buf, "[object %s]", obj->getClass()->name);
|
||||
JS_snprintf(buf, sizeof buf, "[object %s]", js::UnwrapObject(obj, false)->getClass()->name);
|
||||
str = JS_NewStringCopyZ(cx, buf);
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ _CHROME_FILES = \
|
|||
test_weakmaps.xul \
|
||||
test_bug706301.xul \
|
||||
test_watchpoints.xul \
|
||||
test_exnstack.xul \
|
||||
$(NULL)
|
||||
|
||||
# Disabled until this test gets updated to test the new proxy based
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=735544
|
||||
-->
|
||||
<window title="Mozilla Bug 735544"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=735544"
|
||||
target="_blank">Mozilla Bug 735544</a>
|
||||
<iframe id='ifr0' onload="frameDone(0);" src="http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_exnstack.html" />
|
||||
<iframe id='ifr1' onload="frameDone(1);" src="http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_exnstack.html" />
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
/** Test for Bug 735544 - Allow exception stacks to cross compartment boundaries **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var gFramesDone = [false, false];
|
||||
function frameDone(idx) {
|
||||
gFramesDone[idx] = true;
|
||||
if (gFramesDone[0] && gFramesDone[1])
|
||||
startTest();
|
||||
}
|
||||
|
||||
function throwAsChrome() {
|
||||
|
||||
// Grab the iframe content windows.
|
||||
var cwin0 = document.getElementById('ifr0').contentWindow;
|
||||
var cwin1 = document.getElementById('ifr1').contentWindow;
|
||||
|
||||
// Have cwin0 call a function on cwin1 that throws.
|
||||
cwin0.wrappedJSObject.doThrow(cwin1);
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
|
||||
try {
|
||||
throwAsChrome();
|
||||
ok(false, "should throw");
|
||||
} catch (e) {
|
||||
|
||||
stackFrames = e.stack.split("\n");
|
||||
|
||||
ok(/throwAsInner/.exec(stackFrames[0]),
|
||||
"The bottom frame should be thrown by the inner");
|
||||
|
||||
ok(/throwAsOuter/.exec(stackFrames[2]),
|
||||
"The 3rd-from-bottom frame should be thrown by the other");
|
||||
ok(/Window/.exec(stackFrames[2]), "Should have a |Window| argument");
|
||||
|
||||
ok(!/throwAsChrome/.exec(e.stack),
|
||||
"The entire stack should not cross into chrome.");
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
</window>
|
|
@ -96,6 +96,7 @@ _TEST_FILES = bug500931_helper.html \
|
|||
test_bug691059.html \
|
||||
file_nodelists.html \
|
||||
file_bug706301.html \
|
||||
file_exnstack.html \
|
||||
$(NULL)
|
||||
|
||||
_CHROME_FILES = \
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript">
|
||||
window.doThrow = function(other) {
|
||||
if (other)
|
||||
throwAsOuter(other);
|
||||
else
|
||||
throwAsInner();
|
||||
}
|
||||
|
||||
function throwAsInner() {
|
||||
throw Error('look at me go!');
|
||||
}
|
||||
|
||||
function throwAsOuter(other) {
|
||||
other.doThrow(null);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче