High watch CVSS 8.8 chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized Use in Dawn
DescriptionUninitialized Use in Dawn
ComponentDawn
Bug ClassUninitialized Memory
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar
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.
Tracker513209610
Fix commite7eb019e0d82 (dawn) +14/-4
CWECWE-457 (Use of uninitialized)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-05-14 (per vendor advisory)
Fix landed2026-05-15 (public git)
Disclosed2026-06-30
Patch gap46 days — fix public in source before the advisory

Background

`Blob`
Dawn’s owning byte-buffer type (src/dawn/native/Blob.cpp) that wraps a mData span plus a mDeleter, and can be sliced via Blob::Create(Blob&&, offset).
`VkPipelineCache`
a Vulkan object that stores compiled pipeline state, whose serialized bytes are retrieved with vkGetPipelineCacheData() and persisted by Dawn’s blob cache for reuse across runs.
`vkGetPipelineCacheData()`
the Vulkan entry point that copies pipeline-cache contents into a caller-supplied buffer and reports how many bytes were actually written.
`VulkanIncompletePipelineCacheWorkaround`
a Dawn workaround flag for drivers that return corrupted or incomplete pipeline-cache data, signaling that the retrieved bytes must not be trusted.

Root Cause Analysis

When PipelineCacheVk serialized its VkPipelineCache, it first queried the required size, allocated a Blob of that size with Blob::Create(size), and then called vkGetPipelineCacheData() to fill it. The Vulkan spec permits the driver to write fewer bytes than the previously reported size, but the code assumed the allocated blob was fully populated and stored the entire buffer — including the trailing bytes the driver never wrote — into the persistent blob cache. Because the allocated blob’s tail was never initialized, this leaked uninitialized heap memory into the cache and could feed it back on later runs.

The fix compares the actual bufferSize returned against blob->Size() and, when it is smaller, shrinks the blob to exactly the written extent via the new Blob::Create(std::move(*blob), 0, bufferSize) overload; it additionally clears the blob (*blob = {}) when VulkanIncompletePipelineCacheWorkaround fires so suspected-corrupt data is never persisted.

Key insight
The core mistake was trusting the initially reported cache size as the amount of valid data actually written, so uninitialized tail bytes of the allocation were serialized into the cache; the fix trims the blob to the byte count vkGetPipelineCacheData() actually reports.

Attack Path

  1. Trigger cache serialization Cause Dawn to build and serialize a Vulkan pipeline cache, invoking PipelineCacheVk’s store path that allocates a Blob sized to the pre-queried cache size.
  2. Driver underfills the buffer On a driver that writes fewer bytes than reported, vkGetPipelineCacheData() returns a bufferSize smaller than the allocated blob->Size(), leaving the blob’s tail uninitialized.
  3. Uninitialized data is persisted The unpatched code stores the full-size blob, including the uninitialized tail, into the persistent blob cache.
  4. Read back leaked memory An attacker with access to the on-disk cache (or code that later deserializes and inspects it) recovers the stale heap bytes, potentially disclosing sensitive process memory.

Impact Assessment

An attacker gains disclosure of uninitialized heap memory from the GPU-process/browser context that runs Dawn, since stale allocation bytes are written into the persistent pipeline-cache blob. Exploitation requires a Vulkan driver whose vkGetPipelineCacheData() writes less than the queried size (or that triggers VulkanIncompletePipelineCacheWorkaround) and the ability to observe the serialized cache contents. The severity is limited to information leakage rather than direct memory corruption.

Files Changed

  • src/dawn/native/Blob.cpp
  • src/dawn/native/Blob.h
  • src/dawn/native/vulkan/PipelineCacheVk.cpp

Audit Directions

  • Report-then-fill APIs
    Audit any code that pre-queries a size, allocates a buffer, then calls a fill API assuming full population; verify the actually-written count is used for serialization or hashing.
  • Serialized/cached buffers
    Review paths that persist allocated buffers to disk or the blob cache to confirm no uninitialized or padding bytes are ever stored.
  • Driver workaround flags
    Trace every workaround like VulkanIncompletePipelineCacheWorkaround to ensure that data flagged as suspect is discarded rather than silently propagated.
