Medium watch ✔︎ verified CVSS 3.1 chrome OOB 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
3.1
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds read in GPU
DescriptionOut of bounds read in GPU
ComponentGPU
Sub-componentTerracotta-Phase-1
Bug ClassOOB
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar — factors set by independent review by AI
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Independent review by AIConfirmed
Reviewer notes: Core fix (BeginAccessDawnBuffer lacked ValidateBeginAccess at 1595, diff adds it) fully supported. Only caveat: claim that other paths validate is not shown in material; downstream OOB read in Dawn/D3D12 is inferred.
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker496395450
Fix commita93eb13c129e (chromium/src) +5/-0
CWECWE-125 (Out-of-bounds read)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-03-26 (per vendor advisory)
Fix landed2026-04-01 (public git)
Disclosed2026-05-12
Patch gap41 days — fix public in source before the advisory

Background

`D3DImageBacking`
the GPU-process shared-image backing that wraps a Direct3D resource shared between WebGPU (Dawn), Direct3D 11, and WebNN.
`ValidateBeginAccess`
the backing’s guard that rejects a begin-access request when the resource is already held for exclusive (write) access.
`BeginAccessCommon`
the helper that updates the backing’s access state (e.g. in_write_access_) and clears pending fences once an access is granted.

Root Cause Analysis

In the pre-fix source, D3DImageBacking::BeginAccessDawnBuffer (gpu/command_buffer/service/shared_image/d3d_image_backing.cc:1591-1643) takes the lock at line 1595 and then proceeds directly to resolve the Dawn device, gather fences from write_fences_/read_fences_ (lines 1606-1619), create the wgpu::Buffer, and finally call BeginAccessCommon(true) at line 1641 — without ever asking whether the backing is already in exclusive use. The pre-fix BeginAccessDawnBuffer reaches BeginAccessCommon(true) without any preceding exclusive-access validation — ValidateBeginAccess does not appear anywhere in the pre-fix source shown (the WebNN path at line 1544 likewise just calls BeginAccessCommon(true)), only in the fix diff — so this path grants write access without a guard. The invariant violated is “a backing already held for exclusive write access must not hand out another concurrent writer.”

The fix adds, right after AutoLock auto_lock(this) at line 1595, if (!ValidateBeginAccess(true)) { return nullptr; }, so the Dawn buffer path now bails out instead of acquiring overlapping write access.

The patch therefore TIGHTENS the check, bringing this path in line with the other access entry points; the added argument true requests validation for write access specifically.

Key insight
The single mistake is that BeginAccessDawnBuffer was the one access entry point that updated access state via BeginAccessCommon(true) without first calling ValidateBeginAccess, letting a second writer in while the resource was already locked; the fix inserts the missing ValidateBeginAccess(true) gate.

Attack Path

  1. Create a dual-usage shared image A compromised renderer requests a shared image usable by both WebGPU buffer access and WebNN. (inferred)
  2. Take exclusive access via one path The renderer begins an exclusive (write) access through another entry point so the backing is marked in-write. (inferred)
  3. Re-enter through the ungated path Without ending the first access, the renderer drives BeginAccessDawnBuffer, which pre-fix never calls ValidateBeginAccess (d3d_image_backing.cc:1595) and so grants a concurrent, unsynchronized writer.
  4. Consume the unsynchronized resource Concurrent writes to the same D3D resource race downstream GPU work, which the advisory ties to an out-of-bounds read. (inferred)

Impact Assessment

An attacker controlling a renderer could obtain concurrent, unsynchronized write access to a single shared D3D resource in the GPU process, which the advisory describes as leading to an out-of-bounds read in GPU. Vendor severity is medium and the advisory marks this as not exploited in the wild. The out-of-bounds read itself occurs in Dawn/D3D12 code that is not included in this material, so the end-to-end leak is (inferred).

Changed Functions

FunctionChangeNotes
if
gpu/command_buffer/service/shared_image/d3d_image_backing.cc
modified

