High firefox UAF 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionSandbox escape due to use-after-free in the Disability Access APIs component
ComponentCore
Bug ClassUAF
Tracker2046416
Fix commit08c0166d1889 (firefox) +9/-0
CISA KEVNot listed
CreditedOskar L
Disclosed2026-07-21

Background

`DocAccessibleParent`
the parent-process endpoint of the accessibility IPC actor that mirrors a content document’s RemoteAccessible tree.
`RemoteAccessible`
a parent-process proxy object representing an accessible node whose authoritative state lives in a content process.
`mPendingShowChild`
a member tracking the ID of a child whose show event is still mid-way through a multi-part (“split”) show sequence.
Split show
a show event whose subtree is delivered across multiple IPC messages, leaving the tree in a transient, partially-constructed state.

Root Cause Analysis

While a split show was in progress, mPendingShowChild recorded a child that had not yet been fully attached to the RemoteAccessible tree, but nothing prevented other IPC messages from mutating that same node before the show completed. In DocAccessibleParent::CreateAcc, a move (reparent) request could target the accessible identified by mPendingShowChild, relocating a proxy whose parent was still pending and leaving dangling references across the tree. Similarly, DocAccessibleParent::ProcessHideEvent could tear down accessibles while a split show was outstanding, freeing objects the pending show still expected to exist. The violated invariant is that a node participating in an in-flight split show must not be moved or hidden until that show finishes.

The fix enforces this by rejecting a CreateAcc move whose ID matches mPendingShowChild and by failing the IPC channel outright (IPC_FAIL) if a hide arrives while mPendingShowChild is set.

Key insight
The core mistake was leaving a partially-shown accessible reachable by other tree-mutating IPC messages, so a compromised content process could move or hide it and trigger a use-after-free; the fix closes the window by refusing any move or hide that touches the pending-show child.

Attack Path

  1. Begin a split show A compromised content process sends the first part of a multi-part show event, causing the parent to set mPendingShowChild for a not-yet-attached node.
  2. Interleave a conflicting message Before the show completes, the content process sends a move (CreateAcc reparent) targeting that pending child, or a hide event for part of the tree.
  3. Violate the tree invariant The parent processes the move/hide against the half-constructed subtree, freeing or reparenting a RemoteAccessible still referenced by the pending show.
  4. Dangling reference use Subsequent completion of the split show, or normal tree traversal, dereferences the freed proxy.
  5. Exploit the UAF The attacker grooms the freed allocation to gain control over parent-process memory.

Impact Assessment

An attacker who already controls a content process can escape the sandbox by triggering a use-after-free in the privileged parent process’s accessibility layer, potentially leading to memory corruption and code execution at the parent’s privilege level. Exploitation requires the accessibility (Disability Access) IPC path to be active and the ability to send crafted, interleaved show/hide/move messages. The impact is a sandbox escape from content into the more-privileged parent process.

Changed Functions

FunctionChangeNotes
if
accessible/ipc/DocAccessibleParent.cpp
modified

Files Changed

  • accessible/ipc/DocAccessibleParent.cpp

Audit Directions

  • In-flight state guards
    Audit every IPC handler that mutates the RemoteAccessible tree to confirm it respects mPendingShowChild and other transient in-progress markers.
  • Split/multi-part message races
    Review any operation delivered across multiple IPC messages for interleaving with move, hide, or destroy requests that assume atomic, complete state.
  • Untrusted ID reuse
    Check paths that accept a content-supplied accessible ID (like aAccData.ID()) to ensure the referenced node is not simultaneously pending, freed, or reparented.
diff --git a/accessible/ipc/DocAccessibleParent.cpp b/accessible/ipc/DocAccessibleParent.cpp
index bb69e862469..41fba965cca 100644
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -283,6 +283,11 @@ RemoteAccessible* DocAccessibleParent::CreateAcc(
           "Attempt to move RemoteAccessible which still has a parent!");
       return nullptr;
     }
+    if (aAccData.ID() == mPendingShowChild) {
+      MOZ_ASSERT_UNREACHABLE(
+          "Attempt to move RemoteAccessible which has a pending parent");
+      return nullptr;
+    }
     return newProxy;
   }
 
@@ -422,6 +427,10 @@ mozilla::ipc::IPCResult DocAccessibleParent::ProcessHideEvent(
 
   MOZ_ASSERT(CheckDocTree());
 
+  if (mPendingShowChild) {
+    return IPC_FAIL(this, "Hide during split show");
+  }
+
   // We shouldn't actually need this because mAccessibles shouldn't have an
   // entry for the document itself, but it doesn't hurt to be explicit.
   if (!aRootID) {
Loading diff…