diff options
Diffstat (limited to 'js/src/jit-test/tests/saved-stacks/async-principals.js')
-rw-r--r-- | js/src/jit-test/tests/saved-stacks/async-principals.js | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/saved-stacks/async-principals.js b/js/src/jit-test/tests/saved-stacks/async-principals.js new file mode 100644 index 000000000..09b8b5ce3 --- /dev/null +++ b/js/src/jit-test/tests/saved-stacks/async-principals.js @@ -0,0 +1,247 @@ +// 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 stack = saveStack(0, low); + function xToCall() { return x(stack);}; + + let stackX = callFunctionWithAsyncStack(xToCall, saveStack(), "VtoX"); + + print("high.checkVisibleStack(stackX)"); + checkVisibleStack(stackX, [ + { name: "x", asyncCause: null }, + { name: "xToCall", 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(); |