Medium watch ✔︎ verified CVSS 5.3 chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
5.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactObject lifecycle issue in Dawn
DescriptionObject lifecycle issue in Dawn
ComponentDawn
Bug ClassLogic Error
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: none. Citations verified: SetInitialized at 1166-1167 precedes fallible DAWN_TRY_ASSIGN CreateBuffer (1199), CreateView (1324), bind groups (1327/1329); diff moves SetInitialized after them, before BeginComputePass (1356). Leak path (OOM, abandon encoder, map) correctly inferred.
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker497594413
Fix commit8d862f1501d1 (dawn) +3/-2
CWECWE-664
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-03-30 (per vendor advisory)
Fix landed2026-03-31 (public git)
Disclosed2026-05-12
Patch gap42 days — fix public in source before the advisory

Background

`BlitTextureToBuffer`
Dawn’s compute-shader workaround that copies a texture into a buffer on backends lacking a native path.
`SetInitialized`
marks a buffer’s data as initialized so Dawn can skip lazy zero-clearing on first use.
`fullSizeCopy`
a flag meaning the planned copy overwrites the whole destination buffer, in which case pre-clearing would be redundant.
`DAWN_TRY` / `DAWN_TRY_ASSIGN`
macros that return early from the encoding function when an internal operation (such as device->CreateBuffer) fails.

Root Cause Analysis

In the pre-fix BlitTextureToBuffer (src/dawn/native/BlitTextureToBuffer.cpp), the destination buffer was marked initialized very early: at lines 1166-1167, right after computing fullSizeCopy, the code ran dst.buffer->SetInitialized(fullSizeCopy || dst.buffer->IsInitialized()). Only afterward did the function perform the fallible work that can abort the encode — for example the intermediate-buffer path at line 1199 (DAWN_TRY_ASSIGN(destinationBuffer, device->CreateBuffer(&descriptor))) and later DAWN_TRY_ASSIGN calls for the source view and bind groups (lines 1324, 1327, 1329). The invariant that must hold is “a buffer is only marked initialized once the operation that will actually fill it is guaranteed to be recorded.” Because SetInitialized(true) ran first, a failure in a later device->Create* step would return early with the buffer already flagged initialized even though no clearing or copy was recorded, so a subsequent read could observe stale, un-cleared memory.

The fix removes the early call at lines 1166-1167 and re-inserts the same SetInitialized(fullSizeCopy || dst.buffer->IsInitialized()) after those fallible creation steps (near line 1353), immediately before commandEncoder->BeginComputePass() at line 1356. This TIGHTENS ordering so the initialized flag is only set once the fallible setup has succeeded, while still being set before encoding so the clear can still be skipped when unneeded.

Key insight
The buffer was marked initialized before the fallible allocations that make the fill possible, so an early-return on failure left it flagged initialized but never cleared or written; the fix defers SetInitialized to after those allocations succeed.

Attack Path

  1. Target a blit-workaround copy Content issues a texture-to-buffer copy that routes through BlitTextureToBuffer and fully overwrites the destination, setting fullSizeCopy. (inferred)
  2. Force an internal allocation failure An internal device->CreateBuffer or similar step fails, triggering a DAWN_TRY/DAWN_TRY_ASSIGN early return after the buffer was already marked initialized (pre-fix BlitTextureToBuffer.cpp:1166-1167, :1199). (inferred)
  3. Abandon the encode The failing encode is discarded so no clear/copy is ever recorded for the buffer. (inferred)
  4. Read uninitialized memory A later use of the buffer skips lazy clearing because it is flagged initialized, exposing previous GPU memory contents. (inferred)

Impact Assessment

If an internal allocation inside the blit workaround failed, the destination buffer could be left flagged as initialized without being cleared or written, allowing later reads of uninitialized GPU memory in the GPU process. The advisory classifies this as an object-lifecycle issue in Dawn, rates it medium severity, and marks it not exploited in the wild. The full end-to-end disclosure (forcing OOM, abandoning the encoder, mapping the buffer) is described by the reporter but not demonstrated by the shown diff, so those steps are (inferred).

Files Changed

  • src/dawn/native/BlitTextureToBuffer.cpp

Audit Directions

  • State-before-fallible-op
    Flag code that sets a success/initialized flag before subsequent operations that can fail and early-return, leaving the flag inconsistent with reality.
  • Lazy-clear bypass
    Audit any SetInitialized(true) that governs skipping zero-initialization to ensure the actual fill is guaranteed before the flag is set.
  • Early-return error macros
    Where DAWN_TRY/DAWN_TRY_ASSIGN (or equivalent) can bail mid-function, check that no externally visible object state was committed earlier in the function.
From 8d862f1501d19ebe7e1e030fc71158a015d0db64 Mon Sep 17 00:00:00 2001
From: Shrek Shao <shrekshao@google.com>
Date: Tue, 31 Mar 2026 01:18:05 -0700
Subject: [PATCH] BlitTextureToBuffer.cpp: slightly defer dst.buffer->SetInitialized call

This CL slightly makes the code nicer and more correct.

Moves `dst.buffer->SetInitialized` to
- After several `device->Create*` that may throw error so the internal
  mIsDataInitialized is less likely leaking.
- Before the commandEncoder encoding so it can still skip the extra
  clearing when not needed.

Bug: 497594413
Change-Id: Ic7e5e14d5956500edbda017cf9eba910b15d777c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/300316
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Auto-Submit: Shrek Shao <shrekshao@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
---

