File: test_bug1969341.html

package info (click to toggle)
firefox 143.0.3-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,617,328 kB
  • sloc: cpp: 7,478,492; javascript: 6,417,157; ansic: 3,720,058; python: 1,396,372; xml: 627,523; asm: 438,677; java: 186,156; sh: 63,477; makefile: 19,171; objc: 13,059; perl: 12,983; yacc: 4,583; cs: 3,846; pascal: 3,405; lex: 1,720; ruby: 1,003; exp: 762; php: 436; lisp: 258; awk: 247; sql: 66; sed: 53; csh: 10
file content (95 lines) | stat: -rw-r--r-- 3,225 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<!DOCTYPE html>
<meta charset=utf-8>
<head>
  <title>Test for Bug 1969341</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script type="text/javascript" src="u2futil.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>

  <h1>Test for Bug 1969341</h1>
  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1969341">Mozilla Bug 1969341</a>

  <script class="testbody" type="text/javascript">
    "use strict";

    function arrivingHereIsBad(aResult) {
      ok(false, "Bad result! Received a: " + aResult);
      return Promise.resolve();
    }

    add_task(async () => {
      await addVirtualAuthenticator();
    });

    add_task(async function test_bug1969341() {
      // Bug 1969341 was an assertion crash due to a race condition. To hit the
      // assertion you had to rapidly initiate a new WebAuthn transaction
      // before the previous transaction state was fully torn down. The reproducer
      // attached to bug 1969341 kicked off a WebAuthn transaction in the getter
      // for PublicKeyCredential.then, which caused uncontrolled recursion.
      // This test case limits the recursion depth. In manual tests, 10 levels
      // was sufficient to hit the crash with high probability.
      const maxRecursionDepth = 10
      let currentRecursionDepth = 0;

      var triggerDone;
      var done = new Promise((resolve) => {
        triggerDone = resolve;
      });

      // Set up a PublicKeyCredential prior to making PublicKeyCredential thenable,
      // this lets us control when the recursion starts.
      let credential = await navigator.credentials
        .create({
          publicKey: {
            rp: { id: document.domain, name: "none" },
            user: { id: crypto.getRandomValues(new Uint8Array(16)), displayName: "A", name: "A" },
            challenge: crypto.getRandomValues(new Uint8Array(16)),
            pubKeyCredParams: [
              { type: "public-key", alg: cose_alg_ECDSA_w_SHA256 },
            ],
          },
        })

      ok(credential instanceof PublicKeyCredential, "expected PublicKeyCredential");

      Object.defineProperty(PublicKeyCredential.prototype, "then", {
        async get() {
          if (++currentRecursionDepth < maxRecursionDepth) {
            await navigator.credentials
              .get({
                publicKey: {
                  challenge: crypto.getRandomValues(new Uint8Array(16)),
                  allowCredentials: [
                    { type: "public-key", id: credential.rawId },
                  ],
                },
              })
          } else {
            triggerDone();
          }
          return undefined;
        },
      });

      ok(
        currentRecursionDepth == 0,
        "the 'PublicKeyCredential.then' getter should not have been evaluated"
      );

      // This will invoke the PublicKeyCredential.then getter.
      await credential;

      // Wait until we've reached maxRecursionDepth.
      await done;

      ok(
        currentRecursionDepth == maxRecursionDepth,
        "the 'PublicKeyCredential.then' getter should been called recursively"
      );
    });
  </script>
</body>
</html>