summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/saved-stacks/async-principals.js
diff options
context:
space:
mode:
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.js247
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 0000000000..4a253057e8
--- /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.toString());
+high.eval(b.toString());
+high.eval(c.toString());
+high.eval(d.toString());
+low .eval(e.toString());
+
+low .b = high.b;
+high.e = low .e;
+
+low .eval(u.toString());
+high.eval(v.toString());
+high.eval(w.toString());
+low .eval(x.toString());
+low .eval(y.toString());
+low .eval(z.toString());
+
+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.toString());
+high.eval(checkVisibleStack.toString());
+
+// Execute the tests.
+low.a();
+low.u();