diff --git a/src/dawn/native/BlitTextureToBuffer.cpp b/src/dawn/native/BlitTextureToBuffer.cpp
index b7c606a..2e84864 100644
--- a/src/dawn/native/BlitTextureToBuffer.cpp
+++ b/src/dawn/native/BlitTextureToBuffer.cpp
@@ -1163,8 +1163,6 @@
 
     const bool fullSizeCopy = IsFullBufferOverwrittenInTextureToBufferCopy(
         src, dst, blockInfo.ToTexel(copyExtent).ToExtent3D());
-    // Skip clearing the buffer if this is full size copy.
-    dst.buffer->SetInitialized(fullSizeCopy || dst.buffer->IsInitialized());
 
     Ref<BufferBase> destinationBuffer = dst.buffer.Get();
     const uint64_t numBytesToCopy =
@@ -1353,6 +1351,9 @@
                                                          UsageValidationMode::Internal));
     }
 
+    // Skip clearing the buffer if this is full size copy.
+    dst.buffer->SetInitialized(fullSizeCopy || dst.buffer->IsInitialized());
+
     Ref<ComputePassEncoder> pass = commandEncoder->BeginComputePass();
     pass->APISetPipeline(pipeline.Get());
     pass->APISetBindGroup(0, bindGroup0.Get());
Loading diff…

Original Bug Report

reported by vm...@google.com

GPU memory information leak via premature SetInitialized in BlitTextureToBuffer

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

Overview: Dawn’s BlitTextureToBuffer prematurely marks destination buffers as initialized before performing fallible internal operations like memory allocation. If an allocation fails (e.g., OOM) and the CommandEncoder is abandoned, the error is silently discarded while the buffer remains marked as initialized, bypassing LazyClearResourceOnFirstUse and allowing an attacker to map uninitialized GPU memory.

Affected files:

  • third_party/dawn/src/dawn/native/BlitTextureToBuffer.cpp
  • third_party/dawn/src/dawn/native/Buffer.cpp
  • third_party/dawn/src/dawn/native/EncodingContext.cpp

Estimated timestamp from git blame: 2024-07-02

Description

In third_party/dawn/src/dawn/native/BlitTextureToBuffer.cpp, the BlitTextureToBuffer function is used as a workaround for certain texture-to-buffer copies on backends like OpenGL, GLES, and D3D11. This function prematurely updates the initialized state of the destination buffer before performing fallible internal operations.

At line 1167 in BlitTextureToBuffer.cpp:

dst.buffer->SetInitialized(fullSizeCopy || dst.buffer->IsInitialized());

If fullSizeCopy is true (meaning the copy operation is expected to overwrite the entire buffer), the destination buffer is immediately marked as initialized.

Following this, several internal operations occur that can fail, most notably device->CreateBuffer (e.g., at lines 1199 and 1253) which may fail with an Out Of Memory (OOM) error if GPU memory is exhausted. When such an error occurs, the DAWN_TRY or DAWN_TRY_ASSIGN macros cause the function to return early. In the context of a CommandEncoder, this error is deferred into the EncodingContext.

If the attacker then releases the CommandEncoder without calling finish(), the deferred error is silently discarded during EncodingContext::Destroy(). No device-lost event occurs, and the destination buffer remains in the incorrectly marked ‘initialized’ state.

Because the recording of the BlitTextureToBuffer commands was aborted, the destination buffer’s contents are never actually updated or cleared. However, since the buffer is marked as initialized, subsequent operations (such as MapAsync or using the buffer as a source in another copy) will evaluate NeedsInitialization() as false. This skips the LazyClearResourceOnFirstUse step (verified in Buffer.cpp), which is intended to zero-initialize buffers before their first use. This allows an attacker to read uninitialized GPU memory, potentially containing sensitive data from other WebGPU contexts or previous allocations in the same GPU process.

Note: This analysis relies on source code review. The steps below outline a potential exploit strategy but have not been verified with a live Proof of Concept.

Potential Reproduction Steps

  1. Use a WebGPU-enabled browser on a platform using the OpenGLES or D3D11 backend.
  2. Create a GPUBuffer (B) with COPY_DST | MAP_READ usage and a size matching a planned copy.
  3. Create a GPUTexture (T) with a format that triggers a blit workaround (e.g., depth16unorm on GLES).
  4. Induce GPU memory pressure to ensure internal allocations fail (e.g., by allocating many large buffers until near the driver limit).
  5. Use a GPUCommandEncoder to call copyTextureToBuffer({texture: T}, {buffer: B}, ...) such that it covers the full buffer (fullSizeCopy becomes true).
  6. Internal CreateBuffer calls within BlitTextureToBuffer fail due to OOM. The error is deferred in the encoder’s EncodingContext.
  7. Destroy/Release the GPUCommandEncoder without calling finish(). The deferred error is silently dropped during EncodingContext::Destroy().
  8. Release the pressure buffers from step 4.
  9. Call B.mapAsync(GPUMapMode.READ) and read the buffer’s contents via getMappedRange(). The contents will be uninitialized GPU memory instead of zeros, as LazyClearResourceOnFirstUse is bypassed.

Suggested Fix

Ensure that the destination buffer is only marked as initialized after all fallible operations in BlitTextureToBuffer have succeeded, or defer the SetInitialized(true) call until the command is actually submitted or executed. Alternatively, track whether the initialization was performed by a specific command and revert the state if the command fails to encode or is discarded.

Evaluated with Chrome root at commit: a9cbf6e8b275fe4147435aa905f3b7f5a656f5f0


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