High firefox UAF 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionUse-after-free in the JavaScript Engine component
ComponentDOM
Bug ClassUAF
Tracker2027541
Fix commit6164ea4bacae (firefox) +49/-0
CISA KEVNot listed
CreditedXuehao Guo
Disclosed2026-04-21

Files Changed

  • dom/base/test/jsmodules/mochitest.toml
  • dom/base/test/jsmodules/test_bug_2027541.html
diff --git a/dom/base/test/jsmodules/mochitest.toml b/dom/base/test/jsmodules/mochitest.toml
index 99b842ef1d1..a099c6af0b3 100644
--- a/dom/base/test/jsmodules/mochitest.toml
+++ b/dom/base/test/jsmodules/mochitest.toml
@@ -1,3 +1,5 @@
 [DEFAULT]
 
+["test_bug_2027541.html"]
+
 ["test_nested_modules.html"]
diff --git a/dom/base/test/jsmodules/test_bug_2027541.html b/dom/base/test/jsmodules/test_bug_2027541.html
new file mode 100644
index 00000000000..2dfbe7c6874
--- /dev/null
+++ b/dom/base/test/jsmodules/test_bug_2027541.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<head>
+  <meta charset=utf-8>
+  <title>Call import.meta.resolve after iframe removal</title>
+</head>
+<body>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+window.stolenResolve = null;
+
+async function runTest() {
+  const iframe = document.createElement("iframe");
+  iframe.srcdoc = `<!DOCTYPE html><html><body>
+    <script type="module">
+      window.parent.stolenResolve = import.meta.resolve;
+      window.parent.postMessage("ready", "*");
+    <\/script>
+    </body></html>`;
+
+  const ready = new Promise(resolve => {
+    window.addEventListener("message", () => resolve(), { once: true });
+  });
+  document.body.appendChild(iframe);
+  await ready;
+
+  ok(typeof window.stolenResolve === "function",
+     "Got import.meta.resolve from inline iframe module");
+
+  iframe.remove();
+
+  SpecialPowers.forceGC();
+  SpecialPowers.forceCC();
+  await new Promise(r => requestAnimationFrame(r));
+
+  let result = window.stolenResolve("https://example.com/");
+  is(result, "https://example.com/",
+     "import.meta.resolve returns correct result after iframe removal and GC");
+
+  window.stolenResolve = null;
+  SimpleTest.finish();
+}
+
+runTest();
+</script>
+</body>
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/dom/base/test/jsmodules/mochitest.toml b/dom/base/test/jsmodules/mochitest.toml
index 99b842ef1d1..a099c6af0b3 100644
--- a/dom/base/test/jsmodules/mochitest.toml
+++ b/dom/base/test/jsmodules/mochitest.toml
@@ -1,3 +1,5 @@
 [DEFAULT]
 
+["test_bug_2027541.html"]
+
 ["test_nested_modules.html"]
diff --git a/dom/base/test/jsmodules/test_bug_2027541.html b/dom/base/test/jsmodules/test_bug_2027541.html
new file mode 100644
index 00000000000..2dfbe7c6874
--- /dev/null
+++ b/dom/base/test/jsmodules/test_bug_2027541.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<head>
+  <meta charset=utf-8>
+  <title>Call import.meta.resolve after iframe removal</title>
+</head>
+<body>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+window.stolenResolve = null;
+
+async function runTest() {
+  const iframe = document.createElement("iframe");
+  iframe.srcdoc = `<!DOCTYPE html><html><body>
+    <script type="module">
+      window.parent.stolenResolve = import.meta.resolve;
+      window.parent.postMessage("ready", "*");
+    <\/script>
+    </body></html>`;
+
+  const ready = new Promise(resolve => {
+    window.addEventListener("message", () => resolve(), { once: true });
+  });
+  document.body.appendChild(iframe);
+  await ready;
+
+  ok(typeof window.stolenResolve === "function",
+     "Got import.meta.resolve from inline iframe module");
+
+  iframe.remove();
+
+  SpecialPowers.forceGC();
+  SpecialPowers.forceCC();
+  await new Promise(r => requestAnimationFrame(r));
+
+  let result = window.stolenResolve("https://example.com/");
+  is(result, "https://example.com/",
+     "import.meta.resolve returns correct result after iframe removal and GC");
+
+  window.stolenResolve = null;
+  SimpleTest.finish();
+}
+
+runTest();
+</script>
+</body>
Loading diff…