High firefox UAF 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionUse-after-free in the JavaScript: WebAssembly component
ComponentSpiderMonkey
Bug ClassUAF
Tracker2052688
Fix commit9823b5c86ffa (firefox) +18/-0
CISA KEVNot listed
CreditedAmy Burnett of OpenAI
Disclosed2026-08-18

Files Changed

  • js/src/vm/JitActivation.cpp
  • js/src/vm/JitActivation.h
diff --git a/js/src/vm/JitActivation.cpp b/js/src/vm/JitActivation.cpp
index 5fff795afc9..4c98a2ec424 100644
--- a/js/src/vm/JitActivation.cpp
+++ b/js/src/vm/JitActivation.cpp
@@ -244,6 +244,14 @@ void js::jit::JitActivation::startWasmTrap(wasm::Trap trap,
   const wasm::Code& code = wasm::GetNearestEffectiveInstance(fp)->code();
   MOZ_RELEASE_ASSERT(&code == wasm::LookupCode(pc));
 
+  // Keep the trapping code alive across a GC. For an IndirectCallBadSig trap
+  // reached through a tail call, this code can belong to a different instance
+  // than the effective one above, whose frame has already been unwound. Nothing
+  // else then keeps alive the code segment we are about to run the trap stub
+  // in.
+  wasmTrapCode_ = wasm::LookupCode(state.pc);
+  MOZ_RELEASE_ASSERT(wasmTrapCode_);
+
   setWasmExitFP(fp);
   wasmTrapData_.emplace();
   wasmTrapData_->resumePC =
@@ -271,6 +279,9 @@ void js::jit::JitActivation::finishWasmTrap() {
   MOZ_ASSERT(hasWasmExitFP());
   MOZ_ASSERT(isWasmTrapping());
   wasmTrapData_.reset();
+  // Keep wasmTrapCode_ alive: we are still executing inside this code and are
+  // about to return into it, so releasing it now could free it. It is released
+  // on the next trap or when the activation is destroyed.
   packedExitFP_ = nullptr;
   MOZ_ASSERT(!isWasmTrapping());
 }
diff --git a/js/src/vm/JitActivation.h b/js/src/vm/JitActivation.h
index 4f1b2d4c248..884ff99bf4b 100644
--- a/js/src/vm/JitActivation.h
+++ b/js/src/vm/JitActivation.h
@@ -8,6 +8,7 @@
 #include "mozilla/Assertions.h"  // MOZ_ASSERT
 #include "mozilla/Atomics.h"     // mozilla::Atomic, mozilla::Relaxed
 #include "mozilla/Maybe.h"       // mozilla::Maybe
+#include "mozilla/RefPtr.h"      // RefPtr
 
 #include <stddef.h>  // size_t
 #include <stdint.h>  // uint8_t, uint32_t, uintptr_t
@@ -94,6 +95,12 @@ class JitActivation : public Activation {
   // purposes. Wasm code can't trap reentrantly.
   mozilla::Maybe<wasm::TrapData> wasmTrapData_;
 
+  // Keeps the trapping code alive while the trap is handled. With tail calls
+  // the trapping frame may already be unwound, so no wasm::Frame keeps it alive
+  // and a GC (e.g. while building the RuntimeError) could otherwise free the
+  // code segment we are still executing in.
+  RefPtr<const wasm::Code> wasmTrapCode_;
+
 #ifdef CHECK_OSIPOINT_REGISTERS
  protected:
   // Used to verify that live registers don't change between a VM call and
Loading diff…