Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Dawn
DescriptionInsufficient validation of untrusted input in Dawn
ComponentDawn
Bug ClassLogic Error
Tracker500140111
Fix commite01ebb9771ea (dawn) +35/-7
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Changed Functions

FunctionChangeNotes
if
src/dawn/native/Texture.cpp
modified
switch
src/dawn/native/Texture.cpp
modified
if
src/dawn/native/vulkan/TextureVk.cpp
modified

Files Changed

  • src/dawn/native/Texture.cpp
  • src/dawn/native/vulkan/TextureVk.cpp
From e01ebb9771eac8a4b4853d51a5839c52e39df04b Mon Sep 17 00:00:00 2001
From: Brandon Jones <bajones@chromium.org>
Date: Wed, 08 Apr 2026 15:33:29 -0700
Subject: [PATCH] Validate internal usage when creating views

Validates texture usage constraints against the internal usage
of a texture rather that the public usage when creating a view.
This prevents things like STORAGE usages from being used when
creating views that use a an sRGB format.

Bug: 500140111
Change-Id: I7e284c88284bebdf21a5a194a3809dba006b9234
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/301456
Reviewed-by: Loko Kung <lokokung@google.com>
Reviewed-by: Brandon Jones <bajones@chromium.org>
Commit-Queue: Brandon Jones <bajones@chromium.org>
Auto-Submit: Brandon Jones <bajones@chromium.org>
---

diff --git a/src/dawn/native/Texture.cpp b/src/dawn/native/Texture.cpp
index d23bd66..08acd26 100644
--- a/src/dawn/native/Texture.cpp
+++ b/src/dawn/native/Texture.cpp
@@ -696,6 +696,30 @@
     return internalUsage;
 }
 
+// Removes internal usages incompatible with the format/dimension before calling AddInternalUsages.
+wgpu::TextureUsage AdjustViewInternalUsages(const DeviceBase* device,
+                                            wgpu::TextureUsage usage,
+                                            const Format& format,
+                                            const wgpu::TextureViewDimension dimension,
+                                            uint32_t sampleCount,
+                                            uint32_t mipLevelCount,
+                                            uint32_t arrayLayerCount) {
+    wgpu::TextureUsage internalUsage = usage;
+
+    // Remove render attachment usage if the dimension is 1D
+    if (dimension == wgpu::TextureViewDimension::e1D) {
+        internalUsage &= ~wgpu::TextureUsage::RenderAttachment;
+    }
+
+    // Remove storage usage if neither read or write is supported.
+    if (!format.SupportsReadOnlyStorageUsage() && !format.SupportsWriteOnlyStorageUsage()) {
+        internalUsage &= ~wgpu::TextureUsage::StorageBinding;
+    }
+
+    return AddInternalUsages(device, internalUsage, format, sampleCount, mipLevelCount,
+                             arrayLayerCount);
+}
+
 wgpu::ComponentSwizzle ComposeSwizzleComponent(wgpu::TextureComponentSwizzle swizzle,
                                                wgpu::ComponentSwizzle component) {
     switch (component) {
@@ -1828,13 +1852,14 @@
               {descriptor->baseArrayLayer, descriptor->arrayLayerCount},
               {descriptor->baseMipLevel, descriptor->mipLevelCount}}),
       mUsage(GetTextureViewUsage(texture->GetUsage(), descriptor->usage)),
