CVE-2026-16352
Overview
Background
- `DocAccessibleParent`
- the parent-process endpoint of the accessibility IPC actor that mirrors a content document’s
RemoteAccessibletree. - `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.
Attack Path
- Begin a split show
A compromised content process sends the first part of a multi-part show event, causing the parent to set
mPendingShowChildfor a not-yet-attached node. - Interleave a conflicting message
Before the show completes, the content process sends a move (
CreateAccreparent) targeting that pending child, or a hide event for part of the tree. - Violate the tree invariant
The parent processes the move/hide against the half-constructed subtree, freeing or reparenting a
RemoteAccessiblestill referenced by the pending show. - Dangling reference use Subsequent completion of the split show, or normal tree traversal, dereferences the freed proxy.
- Exploit the UAF The attacker grooms the freed allocation to gain control over parent-process memory.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifaccessible/ipc/DocAccessibleParent.cpp |
modified |
Files Changed
accessible/ipc/DocAccessibleParent.cpp
Audit Directions
- In-flight state guardsAudit every IPC handler that mutates the
RemoteAccessibletree to confirm it respectsmPendingShowChildand other transient in-progress markers. - Split/multi-part message racesReview any operation delivered across multiple IPC messages for interleaving with move, hide, or destroy requests that assume atomic, complete state.
- Untrusted ID reuseCheck paths that accept a content-supplied accessible ID (like
aAccData.ID()) to ensure the referenced node is not simultaneously pending, freed, or reparented.
Patch
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) {