Files Changed

  • gpu/command_buffer/service/shared_image/d3d_image_backing.cc

Audit Directions

  • Parallel access entry points
    When several methods converge on a shared state-mutating helper like BeginAccessCommon, verify every one of them performs the same precondition validation; the odd one out is the bug.
  • Lock without validation
    Flag functions that acquire a backing/object lock and then mutate shared access state without a guarding validation call between the two.
  • Cross-API resource sharing
    Audit shared-image paths reachable from multiple GPU client APIs (Dawn, D3D11, WebNN) for consistent exclusive-access accounting.
From a93eb13c129e125e5ba347a136cb2daa1c614049 Mon Sep 17 00:00:00 2001
From: Kalvin Lee <[email protected]>
Date: Wed, 01 Apr 2026 07:36:33 -0700
Subject: [PATCH] Terracotta-Phase-1: Gate `BeginAccessDawnBuffer()`

This patch is speculative. Please see the linked bug for details.

Bug: 496395450
Change-Id: I883e9b1b01a97261f56f5aee00bd3e24a7f1c5e0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7707479
Auto-Submit: Kalvin Lee <[email protected]>
Commit-Queue: Vasiliy Telezhnikov <[email protected]>
Reviewed-by: Vasiliy Telezhnikov <[email protected]>
Reviewed-by: Colin Blundell <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1608520}
---

diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
index 1fd7df893..b1392f6 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
@@ -1593,6 +1593,11 @@
     wgpu::BackendType backend_type,
     wgpu::BufferUsage usage) {
   AutoLock auto_lock(this);
+
+  if (!ValidateBeginAccess(true)) {
+    return nullptr;
+  }
+
   Microsoft::WRL::ComPtr<ID3D12Device> dawn_d3d12_device;
   if (backend_type == wgpu::BackendType::D3D12) {
     dawn_d3d12_device = dawn::native::d3d12::GetD3D12Device(device.Get());
Loading diff…

Original Bug Report

reported by [email protected]

Potential cross-origin GPU memory disclosure via missing ValidateBeginAccess in D3DImageBacking

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

Overview: A synchronization flaw exists in D3DImageBacking::BeginAccessDawnBuffer where it fails to call ValidateBeginAccess. This allows a compromised renderer to establish concurrent, unsynchronized write access to a single D3D12 resource from both WebNN and WebGPU. This concurrent access can be used to exploit a Time-of-Check Time-of-Use (TOCTOU) vulnerability in Dawn’s indirect draw validation, leading to out-of-bounds reads and cross-origin GPU memory disclosure.

Affected files:

  • gpu/command_buffer/service/shared_image/d3d_image_backing.cc
  • gpu/command_buffer/service/shared_image/d3d_image_backing_factory.cc
  • services/webnn/webnn_context_impl.cc
  • gpu/command_buffer/service/webgpu_decoder_impl.cc

Estimated timestamp from git blame: 2024-09-12

Summary

A synchronization vulnerability exists in D3DImageBacking where the BeginAccessDawnBuffer method (located in gpu/command_buffer/service/shared_image/d3d_image_backing.cc) fails to call ValidateBeginAccess. This omission allows a compromised renderer to bypass internal state tracking (in_write_access_) and obtain concurrent write access to an underlying D3D12 resource while it is already locked for exclusive access by WebNN.

Vulnerability Details

In the D3DImageBacking implementation, all other access entry points—BeginAccessDawn, BeginAccessD3D11, and BeginAccessWebNN—call ValidateBeginAccess() to ensure that the backing is not already locked for exclusive (write) access.

However, BeginAccessDawnBuffer (lines 1591-1643) lacks this critical check. Furthermore, Dawn has its own internal mExclusiveAccess tracking, but this check is bypassed because WebNN access only updates Chrome’s SharedImage state (in_write_access_ via BeginAccessCommon) and does not interact with Dawn’s internal resource management.

Attack Scenario / Exploitation

This concurrency enables a Time-of-Check to Time-of-Use (TOCTOU) vulnerability in Dawn’s indirect draw validation. Specifically, Dawn’s IndirectDrawValidationEncoder reads parameters (like indexCount and firstIndex) from an indirect buffer, validates them against bounds, and then re-reads the buffer to copy the values to a safe scratch buffer for ExecuteIndirect.

An attacker can use WebNN to concurrently overwrite the indirect buffer after Dawn’s validation but before the copy. This allows unvalidated out-of-bounds indexCount or firstIndex values to be consumed by ExecuteIndirect. In D3D12, an out-of-bounds index buffer read will fetch arbitrary memory from the D3D12 heap. By crafting a vertex shader to reflect these fetched values, an attacker can leak cross-origin GPU memory, such as textures belonging to other origins or browser UI elements.

Suggested Steps to Reproduce

(Note: These are potential steps, as Fortify LLM agent doesn’t yet have the ability to run code.)

  1. Preparation: A compromised renderer creates a SharedImage with both SHARED_IMAGE_USAGE_WEBGPU_SHARED_BUFFER and SHARED_IMAGE_USAGE_WEBNN_SHARED_TENSOR usages. This combination is explicitly permitted in D3DImageBackingFactory::IsSupported.
  2. WebNN Access: The renderer sends an ImportTensor WebNN Mojo command to acquire WebNN access to the tensor. The GPU process executes BeginAccessWebNN, which calls BeginAccessCommon(true), setting in_write_access_ = true and clearing any existing write_fences_ and read_fences_.
  3. Bypassed WebGPU Access: Without finishing the WebNN access, the renderer sends an AssociateMailboxForBufferImmediate command for WebGPU using the same SharedImage mailbox. The GPU process executes BeginAccessDawnBuffer.
  4. Race Condition: Because BeginAccessDawnBuffer misses ValidateBeginAccess(true), it does not check in_write_access_. It reads the empty fences (cleared by WebNN) and initiates Dawn access with zero fence waits. Now, both DirectML (via WebNN) and Dawn (via WebGPU) are submitting commands to the same ID3D12Resource concurrently without synchronization.
  5. Exploitation: The attacker dispatches a WebGPU indirect draw command that sources parameters from this SharedImage buffer. Concurrently, the attacker dispatches a WebNN workload that rapidly writes out-of-bounds (OOB) values (e.g., extremely large indexCount) to the buffer. If the WebNN overwrite occurs precisely between Dawn’s validation read and its copy read in sRenderValidationShaderSource, Dawn will copy the unvalidated OOB values to its internal scratch buffer.
  6. Information Leak: The hardware executes ExecuteIndirect using the OOB parameters from the scratch buffer, fetching out-of-bounds indices from the D3D12 heap. The attacker’s vertex shader reflects these leaked values onto a render target, which is read back to the renderer, disclosing cross-origin memory.
  7. State Corruption: Upon EndAccess, the overlapping access causes num_readers_ to underflow (decrementing from 0 to -1), permanently corrupting the synchronization state for that SharedImage.

Suggested Fix

Add the missing validation check to D3DImageBacking::BeginAccessDawnBuffer:

wgpu::Buffer D3DImageBacking::BeginAccessDawnBuffer(
    const wgpu::Device& device,
    wgpu::BackendType backend_type,
    wgpu::BufferUsage usage) {
  AutoLock auto_lock(this);
  
  // Add validation check here
  if (!ValidateBeginAccess(true)) {
    return nullptr;
  }

  // ... rest of the function
}

Note: BeginAccessDawnBuffer implicitly grants write access as it creates a wgpu::Buffer and Dawn currently treats all mapped buffers as potentially writable from the perspective of SharedImage synchronization.

Evaluated with Chrome root at commit: 0eb4855bda702feaaa8b899336664f97e3df88b8


Results 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. Please feel free to reach out to me if you have concerns or feedback.

View on issue tracker