CVE-2026-12009
Overview
Background
- `NOTREACHED()`
- A Chromium control-flow assertion that marks a code path the developer believes can never execute, aborting (in most builds) and hinting to the compiler that no code follows.
- `AXPlatformNodeCocoa`
- The macOS Objective-C class that exposes Chromium’s accessibility tree nodes to the platform via the Cocoa
NSAccessibilityAPI, inui/accessibility/platform/ax_platform_node_cocoa.mm. - `ax::mojom` enums
- Accessibility attribute enumerations (e.g.
ax::mojom::InvalidState,ax::mojom::HasPopup) carried over mojom IPC that describe node state and can, in principle, hold an integer outside the declared enumerators. - Exhaustive `switch`
- A
switchcovering every declared enumerator with nodefault:case, which falls off the end (returning an indeterminate value from a non-void method) if the scrutinee is out of range.
Root Cause Analysis
The two accessibility accessor methods edited in ax_platform_node_cocoa.mm each contain a switch over an ax::mojom enum with one return per enumerator and no trailing return or default: after the closing brace, as shown by the hunks ending on return @"true";/} and return @"dialog";/}. If the enum value supplied to such a method is an out-of-range integer, control reaches the end of the non-void Objective-C method without executing any return, yielding an indeterminate object-pointer result — the exact “out-of-range integers” condition the commit message calls out. The invariant being restored is that the switch is truly total: every reachable value produces a defined return.
The fix adds NOTREACHED() immediately after each switch (the two added lines), so an unexpected value terminates deterministically instead of falling through, and the compiler is told the post-switch path is dead, which also silences the Fortify out-of-range reports the commit message references. Because both enumerators shown (ax::mojom::InvalidState::kTrue, ax::mojom::HasPopup::kDialog) are handled, the change alters behavior only on the previously-undefined out-of-range path.
ax::mojom enum as guaranteed in-range and relying on an exhaustive switch with no fallthrough handling, so an out-of-range value produced an indeterminate return; adding NOTREACHED() converts that silent, undefined fall-through into a deterministic, compiler-recognized dead path.Attack Path
- Supply an out-of-range enum value
Deliver an accessibility node whose
InvalidStateorHasPopupattribute carries an integer outside the declared enumerators, reaching the affected methods over the accessibility/mojom surface the advisory classes as “insufficient validation of untrusted input” (inferred that the value can be attacker-influenced; the diff shows only the consuming switch). - Fall off the exhaustive switch
Because neither switch has a
default:or trailingreturn, the out-of-range value matches nocaseand control reaches the end of the non-void method with noreturnexecuted. - Return an indeterminate object pointer
The method returns whatever indeterminate value occupies the return register/stack slot, which downstream Cocoa accessibility code may consume as an
NSString*/NSNumber*(inferred; the diff does not show the consumer or any dereference). - Potential further escalation
Any memory-safety consequence beyond the indeterminate return (e.g. type confusion or invalid pointer use in the a11y consumer) is not demonstrated by the diff and is
(inferred); the patch itself only hardens the fall-through, and the change is described as preventing Fortify reports.
Impact Assessment
InvalidState/HasPopup enum value could drive one of these methods off the end of an exhaustive switch, causing it to return an indeterminate object pointer in the browser/renderer accessibility path (the affected code is macOS AXPlatformNodeCocoa). The concrete precondition is delivering an out-of-range enum over the accessibility surface; any exploitation beyond undefined-value return — memory corruption, information disclosure, or code execution — is (inferred) and not established by the diff. The vendor advisory rates this critical severity and does not report in-the-wild exploitation; NVD scores it CVSS 8.3. The commit frames the change as hardening that also prevents Fortify out-of-range reports.Files Changed
ui/accessibility/platform/ax_platform_node_cocoa.mm
Audit Directions
- Exhaustive switches with no fallthroughLook for
switchstatements overax::mojom(and other IPC-sourced) enums in non-void functions that lack adefault:or trailingreturn, since an out-of-range value falls off the end into undefined behavior. - Untrusted enums crossing mojomAudit accessibility and other mojom-deserialized enum values that are used directly as switch scrutinees without range validation, where a malformed integer can bypass all cases.
- Indeterminate-return object accessorsReview Objective-C/C++ accessors that return object pointers from a terminal switch, where a missing
NOTREACHED()/default:lets the method return an uninitialized or indeterminate pointer that downstream code dereferences.
Patch
From b67efd3a537b800df6fc49d48089b241b7d6c843 Mon Sep 17 00:00:00 2001 From: Carlos Barrera <[email protected]> Date: Thu, 04 Jun 2026 10:01:08 -0700 Subject: [PATCH] [a11y] Add NOTREACHED() to exhaustive switches in AXPlatformNodeCocoa This change adds NOTREACHED() to the AXInvalid and AXPopupValue methods in AXPlatformNodeCocoa. These methods use exhaustive switches over enums, and adding NOTREACHED() prevents Fortify reports regarding out-of-range integers and ensures the compiler recognizes all paths are covered. AX-Relnotes: n/a. Bug: 517332006 Change-Id: Ie2c780582f6b40b84e81f422b7ebff32c1387b16 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7896760 Reviewed-by: David Tseng <[email protected]> Commit-Queue: Carlos Marcelo Barrera Nolasco <[email protected]> Cr-Commit-Position: refs/heads/main@{#1641747} --- diff --git a/ui/accessibility/platform/ax_platform_node_cocoa.mm b/ui/accessibility/platform/ax_platform_node_cocoa.mm index 58b407e..375ff742 100644 --- a/ui/accessibility/platform/ax_platform_node_cocoa.mm +++ b/ui/accessibility/platform/ax_platform_node_cocoa.mm @@ -2219,6 +2219,7 @@ case ax::mojom::InvalidState::kTrue: return @"true"; } + NOTREACHED(); } - (NSNumber*)AXIsMultiSelectable { @@ -2281,6 +2282,7 @@ case ax::mojom::HasPopup::kDialog: return @"dialog"; } + NOTREACHED(); } - (NSNumber*)AXRequired {
Original Bug Report
Potential Browser-Process UB in -[AXPlatformNodeCocoa AXInvalid] and AXPopupValue on macOS
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A compromised renderer can supply out-of-range integers for accessibility enum attributes due to a lack of Mojo validation. When accessed via macOS accessibility selectors, this can cause control flow to fall off the end of non-void Objective-C methods. This leads to undefined behavior and potential code execution in the unsandboxed browser process.
Affected files:
ui/accessibility/platform/ax_platform_node_cocoa.mmui/accessibility/mojom/ax_node_data_mojom_traits.ccui/accessibility/ax_node_data.cc
Estimated timestamp from git blame: 2021-11-09
Technical Analysis
1. Unvalidated Mojo Deserialization of Enum Attributes
In ui/accessibility/mojom/ax_node_data_mojom_traits.cc, the int_attributes map is deserialized directly without verifying that the integers correspond to valid enum ranges:
if (!data.ReadIntAttributes(&out->int_attributes.container())) {
return false;
}
Because attributes like ax::mojom::InvalidState are defined as enums with limited valid values (e.g., kNone = 0, kFalse = 1, kTrue = 2), a compromised renderer can pass arbitrary out-of-range values (such as 3 or 0x10000000) for the ax::mojom::IntAttribute::kInvalidState key.
2. Enum Casting in the Browser Process
In ui/accessibility/ax_node_data.cc (line 591), AXNodeData::GetInvalidState() performs a static cast on the unvalidated attribute retrieved from the map:
ax::mojom::InvalidState AXNodeData::GetInvalidState() const {
return static_cast<ax::mojom::InvalidState>(
GetIntAttribute(ax::mojom::IntAttribute::kInvalidState));
}
3. Undefined Behavior in -[AXPlatformNodeCocoa AXInvalid]
In ui/accessibility/platform/ax_platform_node_cocoa.mm (line 2212), the macOS-specific selector switches on this state:
- (NSString*)AXInvalid {
if (![self instanceActive])
return nil;
switch (_node->GetData().GetInvalidState()) {
case ax::mojom::InvalidState::kNone:
case ax::mojom::InvalidState::kFalse:
return @"false";
case ax::mojom::InvalidState::kTrue:
return @"true";
}
}
Because this switch is exhaustive relative to the enum definition, Clang assumes that all execution paths are covered and does not issue a -Wreturn-type warning or compilation error. However, at runtime, an out-of-range value (like 3) causes control flow to fall off the end of this non-void method.
In compiled Objective-C++ binaries, falling off a non-void method is undefined behavior. The compiler typically omits setting the return register (rax/x0), leaving a stale or garbage pointer. Since the caller (AppKit or VoiceOver dispatch) expects a valid NSString* return value, treating this garbage value as an object pointer and passing it to ARC reference management (e.g., objc_retainAutoreleasedReturnValue) or invoking selectors on it can lead to control flow hijack and potential RCE in the unsandboxed browser process.
4. Sibling Vulnerability: -[AXPlatformNodeCocoa AXPopupValue]
The exact same vulnerable pattern is present in -[AXPlatformNodeCocoa AXPopupValue] (line 2264) in the same file:
- (NSString*)AXPopupValue {
if (![self instanceActive])
return nil;
int hasPopup = _node->GetIntAttribute(ax::mojom::IntAttribute::kHasPopup);
switch (static_cast<ax::mojom::HasPopup>(hasPopup)) {
case ax::mojom::HasPopup::kFalse:
return @"false";
case ax::mojom::HasPopup::kTrue:
return @"true";
case ax::mojom::HasPopup::kMenu:
return @"menu";
case ax::mojom::HasPopup::kListbox:
return @"listbox";
case ax::mojom::HasPopup::kTree:
return @"tree";
case ax::mojom::HasPopup::kGrid:
return @"grid";
case ax::mojom::HasPopup::kDialog:
return @"dialog";
}
}
This similarly falls off the end of the non-void method when an out-of-range integer is supplied for kHasPopup.
Potential Steps to Trigger (Hypothetical)
Note: These are potential steps based on source code analysis; our tooling has not run any code to verify execution.
- From a compromised Renderer process, access the
blink::mojom::RenderAccessibilityHostinterface. - Construct an
AXUpdatesAndEventspayload where anAXNodeData’sint_attributesmap contains the keyax::mojom::IntAttribute::kInvalidStatemapped to an out-of-range value (e.g.,3). - Invoke the
HandleAXEventsMojo method with this payload. - On macOS, trigger an accessibility query from a client (e.g., VoiceOver) that targets the modified node’s
AXInvalidattribute. - Observe a browser process crash or control flow hijack when AppKit attempts to process the returned garbage pointer.
Suggested Remediation
To safely handle unexpected enum values and prevent undefined behavior, do one of the following:
- Add a trailing return / default case: Ensure that all non-void platform methods have a fallback return value or a terminating
NOTREACHED()if an invalid value is encountered:
- (NSString*)AXInvalid {
if (![self instanceActive])
return nil;
switch (_node->GetData().GetInvalidState()) {
case ax::mojom::InvalidState::kNone:
case ax::mojom::InvalidState::kFalse:
return @"false";
case ax::mojom::InvalidState::kTrue:
return @"true";
}
return nil; // Fallback to prevent falling off the end
}
Apply a similar fix to AXPopupValue and other sibling methods in ax_platform_node_cocoa.mm.
- Validate during Mojo Deserialization: Update
ui/accessibility/mojom/ax_node_data_mojom_traits.ccto sanitize enum-typed integer values upon receipt, rejecting payloads that contain invalid out-of-range values.
Evaluated with Chrome root at commit: b1520ef4a76878853a31f0943b565e42060edec8
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.