High chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized Use in Accessibility
DescriptionUninitialized Use in Accessibility
ComponentAccessibility
Bug ClassUninitialized Memory
Tracker498201025
Fix commitdb2a3f8ac9a6 (chromium/src) +2/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-04-15

Files Changed

  • ui/accessibility/platform/ax_platform_node_win.cc
From db2a3f8ac9a6c1fa8e8a26c5d34072814f14c6a0 Mon Sep 17 00:00:00 2001
From: Benjamin Beaudry <benjamin.beaudry@microsoft.com>
Date: Fri, 03 Apr 2026 08:57:27 -0700
Subject: [PATCH] [M147][a11y] Fix uninitialized memory in get_columnHeaderCells/get_rowHeaderCells

Both get_columnHeaderCells and get_rowHeaderCells allocate a COM array
via CoTaskMemAlloc, but report the requested array size
(column_header_ids.size()) to the caller instead of the number of
successfully populated elements (index). If a node lookup fails
mid-iteration, the trailing array slots contain uninitialized memory
that the COM marshaller will treat as valid IUnknown* pointers.

This CL fixes both methods to return the actual populated count
(index), matching the pattern already used by get_targets.

(cherry picked from commit c81f01b469c4eae60e312ebe8b1b691d454609e9)

Fixed: 498201025
Change-Id: I596745388199d61eef8261fe0ae6e1d3e773f240
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7717598
Reviewed-by: Kevin Babbitt <kbabbitt@microsoft.com>
Commit-Queue: Kevin Babbitt <kbabbitt@microsoft.com>
Auto-Submit: Benjamin Beaudry <benjamin.beaudry@microsoft.com>
Commit-Queue: Benjamin Beaudry <benjamin.beaudry@microsoft.com>
Cr-Original-Commit-Position: refs/heads/main@{#1607938}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7729125
Cr-Commit-Position: refs/branch-heads/7727@{#2183}
Cr-Branched-From: ce01102937348db7b88c8a4257ee4b3ac702eb1a-refs/heads/main@{#1596535}
---

diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index e8c6a971..3685384d 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -4274,7 +4274,7 @@
     }
   }
 
-  *n_column_header_cells = static_cast<LONG>(column_header_ids.size());
+  *n_column_header_cells = static_cast<LONG>(index);
   return S_OK;
 }
 
@@ -4330,7 +4330,7 @@
     }
   }
 
-  *n_row_header_cells = static_cast<LONG>(row_header_ids.size());
+  *n_row_header_cells = static_cast<LONG>(index);
   return S_OK;
 }
 
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential uninitialized memory use in AXPlatformNodeWin header cell getters

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: The AXPlatformNodeWin::get_columnHeaderCells and get_rowHeaderCells methods allocate an array via CoTaskMemAlloc but may leave elements uninitialized if node lookups fail. The methods incorrectly return the requested array size rather than the successfully populated count, causing the COM marshaller to process uninitialized memory. A compromised renderer could potentially exploit this to escape the sandbox by grooming the OLE heap.

Affected files:

  • ui/accessibility/platform/ax_platform_node_win.cc

Estimated timestamp from git blame: 2022-02-08

Summary

A potential uninitialized memory vulnerability exists in ui/accessibility/platform/ax_platform_node_win.cc within the AXPlatformNodeWin::get_columnHeaderCells and AXPlatformNodeWin::get_rowHeaderCells methods. If an accessibility node lookup fails during array population, the uninitialized memory allocated by ::CoTaskMemAlloc is returned to the COM caller because the methods return the requested array size instead of the number of successfully populated elements.

Technical Details

Both vulnerable methods follow the exact same pattern. For instance, in get_columnHeaderCells:

  1. The method retrieves a list of node IDs (column_header_ids).
  2. It allocates memory using ::CoTaskMemAlloc(column_header_ids.size() * sizeof(IUnknown*)). Crucially, CoTaskMemAlloc does not zero-initialize the memory.
  3. It iterates over the IDs, looks up the corresponding wrapper, and if successful, calls QueryInterface and increments an index counter.
  4. If GetDelegate()->GetFromNodeID(node_id) returns nullptr (e.g., due to a missing wrapper from reparenting edge cases), the loop skips the element and does not increment index.
  5. The method concludes by setting *n_column_header_cells = static_cast<LONG>(column_header_ids.size()); instead of using the index variable.

If any node lookup fails, the resulting array will contain uninitialized bytes at the end, but the output count (*n_column_header_cells) tells the COM marshaller that the entire array is populated with valid IUnknown* pointers.

Potential Exploitation Steps

Note: These are suggested steps based on static analysis. Our tooling agent does not currently have the capability to run code to produce a working Proof of Concept.

An attacker with a compromised renderer process could potentially achieve a sandbox escape (RCE in the browser process) through the following sequence:

  1. Heap Grooming: The attacker sends AXTreeUpdate IPC messages containing many nodes with large string attributes (like kName or kDescription). These are allocated as BSTR objects in the browser process, which use the same Windows OLE/COM heap as CoTaskMemAlloc. This allows the attacker to fill the heap with fake COM object pointers and fake vtables.
  2. State Corruption: The attacker sends a crafted AXTreeUpdate designed to trigger a reparenting edge case (e.g., exploiting crbug.com/40833630). This creates a state where an AXNode exists logically in the tree but lacks a corresponding platform wrapper in the id_wrapper_map_.
  3. Table Construction: The attacker constructs a table where the column headers reference the ID of the corrupted node alongside valid node IDs.
  4. Trigger: An external out-of-process accessibility client (like a screen reader) is induced to query get_columnHeaderCells on the target table.
  5. Vtable Dispatch: The browser returns the array containing uninitialized, attacker-controlled OLE heap memory. The Windows NDR (Network Data Representation) marshaller iterates over the array based on the inflated count and calls QueryInterface and Release on the garbage pointers. This indirect call dereferences the attacker’s fake vtable, hijacking control flow.

Note that MiraclePtr does not protect against this issue, as the uninitialized bytes are processed directly by the OS-level COM marshaller, bypassing PartitionAlloc.

Suggested Fix

Update both get_columnHeaderCells and get_rowHeaderCells to set the output count to the number of successfully processed nodes (index) rather than the requested size.

// ui/accessibility/platform/ax_platform_node_win.cc

// In AXPlatformNodeWin::get_columnHeaderCells (approx line 4288)
-  *n_column_header_cells = static_cast<LONG>(column_header_ids.size());
+  *n_column_header_cells = static_cast<LONG>(index);

// In AXPlatformNodeWin::get_rowHeaderCells (approx line 4344)
-  *n_row_header_cells = static_cast<LONG>(row_header_ids.size());
+  *n_row_header_cells = static_cast<LONG>(index);

This correctly handles cases where nodes fail to resolve, identical to the pattern already safely used in AXPlatformNodeWin::get_targets.

Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33


Results from so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; 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.

View on issue tracker
Links in the report