blob: 75eced08de13b9b921e4987546b8bb71e223cbbc [file] [log] [blame]
// Test cases when a SavedFrame or one of its ancestors have a principal that is
// not subsumed by the caller's principal, when async parents are present.
function checkVisibleStack(stackFrame, expectedFrames) {
// We check toString separately from properties like asyncCause to check that
// it walks the physical SavedFrame chain consistently with the properties.
var stringFrames = stackFrame.toString().split("\n");
while (expectedFrames.length) {
let expectedFrame = expectedFrames.shift();
let stringFrame = stringFrames.shift();
// Check the frame properties.
assertEq(stackFrame.functionDisplayName, expectedFrame.name);
assertEq(stackFrame.asyncCause, expectedFrame.asyncCause);
// Check the stringified version.
let expectedStart =
(expectedFrame.asyncCause ? expectedFrame.asyncCause + "*" : "") +
expectedFrame.name;
assertEq(stringFrame.replace(/@.*$/, ""), expectedStart);
// If the next frame has an asyncCause, it should be an asyncParent.
if (expectedFrames.length && expectedFrames[0].asyncCause) {
assertEq(stackFrame.parent, null);
stackFrame = stackFrame.asyncParent;
} else {
assertEq(stackFrame.asyncParent, null);
stackFrame = stackFrame.parent;
}
}
}
var low = newGlobal({ principal: 0 });
var high = newGlobal({ principal: 0xfffff });
// Test with synchronous cross-compartment calls.
//
// With arrows representing child-to-parent links, and fat arrows representing
// child-to-async-parent links, create a SavedFrame stack like this:
//
// low.e -> high.d => high.c => high.b -> low.a
//
// This stack captured in function `e` would be seen in its complete version if
// accessed by `high`'s compartment, while in `low`'s compartment it would look
// like this:
//
// low.e => low.a
//
// The asyncCause seen on `low.a` above should not leak information about the
// real asyncCause on `high.c` and `high.d`.
//
// The stack captured in function `d` would be seen in its complete version if
// accessed by `high`'s compartment, while in `low`'s compartment it would look
// like this:
//
// low.a
// We'll move these functions into the right globals below before invoking them.
function a() {
b();
}
function b() {
callFunctionWithAsyncStack(c, saveStack(), "BtoC");
}
function c() {
callFunctionWithAsyncStack(d, saveStack(), "CtoD");
}
function d() {
let stackD = saveStack();
print("high.checkVisibleStack(stackD)");
checkVisibleStack(stackD, [
{ name: "d", asyncCause: null },
{ name: "c", asyncCause: "CtoD" },
{ name: "b", asyncCause: "BtoC" },
{ name: "a", asyncCause: null },
]);
let stackE = e(saveStack(0, low));
print("high.checkVisibleStack(stackE)");
checkVisibleStack(stackE, [
{ name: "e", asyncCause: null },
{ name: "d", asyncCause: null },
{ name: "c", asyncCause: "CtoD" },
{ name: "b", asyncCause: "BtoC" },
{ name: "a", asyncCause: null },
]);
}
function e(stackD) {
print("low.checkVisibleStack(stackD)");
checkVisibleStack(stackD, [
{ name: "a", asyncCause: "Async" },
]);
let stackE = saveStack();
print("low.checkVisibleStack(stackE)");
checkVisibleStack(stackE, [
{ name: "e", asyncCause: null },
{ name: "a", asyncCause: "Async" },
]);
return saveStack(0, high);
}
// Test with asynchronous cross-compartment calls and shared frames.
//
// With arrows representing child-to-parent links, and fat arrows representing
// child-to-async-parent links, create a SavedFrame stack like this:
//
// low.x => high.v => low.u
// low.y -> high.v => low.u
// low.z => high.w -> low.u
//
// This stack captured in functions `x`, `y`, and `z` would be seen in its
// complete version if accessed by `high`'s compartment, while in `low`'s
// compartment it would look like this:
//
// low.x => low.u
// low.y => low.u
// low.z => low.u
//
// The stack captured in function `v` would be seen in its complete version if
// accessed by `high`'s compartment, while in `low`'s compartment it would look
// like this:
//
// low.u
// We'll move these functions into the right globals below before invoking them.
function u() {
callFunctionWithAsyncStack(v, saveStack(), "UtoV");
w();
}
function v() {
let stackV = saveStack();
print("high.checkVisibleStack(stackV)");
checkVisibleStack(stackV, [
{ name: "v", asyncCause: null },
{ name: "u", asyncCause: "UtoV" },
]);
let xToCall = x.bind(undefined, saveStack(0, low));
let stackX = callFunctionWithAsyncStack(xToCall, saveStack(), "VtoX");
print("high.checkVisibleStack(stackX)");
checkVisibleStack(stackX, [
{ name: "x", asyncCause: null },
{ name: "v", asyncCause: "VtoX" },
{ name: "u", asyncCause: "UtoV" },
]);
let stackY = y();
print("high.checkVisibleStack(stackY)");
checkVisibleStack(stackY, [
{ name: "y", asyncCause: null },
{ name: "v", asyncCause: null },
{ name: "u", asyncCause: "UtoV" },
]);
}
function w() {
let stackZ = callFunctionWithAsyncStack(z, saveStack(), "WtoZ");
print("high.checkVisibleStack(stackZ)");
checkVisibleStack(stackZ, [
{ name: "z", asyncCause: null },
{ name: "w", asyncCause: "WtoZ" },
{ name: "u", asyncCause: null },
]);
}
function x(stackV) {
print("low.checkVisibleStack(stackV)");
checkVisibleStack(stackV, [
{ name: "u", asyncCause: "UtoV" },
]);
let stackX = saveStack();
print("low.checkVisibleStack(stackX)");
checkVisibleStack(stackX, [
{ name: "x", asyncCause: null },
{ name: "u", asyncCause: "UtoV" },
]);
return saveStack(0, high);
}
function y() {
let stackY = saveStack();
print("low.checkVisibleStack(stackY)");
checkVisibleStack(stackY, [
{ name: "y", asyncCause: null },
{ name: "u", asyncCause: "UtoV" },
]);
return saveStack(0, high);
}
function z() {
let stackZ = saveStack();
print("low.checkVisibleStack(stackZ)");
checkVisibleStack(stackZ, [
{ name: "z", asyncCause: null },
{ name: "u", asyncCause: "Async" },
]);
return saveStack(0, high);
}
// Split the functions in their respective globals.
low .eval(a.toSource());
high.eval(b.toSource());
high.eval(c.toSource());
high.eval(d.toSource());
low .eval(e.toSource());
low .b = high.b;
high.e = low .e;
low .eval(u.toSource());
high.eval(v.toSource());
high.eval(w.toSource());
low .eval(x.toSource());
low .eval(y.toSource());
low .eval(z.toSource());
low .v = high.v;
low .w = high.w;
high.x = low .x;
high.y = low .y;
high.z = low .z;
low .high = high;
high.low = low;
low .eval(checkVisibleStack.toSource());
high.eval(checkVisibleStack.toSource());
// Execute the tests.
low.a();
low.u();