Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds read in GPU
DescriptionOut of bounds read in GPU
ComponentGPU
Bug ClassOOB
Tracker496395450
Fix commita93eb13c129e (chromium/src) +5/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-12

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
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