Moderate firefox Race 🔧 Commit mapped

Overview

Moderate
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionRace condition in the Disability Access APIs component
ComponentCore
Bug ClassRace
Tracker2076227
Fix commitb9d206bc5f77 (firefox) +4/-0
CISA KEVNot listed
CreditedNguyen Thanh Nguyen
Disclosed2026-10-27

Background

`DocAccessibleParent`
The parent-process endpoint (accessible/ipc/DocAccessibleParent.cpp) of the accessibility IPC protocol; it receives accessibility events and mutation events from content processes and maintains the parent-side RemoteAccessible tree.
Android accessibility monitor / lock
On Android, accessibility runs across threads (the Java/UI accessibility thread and the IPC thread), so access to the remote accessibility tree is guarded by a monitor obtained via nsAccessibilityService::GetAndroidMonitor(). ACQUIRE_ANDROID_LOCK is the macro that takes that monitor for the scope.
`AssertCurrentThreadOwns()`
A debug assertion that the calling thread already holds the given monitor, used to document and enforce the locking contract at a point that assumes the lock is held.
`FireEvent` / `RecvRequestAckMutationEvents`
FireEvent(RemoteAccessible*, uint32_t) dispatches accessibility events and walks aAcc’s children (ChildCount()); RecvRequestAckMutationEvents() is an IPC message handler that touches shutdown and tree-initialization state (mShutdown, mIsInitialTreeDone).
Race condition
Advisory class: concurrent access to the remote accessibility tree from the IPC thread and the Android accessibility thread without consistent locking.

Root Cause Analysis

The patch closes a locking gap on Android in two places. In DocAccessibleParent::FireEvent, it adds (under #if defined(ANDROID)) nsAccessibilityService::GetAndroidMonitor().AssertCurrentThreadOwns(); at function entry, asserting that callers hold the Android monitor before FireEvent walks the accessibility tree (aAcc->ChildCount() for reorder events). More substantively, in the IPC handler DocAccessibleParent::RecvRequestAckMutationEvents, it adds ACQUIRE_ANDROID_LOCK at the top, so the handler now takes the Android monitor before touching mShutdown, mIsInitialTreeDone, and the mutation/tree state. Pre-fix, RecvRequestAckMutationEvents ran without acquiring that lock, so its reads/writes of the remote accessibility tree and the shutdown/initial-tree flags could race with the Android accessibility thread (which does hold the monitor) and with other mutation handling. The violated invariant is that all access to DocAccessibleParent’s remote tree and lifecycle flags on Android must occur under the Android monitor; the handler violated it.

The fix makes the acquisition explicit in the handler and adds the AssertCurrentThreadOwns assertion in FireEvent to enforce the contract on the event-dispatch path. The advisory’s “race condition in Disability Access APIs” matches the diff: this is a missing-lock data race, not a memory-corruption bug visible in the diff (though a race on a tree being torn down can degenerate into a UAF — inferred, not shown).

Key insight
The parent-process accessibility IPC handler RecvRequestAckMutationEvents touched the remote accessibility tree and shutdown/initial-tree flags without holding the Android monitor that guards them, racing the Android accessibility thread; the fix acquires the lock in the handler and asserts ownership on the FireEvent dispatch path.

Attack Path

  1. Android target The bug is gated on ANDROID builds where accessibility spans the IPC thread and the Android accessibility thread.
  2. Drive accessibility IPC A content process (or content that influences accessibility state) causes mutation events and ACK requests, invoking RecvRequestAckMutationEvents on the parent IPC thread without the monitor (inferred reachability from a web page that mutates accessible content).
  3. Concurrent Android-thread access Simultaneously the Android accessibility thread accesses the same DocAccessibleParent tree/flags under the monitor.
  4. Race the unlocked handler The unlocked handler reads/writes mShutdown/mIsInitialTreeDone and tree state concurrently with the locked thread, producing inconsistent state or a torn read.
  5. Observe/trigger corruption Racing a shutdown or tree-rebuild can leave the tree in an inconsistent or partially-freed state.
  6. Primitive achieved A data race on the remote accessibility tree/lifecycle flags; potential escalation to use-after-free of accessibility objects is inferred and not shown by the diff.

Impact Assessment

The affected process is the parent/browser process, in the accessibility IPC layer, on Android only. Triggering requires accessibility to be active and the attacker to drive mutation/ACK IPC concurrently with the Android accessibility thread; exact web-reachability of the ACK handler is inferred. The demonstrated defect is a missing-lock data race; the diff does not show a concrete memory-safety sink, so any escalation to use-after-free/corruption is inferred. Mozilla rates it moderate. Honestly, the diff substantiates a race (added lock + ownership assertion), not a proven exploitable corruption primitive.

Changed Functions

FunctionChangeNotes
if
accessible/ipc/DocAccessibleParent.cpp
modified

Files Changed

  • accessible/ipc/DocAccessibleParent.cpp

Audit Directions

  • Unlocked IPC handlers on Android
    Audit every Recv* handler in DocAccessibleParent (and related accessibility parent actors) for ones that touch the remote tree or lifecycle flags without ACQUIRE_ANDROID_LOCK.
  • Lock-ownership contracts
    Look for other tree-walking helpers (like FireEvent) that assume the monitor is held but lacked an AssertCurrentThreadOwns guard, to find callers that enter them unlocked.
  • Race-to-UAF escalation
    Review whether any of these racy paths can run concurrently with DocAccessibleParent shutdown/teardown, where the data race could become a use-after-free of RemoteAccessible objects.
diff --git a/accessible/ipc/DocAccessibleParent.cpp b/accessible/ipc/DocAccessibleParent.cpp
index 63b54079bab..2ae68a9dfe1 100644
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -540,6 +540,9 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvEvent(
 
 void DocAccessibleParent::FireEvent(RemoteAccessible* aAcc,
                                     const uint32_t& aEventType) {
+#if defined(ANDROID)
+  nsAccessibilityService::GetAndroidMonitor().AssertCurrentThreadOwns();
+#endif
   if (aEventType == nsIAccessibleEvent::EVENT_REORDER ||
       aEventType == nsIAccessibleEvent::EVENT_INNER_REORDER) {
     uint32_t count = aAcc->ChildCount();
@@ -750,6 +753,7 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvMutationEvents(
 }
 
 mozilla::ipc::IPCResult DocAccessibleParent::RecvRequestAckMutationEvents() {
+  ACQUIRE_ANDROID_LOCK
   if (!mShutdown) {
     if (!mIsInitialTreeDone) {
       // This is the first request for an ACK, which means we now have the
Loading diff…