High CVSS 9.8 firefox UAF 🔧 Commit mapped

Overview

High
Severity
9.8
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionAn attacker could have caused a use-after-free via crafted XSLT data, leading to a potentially exploitable crash.
ComponentDOM
Bug ClassUAF
Tracker1936613
Fix commitb6875c1d0c25 (firefox) +4/-0
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedIvan Fratric of Google Project Zero
Disclosed2025-02-04

Background

XSLT
an XML-based transformation language that Firefox executes through the txMozillaXSLTProcessor DOM binding to convert source documents into new output trees.
`txMozillaXSLTProcessor`
the C++ object backing the scriptable XSLTProcessor DOM interface, holding per-instance state such as mStylesheet, mEmbeddedStylesheetRoot, and mCompileResult across multiple API calls.
`mCompileResult`
a cached nsresult member recording the outcome of the most recent stylesheet compilation, later consulted to decide whether a transform may proceed against valid compiled data.
Use-after-free (UAF)
a memory-safety defect in which a pointer is dereferenced after the object it referenced has been freed, yielding a potentially exploitable crash.

Root Cause Analysis

The scriptable txMozillaXSLTProcessor::ImportStylesheet entry point could be invoked repeatedly on the same processor instance, but it never reset the state left behind by a previous compilation attempt. If an earlier operation had populated mEmbeddedStylesheetRoot or left mCompileResult holding a stale value, a subsequent ImportStylesheet call would begin compiling a new stylesheet while inconsistent bookkeeping still pointed at nodes tied to the prior document, whose lifetime the processor no longer controlled. This violated the invariant that a fresh import starts from a clean compile state, so a later transform could dereference freed or reused DOM nodes reachable through the stale root, producing a use-after-free.

The fix asserts mEmbeddedStylesheetRoot is empty at entry to catch the invalid state in debug builds and unconditionally sets mCompileResult = NS_OK, ensuring the new compilation’s success/failure tracking cannot be poisoned by residue from a previous call. With the compile result forced to a defined baseline before the new stylesheet is processed, the transform path no longer proceeds on the basis of stale, dangling state.

Key insight
The core mistake was that txMozillaXSLTProcessor::ImportStylesheet failed to reset its per-compilation state (mCompileResult) and did not enforce that no embedded stylesheet root was already retained, allowing a stale result to authorize a transform over freed nodes; the fix re-initializes mCompileResult to NS_OK (and asserts !mEmbeddedStylesheetRoot) at the start of every import.

Attack Path

  1. Obtain a processor Script creates an XSLTProcessor instance, exposing the underlying txMozillaXSLTProcessor with its persistent mCompileResult and mEmbeddedStylesheetRoot members.
  2. Seed stale state The attacker drives an initial ImportStylesheet (or related compilation) whose nodes belong to a document that is subsequently torn down, leaving mCompileResult/mEmbeddedStylesheetRoot referring to now-freed data.
  3. Re-import a crafted stylesheet A second ImportStylesheet call is made without the processor clearing the previous compile state, so bookkeeping still reflects the freed prior root.
  4. Trigger the transform Script calls TransformToDocument/TransformToFragment, which consults the poisoned mCompileResult and follows pointers into freed memory.
  5. Achieve UAF The dangling dereference reads or writes reclaimed memory, causing a potentially exploitable crash that an attacker may groom for control.

Impact Assessment

An attacker who can run script in a content process gains a use-after-free during XSLT processing, giving them a potentially exploitable crash and a primitive that, with heap grooming, could be escalated toward memory disclosure or code execution within that content process. Exploitation requires only that the page perform a sequence of XSLTProcessor operations with attacker-controlled XSLT data, so no special privileges or user interaction beyond visiting a malicious page are needed. The defect is rated high impact because it is reachable directly from untrusted web content through standard DOM APIs.

Files Changed

  • dom/xslt/xslt/txMozillaXSLTProcessor.cpp

Audit Directions

  • Persistent per-instance state across reentrant APIs
    Review other multi-call DOM objects (processors, parsers, serializers) whose entry points assume single use but cache result codes or node pointers between invocations without resetting them.
  • Cached `nsresult` gating memory access
    Audit paths where a stored status like mCompileResult decides whether later code dereferences retained nodes, ensuring the status and the referenced objects are cleared together on re-entry or teardown.
  • Retained DOM roots outliving their document
    Look for members such as mEmbeddedStylesheetRoot that hold references to nodes from a source document, and verify they are dropped when that document is destroyed rather than reused in a subsequent operation.
diff --git a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
index 5fe01a2a9f1..fc6b3a8e2d2 100644
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -501,6 +501,10 @@ void txMozillaXSLTProcessor::ImportStylesheet(nsINode& aStyle,
     return;
   }
 
+  MOZ_ASSERT(!mEmbeddedStylesheetRoot);
+
+  mCompileResult = NS_OK;
+
   if (!nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller()->Subsumes(
           aStyle.NodePrincipal())) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
Loading diff…