High firefox UAF 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionUse-after-free in the JavaScript Engine: JIT component
ComponentSpiderMonkey
Bug ClassUAF
Tracker2013583
Fix commit32cb02d93d4f (firefox) +47/-14
CISA KEVNot listed
CreditedEvyatar Ben Asher, Keane Lucas, Nicholas Carlini, Newton Cheng, Daniel Freeman, Alex Gaynor, and Joel Weinberger using Claude from Anthropic
Disclosed2026-02-24

Changed Functions

FunctionChangeNotes
switch
js/src/jit/CacheIRCompiler.cpp
modified
if
js/src/jit/CacheIRCompiler.cpp
modified

Files Changed

  • js/src/jit/BaselineIC.h
  • js/src/jit/CacheIR.cpp
  • js/src/jit/CacheIR.h
  • js/src/jit/CacheIRCloner.h
  • js/src/jit/CacheIRCompiler.cpp
  • js/src/jit/CacheIROps.yaml
  • js/src/jit/CacheIRWriter.h
  • js/src/jit/GenerateCacheIRFiles.py
  • js/src/jit/JitScript.cpp
  • js/src/jit/WarpOracle.cpp
  • js/src/jit/WarpSnapshot.cpp
diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h
index 5b469b300ac..88de0e06ac7 100644
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -299,7 +299,9 @@ class ICCacheIRStub final : public ICStub {
   void trace(JSTracer* trc);
   bool traceWeak(JSTracer* trc);
 
-  ICCacheIRStub* clone(JSRuntime* rt, ICStubSpace& newSpace);
+  enum class ICScriptHandling { MarkActive, AssertActive };
+  ICCacheIRStub* clone(JSRuntime* rt, ICStubSpace& newSpace,
+                       ICScriptHandling icScriptHandling);
 
   // Returns true if this stub can call JS or VM code that can trigger a GC.
   bool makesGCCalls() const;
diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp
index 3ae32cdc03c..1946de33476 100644
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -242,6 +242,9 @@ uint32_t CacheIRCloner::getRawInt32Field(uint32_t stubOffset) {
 const void* CacheIRCloner::getRawPointerField(uint32_t stubOffset) {
   return reinterpret_cast<const void*>(readStubWord(stubOffset));
 }
+const ICScript* CacheIRCloner::getICScriptField(uint32_t stubOffset) {
+  return reinterpret_cast<const ICScript*>(readStubWord(stubOffset));
+}
 uint64_t CacheIRCloner::getRawInt64Field(uint32_t stubOffset) {
   return static_cast<uint64_t>(readStubInt64(stubOffset));
 }
diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h
index 544e3cb7605..893cc2c1801 100644
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -241,6 +241,7 @@ class StubField {
     // These fields take up a single word.
     RawInt32,
     RawPointer,
+    ICScript,
     Shape,
     WeakShape,
     JSObject,
@@ -314,6 +315,8 @@ inline const char* StubFieldTypeName(StubField::Type ty) {
       return "RawInt32";
     case StubField::Type::RawPointer:
       return "RawPointer";
+    case StubField::Type::ICScript:
+      return "ICScript";
     case StubField::Type::Shape:
       return "Shape";
     case StubField::Type::WeakShape:
diff --git a/js/src/jit/CacheIRCloner.h b/js/src/jit/CacheIRCloner.h
index 2ecf37cc2f4..6ccb58619fd 100644
--- a/js/src/jit/CacheIRCloner.h
+++ b/js/src/jit/CacheIRCloner.h
@@ -70,6 +70,7 @@ class MOZ_RAII CacheIRCloner {
   JitCode* getJitCodeField(uint32_t stubOffset);
   uint32_t getRawInt32Field(uint32_t stubOffset);
   const void* getRawPointerField(uint32_t stubOffset);
+  const ICScript* getICScriptField(uint32_t stubOffset);
   jsid getIdField(uint32_t stubOffset);
   Value getValueField(uint32_t stubOffset);
   Value getWeakValueField(uint32_t stubOffset);
diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp
index 3a3825c4d9e..0a4edd05f62 100644
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1127,6 +1127,7 @@ static void InitWordStubField(StubField::Type type, void* dest,
   switch (type) {
     case StubField::Type::RawInt32:
     case StubField::Type::RawPointer:
+    case StubField::Type::ICScript:
     case StubField::Type::AllocSite:
       *static_cast<uintptr_t*>(dest) = value;
       break;
@@ -1188,6 +1189,7 @@ static void InitInt64StubField(StubField::Type type, void* dest,
       break;
     case StubField::Type::RawInt32:
     case StubField::Type::RawPointer:
+    case StubField::Type::ICScript:
     case StubField::Type::AllocSite:
     case StubField::Type::Shape:
     case StubField::Type::WeakShape:
@@ -1217,7 +1219,8 @@ void CacheIRWriter::copyStubData(uint8_t* dest) const {
   }
 }
 
-ICCacheIRStub* ICCacheIRStub::clone(JSRuntime* rt, ICStubSpace& newSpace) {
+ICCacheIRStub* ICCacheIRStub::clone(JSRuntime* rt, ICStubSpace& newSpace,
+                                    ICScriptHandling icScriptHandling) {
   const CacheIRStubInfo* info = stubInfo();
   MOZ_ASSERT(info->makesGCCalls());
 
@@ -1250,6 +1253,15 @@ ICCacheIRStub* ICCacheIRStub::clone(JSRuntime* rt, ICStubSpace& newSpace) {
       InitWordStubField(type, dest, *srcField);
       src += sizeof(uintptr_t);
       dest += sizeof(uintptr_t);
+      if (type == StubField::Type::ICScript) {
+        auto* icScript = reinterpret_cast<ICScript*>(*srcField);
+        if (icScriptHandling == ICScriptHandling::MarkActive) {
+          icScript->setActive();
+        } else {
+          MOZ_ASSERT(icScriptHandling == ICScriptHandling::AssertActive);
+          MOZ_RELEASE_ASSERT(icScript->active());
+        }
+      }
     } else {
       const uint64_t* srcField = reinterpret_cast<const uint64_t*>(src);
       InitInt64StubField(type, dest, *srcField);
@@ -1286,6 +1298,7 @@ void jit::TraceCacheIRStub(JSTracer* trc, T* stub,
     switch (fieldType) {
       case Type::RawInt32:
       case Type::RawPointer:
+      case Type::ICScript:
       case Type::RawInt64:
       case Type::Double:
         break;
@@ -1431,6 +1444,7 @@ bool jit::TraceWeakCacheIRStub(JSTracer* trc, T* stub,
         return !isDead;
       case Type::RawInt32:
       case Type::RawPointer:
+      case Type::ICScript:
       case Type::Shape:
       case Type::JSObject:
       case Type::Symbol:
diff --git a/js/src/jit/CacheIROps.yaml b/js/src/jit/CacheIROps.yaml
index 2c9ace66157..310b08360a7 100644
--- a/js/src/jit/CacheIROps.yaml
+++ b/js/src/jit/CacheIROps.yaml
@@ -2064,7 +2064,7 @@
     receiver: ObjId
     callee: ObjId
     rhs: ValId
-    icScript: RawPointerField
+    icScript: ICScriptField
     sameRealm: BoolImm
     nargsAndFlags: RawInt32Field
 
@@ -2257,7 +2257,7 @@
   args:
     callee: ObjId
     argc: Int32Id
-    icScript: RawPointerField
+    icScript: ICScriptField
     flags: CallFlagsImm
     argcFixed: UInt32Imm
 
@@ -2704,7 +2704,7 @@
   args:
     receiver: ValId
     callee: ObjId
-    icScript: RawPointerField
+    icScript: ICScriptField
     sameRealm: BoolImm
     nargsAndFlags: RawInt32Field
 
diff --git a/js/src/jit/CacheIRWriter.h b/js/src/jit/CacheIRWriter.h
index d0b9cc4c064..9d52f04dab8 100644
--- a/js/src/jit/CacheIRWriter.h
+++ b/js/src/jit/CacheIRWriter.h
@@ -237,6 +237,9 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter {
   void writeRawPointerField(const void* ptr) {
     addStubField(uintptr_t(ptr), StubField::Type::RawPointer);
   }
+  void writeICScriptField(const ICScript* icScript) {
+    addStubField(uintptr_t(icScript), StubField::Type::ICScript);
+  }
   void writeIdField(jsid id) {
     addStubField(id.asRawBits(), StubField::Type::Id);
   }
diff --git a/js/src/jit/GenerateCacheIRFiles.py b/js/src/jit/GenerateCacheIRFiles.py
index 40c3b504530..6ed6a20aae5 100644
--- a/js/src/jit/GenerateCacheIRFiles.py
+++ b/js/src/jit/GenerateCacheIRFiles.py
@@ -75,6 +75,7 @@ arg_writer_info = {
     "JitCodeField": ("JitCode*", "writeJitCodeField"),
     "RawInt32Field": ("uint32_t", "writeRawInt32Field"),
     "RawPointerField": ("const void*", "writeRawPointerField"),
+    "ICScriptField": ("const ICScript*", "writeICScriptField"),
     "IdField": ("jsid", "writeIdField"),
     "ValueField": ("const Value&", "writeValueField"),
     "WeakValueField": ("const Value&", "writeWeakValueField"),
@@ -181,6 +182,7 @@ arg_reader_info = {
     "JitCodeField": ("uint32_t", "Offset", "reader.stubOffset()"),
     "RawInt32Field": ("uint32_t", "Offset", "reader.stubOffset()"),
     "RawPointerField": ("uint32_t", "Offset", "reader.stubOffset()"),
+    "ICScriptField": ("uint32_t", "Offset", "reader.stubOffset()"),
     "IdField": ("uint32_t", "Offset", "reader.stubOffset()"),
     "ValueField": ("uint32_t", "Offset", "reader.stubOffset()"),
     "WeakValueField": ("uint32_t", "Offset", "reader.stubOffset()"),
@@ -326,6 +328,7 @@ arg_spewer_method = {
     "JitCodeField": "spewField",
     "RawInt32Field": "spewField",
     "RawPointerField": "spewField",
+    "ICScriptField": "spewField",
     "IdField": "spewField",
     "ValueField": "spewField",
     "WeakValueField": "spewField",
@@ -469,6 +472,7 @@ arg_length = {
     "JitCodeField": 1,
     "RawInt32Field": 1,
Loading diff…