Medium firefox UAF 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionParsing a JavaScript module as JSON could, under some circumstances, cause cross-compartment access, which may result in a use-after-free.
ComponentSpiderMonkey
Bug ClassUAF
Tracker1929623
Fix commit481505b2cb40 (firefox) +24/-6
CISA KEVNot listed
CreditedNils Bars
Disclosed2025-01-07

Files Changed

  • js/src/jit-test/tests/modules/evaluation-result.js
  • js/src/testcase.js
  • js/src/vm/Modules.cpp
diff --git a/js/src/jit-test/tests/modules/evaluation-result.js b/js/src/jit-test/tests/modules/evaluation-result.js
new file mode 100644
index 00000000000..6020403a0f2
--- /dev/null
+++ b/js/src/jit-test/tests/modules/evaluation-result.js
@@ -0,0 +1,11 @@
+async function parseAndEvaluate(source, filename, kind) {
+    let m = parseModule(source, filename, kind);
+    moduleLink(m);
+    return await moduleEvaluate(m);
+}
+
+(async () => {
+  assertEq(await parseAndEvaluate("[]", "", "js"), undefined);
+  assertEq(await parseAndEvaluate("[]", "", "json"), undefined);
+})();
+drainJobQueue();
diff --git a/js/src/testcase.js b/js/src/testcase.js
new file mode 100644
index 00000000000..41fa48273cb
--- /dev/null
+++ b/js/src/testcase.js
@@ -0,0 +1,4 @@
+a = "".startsWith("")
+function b() { return newGlobal(b) }
+b.newCompartment = a
+with (b()) this.moduleEvaluate(parseModule("45172", "", "json"))
diff --git a/js/src/vm/Modules.cpp b/js/src/vm/Modules.cpp
index 1a017f0308b..67506114e47 100644
--- a/js/src/vm/Modules.cpp
+++ b/js/src/vm/Modules.cpp
@@ -46,9 +46,9 @@ using mozilla::Utf8Unit;
 
 static bool ModuleLink(JSContext* cx, Handle<ModuleObject*> module);
 static bool ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> module,
-                           MutableHandle<Value> result);
+                           MutableHandle<Value> rval);
 static bool SyntheticModuleEvaluate(JSContext* cx, Handle<ModuleObject*> module,
-                                    MutableHandle<Value> result);
+                                    MutableHandle<Value> rval);
 
 ////////////////////////////////////////////////////////////////////////////////
 // Public API
@@ -1493,8 +1493,8 @@ static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module,
 
 static bool SyntheticModuleEvaluate(JSContext* cx,
                                     Handle<ModuleObject*> moduleArg,
-                                    MutableHandle<Value> result) {
-  // Steps 1-12 happens elsewhere in the engine.
+                                    MutableHandle<Value> rval) {
+  // Steps 1-12 happen elsewhere in the engine.
 
   // Step 13. Let pc be ! NewPromiseCapability(%Promise%).
   Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectForAsync(cx));
@@ -1502,15 +1502,18 @@ static bool SyntheticModuleEvaluate(JSContext* cx,
     return false;
   }
 
+  // Since the only synthetic modules we support are JSON modules, result is
+  // always |undefined|.
+
   // Step 14. IfAbruptRejectPromise(result, pc) (Skipped)
 
   // 15. Perform ! pc.[[Resolve]](result).
-  if (!AsyncFunctionReturned(cx, resultPromise, result)) {
+  if (!AsyncFunctionReturned(cx, resultPromise, JS::UndefinedHandleValue)) {
     return false;
   }
 
   // 16. Return pc.[[Promise]].
-  result.set(ObjectValue(*resultPromise));
+  rval.set(ObjectValue(*resultPromise));
   return true;
 }
 
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/js/src/jit-test/tests/modules/evaluation-result.js b/js/src/jit-test/tests/modules/evaluation-result.js
new file mode 100644
index 00000000000..6020403a0f2
--- /dev/null
+++ b/js/src/jit-test/tests/modules/evaluation-result.js
@@ -0,0 +1,11 @@
+async function parseAndEvaluate(source, filename, kind) {
+    let m = parseModule(source, filename, kind);
+    moduleLink(m);
+    return await moduleEvaluate(m);
+}
+
+(async () => {
+  assertEq(await parseAndEvaluate("[]", "", "js"), undefined);
+  assertEq(await parseAndEvaluate("[]", "", "json"), undefined);
+})();
+drainJobQueue();
Loading diff…