-      mInternalUsage(
-          AddInternalUsages(GetDevice(),
-                            GetTextureViewUsage(texture->GetInternalUsage(), descriptor->usage),
-                            *mFormat,
-                            texture->GetSampleCount(),
-                            texture->GetNumMipLevels(),
-                            texture->GetArrayLayers())) {
+      mInternalUsage(AdjustViewInternalUsages(
+          GetDevice(),
+          GetTextureViewUsage(texture->GetInternalUsage(), descriptor->usage),
+          *mFormat,
+          descriptor->dimension,
+          texture->GetSampleCount(),
+          texture->GetNumMipLevels(),
+          texture->GetArrayLayers())) {
     if (auto* swizzleDesc = descriptor.Get<TextureComponentSwizzleDescriptor>()) {
         auto swizzle = swizzleDesc->swizzle.WithTrivialFrontendDefaults();
         mSwizzleRed = swizzle.r;
diff --git a/src/dawn/native/vulkan/TextureVk.cpp b/src/dawn/native/vulkan/TextureVk.cpp
index f5f4acb..f5ec1cf 100644
--- a/src/dawn/native/vulkan/TextureVk.cpp
+++ b/src/dawn/native/vulkan/TextureVk.cpp
@@ -668,6 +668,9 @@
         }
     }
     if (usage & wgpu::TextureUsage::StorageBinding) {
+        // Storage bit should only be included if the format actually supports a storage uage.
+        DAWN_ASSERT(format.SupportsReadOnlyStorageUsage() ||
+                    format.SupportsWriteOnlyStorageUsage());
         flags |= VK_IMAGE_USAGE_STORAGE_BIT;
     }
     if (usage & wgpu::TextureUsage::RenderAttachment) {
Loading diff…

Original Bug Report

reported by vm...@google.com

Vulkan VUID violation via Dawn internal usage validation bypass

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 security team.

Overview: A compromised renderer can use DawnTextureInternalUsageDescriptor to bypass texture view validation in Dawn. This allows the creation of Vulkan image views with incompatible formats and usage flags, such as SRGB formats with storage usage. This violates Vulkan specifications and can cause undefined behavior in the GPU process.

Affected files:

  • third_party/dawn/src/dawn/native/Texture.cpp
  • third_party/dawn/src/dawn/native/vulkan/TextureVk.cpp
  • gpu/command_buffer/service/webgpu_decoder_impl.cc

Estimated timestamp from git blame: 2025-11-11

Background

In Dawn, textures and texture views possess both public and internal usages. Public usages are defined through the standard WebGPU API, while internal usages are utilized for implementation-specific optimizations and interop. The DawnTextureInternalUsageDescriptor allows specifying internal usages directly. In Chrome, the DawnInternalUsages feature is enabled by default in the GPU process for all web contexts (via gpu/command_buffer/service/webgpu_decoder_impl.cc), and this descriptor is wire-serializable, meaning a compromised renderer can send it over IPC.

Vulnerability

There is a critical divergence between how a TextureView’s usage is validated and how it is ultimately constructed:

  1. Validation: When a texture view is created with a requested usage of None (0), it inherits its usage from the base texture. The validation function ValidateTextureViewUsage calculates this by calling GetTextureViewUsage(texture->GetUsage(), usage). Crucially, this only fetches the base texture’s public usage and ignores its internal usage.
  2. Construction: During object construction, the TextureViewBase constructor initializes the view’s internal usage by calling GetTextureViewUsage(texture->GetInternalUsage(), descriptor->usage). This populates the view with all internal flags from the base texture.

If a texture is created with a public usage of TextureBinding and an internal usage of StorageBinding, creating a view with None usage and an SRGB format (like RGBA8UnormSrgb) will pass validation. ValidateTextureViewUsage only sees TextureBinding (which is compatible with SRGB formats). However, the resulting view object inherits the internal StorageBinding flag.

Impact

When the Vulkan backend initializes the view (TextureView::Initialize in TextureVk.cpp), it translates the view’s internal usages to Vulkan flags. The hidden StorageBinding is translated to VK_IMAGE_USAGE_STORAGE_BIT.

The backend then calls vkCreateImageView with VK_FORMAT_R8G8B8A8_SRGB and VK_IMAGE_USAGE_STORAGE_BIT. This violates Vulkan specification VUID-VkImageViewCreateInfo-usage-02275, which strictly requires that any image view with a storage usage bit must have a format that supports VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT. Since SRGB formats universally lack this feature, the graphics driver is forced into undefined behavior. This can lead to driver crashes, memory corruption, or potential sandbox escapes from the GPU process.

Suggested Reproduction Steps

Note: These are potential steps as our tooling agent cannot execute arbitrary code.

  1. From a compromised renderer process, send a DeviceCreateTexture IPC command to create a texture with:
    • format: wgpu::TextureFormat::RGBA8Unorm
    • usage: wgpu::TextureUsage::TextureBinding
    • viewFormats: [wgpu::TextureFormat::RGBA8UnormSrgb]
    • nextInChain: A DawnTextureInternalUsageDescriptor with internalUsage set to wgpu::TextureUsage::StorageBinding.
  2. Send a TextureCreateView IPC command for the new texture, specifying:
    • format: wgpu::TextureFormat::RGBA8UnormSrgb
    • usage: wgpu::TextureUsage::None (0).
  3. Observe that ValidateTextureViewUsage passes because it only checks TextureBinding against RGBA8UnormSrgb.
  4. The view is constructed with an internal usage that includes StorageBinding.
  5. In the GPU process’s Vulkan backend, vkCreateImageView is executed with an SRGB format and VK_IMAGE_USAGE_STORAGE_BIT, triggering the VUID violation.

Suggested Fix

ValidateTextureViewUsage in third_party/dawn/src/dawn/native/Texture.cpp must be updated to validate the internal usages that the texture view will actually inherit.

Instead of only checking GetTextureViewUsage(texture->GetUsage(), usage), the validation logic should compute the inherited internal usage (GetTextureViewUsage(texture->GetInternalUsage(), usage)) and ensure that these combined usage flags are compatible with the requested view format before allowing the view to be created.

Evaluated with Chrome root at commit: f200f57a19490707ff8bc7aa5de3cbc443a3afad


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. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker