diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc index f8c30f11ed117..35a0a0f11318f 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc @@ -1909,6 +1909,30 @@ void AXObject::SerializeOtherScreenReaderAttributes( if (GetInvalidState() != ax::mojom::blink::InvalidState::kNone) node_data->SetInvalidState(GetInvalidState()); + // === POC INJECTION: simulate compromised renderer === + // A compromised renderer is not bound by the C++ enum mapping above; it can + // place any int32 in the kInvalidState slot of AXNodeData::int_attributes + // before the tree update is sent over Mojo to the browser process. The Mojo + // StructTraits reader (ax_node_data_mojom_traits.cc) only + // validates the IntAttribute *key*, not the value, so this reaches the + // browser-process AXNodeData verbatim. + // + // On macOS, when an AT client queries @"AXInvalid" on a control-role node, + // -[AXPlatformNodeCocoa AXInvalid] switches on this value with no default + // arm and no trailing return/NOTREACHED(), causing fall-off-end-of-non-void + // UB in the unsandboxed browser process. + if (ui::IsControl(node_data->role)) { + if (node_data->HasIntAttribute( + ax::mojom::blink::IntAttribute::kInvalidState)) { + node_data->RemoveIntAttribute( + ax::mojom::blink::IntAttribute::kInvalidState); + } + // 0x41414141 is outside {0,1,2}. Any value >= 3 (or < 0) reproduces. + node_data->AddIntAttribute(ax::mojom::blink::IntAttribute::kInvalidState, + 0x41414141); + } + // === END POC INJECTION === + if (node_data->role == ax::mojom::blink::Role::kListMarker) { SerializeListMarkerAttributes(node_data); }