CVE-2025-1009
Overview
Background
- XSLT
- an XML-based transformation language that Firefox executes through the
txMozillaXSLTProcessorDOM binding to convert source documents into new output trees. - `txMozillaXSLTProcessor`
- the C++ object backing the scriptable
XSLTProcessorDOM interface, holding per-instance state such asmStylesheet,mEmbeddedStylesheetRoot, andmCompileResultacross multiple API calls. - `mCompileResult`
- a cached
nsresultmember 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.
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
- Obtain a processor
Script creates an
XSLTProcessorinstance, exposing the underlyingtxMozillaXSLTProcessorwith its persistentmCompileResultandmEmbeddedStylesheetRootmembers. - Seed stale state
The attacker drives an initial
ImportStylesheet(or related compilation) whose nodes belong to a document that is subsequently torn down, leavingmCompileResult/mEmbeddedStylesheetRootreferring to now-freed data. - Re-import a crafted stylesheet
A second
ImportStylesheetcall is made without the processor clearing the previous compile state, so bookkeeping still reflects the freed prior root. - Trigger the transform
Script calls
TransformToDocument/TransformToFragment, which consults the poisonedmCompileResultand follows pointers into freed memory. - Achieve UAF The dangling dereference reads or writes reclaimed memory, causing a potentially exploitable crash that an attacker may groom for control.
Impact Assessment
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 APIsReview 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 accessAudit paths where a stored status like
mCompileResultdecides 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 documentLook for members such as
mEmbeddedStylesheetRootthat 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.
Patch
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);