From e7eb019e0d8237395c80b6df7790085d09591105 Mon Sep 17 00:00:00 2001
From: Kyle Charbonneau <[email protected]>
Date: Fri, 15 May 2026 07:40:37 -0700
Subject: [PATCH] Trim data returned for VkPipelineCache

vkCreateGraphicsPipeline() can write less data than the allocated blob
stores. If that happens shrink the blob to match the size written.

Also if VulkanIncompletePipelineCacheWorkaround is triggered then ensure
it's not stored into the cache. The data is suspected to be corrupted if
the workaround triggers.

Bug: 513209610
Change-Id: I4490dcecdd2a5a18d3118557d8fe233f37ac2ac8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/308856
Commit-Queue: Kyle Charbonneau <[email protected]>
Reviewed-by: Loko Kung <[email protected]>
---

diff --git a/src/dawn/native/Blob.cpp b/src/dawn/native/Blob.cpp
index 1c6072d..784a0f4 100644
--- a/src/dawn/native/Blob.cpp
+++ b/src/dawn/native/Blob.cpp
@@ -53,8 +53,8 @@
 }
 
 // static
-Blob Blob::Create(Blob&& original, size_t offset) {
-    Blob result(original.mData.subspan(offset), std::move(original.mDeleter));
+Blob Blob::Create(Blob&& original, size_t offset, size_t extent) {
+    Blob result(original.mData.subspan(offset, extent), std::move(original.mDeleter));
     original.mData = {};
     original.mDeleter = nullptr;
     return result;
diff --git a/src/dawn/native/Blob.h b/src/dawn/native/Blob.h
index 195c5bf1..d398a56 100644
--- a/src/dawn/native/Blob.h
+++ b/src/dawn/native/Blob.h
@@ -47,8 +47,9 @@
     // Creates a blob of the given size.
     static Blob Create(size_t size);
 
-    // Takes ownership of the original blob, creating a new one that offsets the data by |offset|.
-    static Blob Create(Blob&& original, size_t offset);
+    // Takes ownership of the original blob, creating a new one with subspan of the original using
+    // `offset` and optionally `extent`.
+    static Blob Create(Blob&& original, size_t offset, size_t extent = std::dynamic_extent);
 
     template <typename T>
     static Blob Create(std::vector<T> vec)
diff --git a/src/dawn/native/vulkan/PipelineCacheVk.cpp b/src/dawn/native/vulkan/PipelineCacheVk.cpp
index 26ef309..bfa7fcd 100644
--- a/src/dawn/native/vulkan/PipelineCacheVk.cpp
+++ b/src/dawn/native/vulkan/PipelineCacheVk.cpp
@@ -28,6 +28,7 @@
 #include "dawn/native/vulkan/PipelineCacheVk.h"
 
 #include <memory>
+#include <utility>
 
 #include "dawn/native/Device.h"
 #include "dawn/native/Error.h"
@@ -102,10 +103,18 @@
         // store it to the blob cache and don't call vkGetPipelineCacheData() since it will return
         // corrupted data in future calls.
         mSkipSerialize = true;
+        *blob = {};
         return {};
     }
 
     DAWN_TRY(CheckVkSuccess(result, "GetPipelineCacheData"));
+
+    if (bufferSize < blob->Size()) {
+        // vkGetPipelineCacheData() returned less data than expected. Shrink the blob so
+        // uninitialized data isn't stored in cache.
+        *blob = Blob::Create(std::move(*blob), 0, bufferSize);
+    }
+
     mStoredDataSize = bufferSize;
 
     return {};
Loading diff…

Original Bug Report

reported by [email protected]

Potential uninitialized memory persistence and corruption in Dawn Vulkan pipeline cache

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A logic error in Dawn’s Vulkan backend allows uninitialized GPU process heap memory to be persisted to the on-disk shader cache when a specific driver workaround is triggered. This occurs because the code fails to clear a previously allocated buffer after an incomplete serialization attempt. The persisted data is later reloaded and passed to the Vulkan driver, potentially leading to memory corruption.

Affected files:

  • third_party/dawn/src/dawn/native/vulkan/PipelineCacheVk.cpp
  • third_party/dawn/src/dawn/native/PipelineCache.cpp
  • third_party/dawn/src/dawn/native/Blob.cpp

Estimated timestamp from git blame: Unknown (Google3 checkout)

Description

A potential logic error exists in PipelineCache::SerializeToBlobImpl within Dawn’s Vulkan backend that can cause uninitialized heap memory to be persisted to the persistent blob cache. This occurs when the VulkanIncompletePipelineCacheWorkaround is triggered on specific hardware (e.g., Pixel 10, Samsung Xclipse).

Root Cause Analysis

In third_party/dawn/src/dawn/native/vulkan/PipelineCacheVk.cpp, the function SerializeToBlobImpl handles the serialization of the Vulkan pipeline cache:

  1. At line 95, a blob is allocated to hold the cache data: *blob = Blob::Create(bufferSize);.
  2. Blob::Create in third_party/dawn/src/dawn/native/Blob.cpp (line 46) allocates memory via new uint8_t[size], which leaves the buffer uninitialized, containing residual data from the GPU process heap.
  3. vkGetPipelineCacheData is called to fill this buffer (line 96).
  4. If the call returns VK_INCOMPLETE and the workaround is enabled (line 99), the code sets mSkipSerialize = true and returns success (return {};) at line 105.

However, the function fails to clear or reset the *blob out-parameter before returning. The caller, PipelineCacheBase::Flush() in third_party/dawn/src/dawn/native/PipelineCache.cpp, subsequently checks if (blob.Size() > 0) at line 62. Since the blob was assigned an uninitialized buffer of bufferSize, this condition is true, and the uninitialized memory is stored to the persistent cache via mCache->Store(mKey, blob); (line 65).

Potential Impact

  1. Information Leak: Sensitive residue from the GPU process heap is written to the persistent shader cache on disk.
  2. Memory Corruption: On subsequent sessions, the persisted uninitialized blob is reloaded and passed as pInitialData to vkCreatePipelineCache (line 130 of PipelineCacheVk.cpp). If the driver’s binary parser is not robust against malformed or uninitialized input, this could lead to memory corruption within the GPU process.
  3. Sandbox Escape (Android): On Android, the GPU process is typically unsandboxed by default. Memory corruption in this process could allow an attacker to achieve arbitrary code execution with the user’s full privileges.

Potential Reproduction Steps (Suggested)

Note: These steps are theoretical as they rely on specific hardware behavior and have not been validated by a running exploit.

  1. Use an affected Android device (e.g., Pixel 10 or Samsung Xclipse) with WebGPU enabled.
  2. From a web page, trigger the compilation of a new shader (e.g., via createComputePipeline).
  3. The Vulkan driver returns VK_INCOMPLETE during cache serialization.
  4. Dawn’s workaround triggers but fails to clear the blob out-parameter.
  5. The uninitialized heap memory is written to the on-disk cache.
  6. Restart the browser and re-trigger the same pipeline creation. The corrupted cache is loaded and passed to the driver’s vkCreatePipelineCache, potentially causing corruption.

Suggested Fix

Ensure the *blob out-parameter is cleared before returning in the VK_INCOMPLETE workaround path in third_party/dawn/src/dawn/native/vulkan/PipelineCacheVk.cpp:

if (result == VK_INCOMPLETE && mInvalidResultWorkaround) {
    mSkipSerialize = true;
    *blob = Blob(); // Clear the out-parameter to prevent storage
    return {};
}

Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

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