Overview

Critical
Severity
9.6
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactBuffer overflow in Dawn
DescriptionBuffer overflow in Dawn
ComponentDawn
Sub-componentDawn
Bug ClassOOB
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable
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.
Tracker540087398
Fix commit178fb7048ad3 (dawn) +28/-1
CWECWE-122 (Heap buffer overflow)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-07-28 (per vendor advisory)
Fix landed2026-07-30 (public git)
Disclosed2026-08-18
Patch gap19 days — fix public in source before the advisory

Background

Dawn
Chromium’s native implementation of the WebGPU API, translating WebGPU calls into backend graphics APIs such as Vulkan.
Mipmap
a precomputed chain of progressively halved-resolution copies of a texture, where each mip level’s dimensions are derived from the base size.width and size.height.
NPOT (non-power-of-two)
a texture whose width or height is not a power of two, which can make per-mip-level size computation ambiguous under buggy driver rounding rules.
Toggle
a Dawn feature/workaround flag (declared in Toggles.h, described in Toggles.cpp) that can be enabled per-device to change validation or backend behavior.

Root Cause Analysis

On the PowerVR/Imagination (ImgTec) proprietary Vulkan driver, the code path that creates a depth/stencil texture with mipLevelCount > 1 and non-power-of-two dimensions triggers a driver-side miscomputation of each mip level’s size. Because the driver derives smaller mip extents differently than Dawn and the WebGPU spec expect, subsequent operations (such as queue.writeTexture to a lower mip, exercised by QueueWriteTextureTests::TextureWriteToMip) address memory using mismatched dimensions, producing an out-of-bounds buffer overflow. Dawn’s texture validation in ValidateTextureDescriptor previously only checked that depth/stencil textures were 2D and did not reject the NPOT-plus-mipmaps combination on affected hardware.

The fix introduces the VulkanDisallowNPOTDepthStencilMipmaps toggle, defaulted on for Imagination proprietary devices, and adds a DAWN_INVALID_IF guard that rejects the dangerous descriptor before any allocation occurs, so the miscomputed mip sizes are never reached.

Key insight
The core mistake is trusting the driver to correctly compute mip-level extents for NPOT depth/stencil textures, which it does not on PowerVR; the fix sidesteps the driver defect entirely by validating and rejecting that texture configuration up front on the affected devices.

Attack Path

  1. Reach texture creation A web page uses the WebGPU API to call device.createTexture with a depth or stencil format on a device backed by the Imagination proprietary Vulkan driver.
  2. Request NPOT mipmaps The descriptor specifies non-power-of-two size.width/size.height together with mipLevelCount > 1, which pre-patch validation accepts.
  3. Trigger miscomputed mip sizes The PowerVR driver computes the extents of the smaller mip levels incorrectly, leaving Dawn’s and the driver’s notions of each mip’s size out of sync.
  4. Overflow the buffer A subsequent write or copy to one of the mismatched mip levels (e.g. via queue.writeTexture) reads or writes past the true allocation bounds, causing the buffer overflow.

Impact Assessment

An attacker who can run WebGPU shaders/commands from a web page gains an out-of-bounds buffer overflow inside the GPU process on affected Android/PowerVR (ImgTec) devices. The preconditions are that the victim uses hardware backed by the Imagination proprietary Vulkan driver and the page can issue a depth/stencil texture creation with NPOT dimensions and multiple mip levels, memory corruption that can serve as a step toward sandbox escape or further exploitation.

Changed Functions

FunctionChangeNotes
TEST_P
src/dawn/tests/end2end/QueueTests.cpp
modified

Files Changed

  • webgpu-cts/expectations.txt
  • src/dawn/native/Toggles.h
  • src/dawn/tests/end2end/QueueTests.cpp
  • src/dawn/native/vulkan/PhysicalDeviceVk.cpp
  • src/dawn/native/Toggles.cpp
  • src/dawn/native/Texture.cpp

Audit Directions

  • Driver-specific size math
    Review other backend code paths that rely on drivers to derive per-mip or per-array-layer extents for NPOT resources, since the same miscomputation may exist for color formats or 3D textures.
  • Validation gaps by hardware
    Search Dawn validation for descriptor combinations accepted globally but known-buggy on a specific vendor, where a targeted toggle-guarded DAWN_INVALID_IF may be missing.
  • Mip-addressed transfers
    Audit writeTexture, copyTextureToTexture, and similar operations that index into individual mip levels, verifying that Dawn’s computed extents match backend assumptions before memory is accessed.
From 178fb7048ad322e4ff797210358ed05254289e0c Mon Sep 17 00:00:00 2001
From: Kai Ninomiya <[email protected]>
Date: Thu, 30 Jul 2026 16:20:14 -0700
Subject: [PATCH] [dawn][vulkan] Disallow NPOT mipmapped depth/stencil textures on ImgTec

Can't think of a way to work around this, so just disable it.

Fixes: 540087398
Cq-Include-Trybots: luci.chromium.try:android-dawn-arm64-p10-rel
Change-Id: I22ce9c18564d338c9ee15e18cfe4598bc34cda95
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/328455
Reviewed-by: Brandon Jones <[email protected]>
Commit-Queue: Kai Ninomiya <[email protected]>
---
diff --git a/src/dawn/native/Texture.cpp b/src/dawn/native/Texture.cpp
index eb3b0ee..23c6752 100644
--- a/src/dawn/native/Texture.cpp
+++ b/src/dawn/native/Texture.cpp
@@ -875,6 +875,15 @@
                         (format->aspects & (Aspect::Depth | Aspect::Stencil)),
                     "The dimension (%s) of a texture with a depth/stencil format (%s) is not 2D.",
                     descriptor->dimension, format->format);
+    if (device->IsToggleEnabled(Toggle::VulkanDisallowNPOTDepthStencilMipmaps)) {
+        DAWN_INVALID_IF(
+            (format->aspects & (Aspect::Depth | Aspect::Stencil)) &&
+                descriptor->mipLevelCount > 1 &&
+                (!IsPowerOfTwo(descriptor->size.width) || !IsPowerOfTwo(descriptor->size.height)),
+            "Non-power-of-two depth/stencil texture (%s) with mipLevelCount (%u) > 1 is "
+            "disallowed on this device due to a driver bug.",
+            descriptor->size, descriptor->mipLevelCount);
+    }
 
     DAWN_TRY(ValidateTextureSize(device, *descriptor, format));
     return {};
diff --git a/src/dawn/native/Toggles.cpp b/src/dawn/native/Toggles.cpp
index b553e64..9dc2218 100644
--- a/src/dawn/native/Toggles.cpp
+++ b/src/dawn/native/Toggles.cpp
@@ -819,6 +819,11 @@
      {"vulkan_replace_workgroup_atomic_store_with_exchange",
       "Workaround for a driver bug that implements atomic store incorrectly.",
       "https://crbug.com/487773864", ToggleStage::Device}},
+    {Toggle::VulkanDisallowNPOTDepthStencilMipmaps,
+     {"vulkan_disallow_npot_depth_stencil_mipmaps",
+      "Reject NPOT depth/stencil textures with mipLevelCount > 1. Workaround for mip level "
+      "miscomputation in PowerVR proprietary driver.",
+      "https://crbug.com/540087398", ToggleStage::Device}},
     {Toggle::WaitIsThreadSafe,
      {"wait_is_thread_safe",
       "WaitFor* functions are thread-safe and can be called without the device-lock if implicit "
diff --git a/src/dawn/native/Toggles.h b/src/dawn/native/Toggles.h
index 52115d1..dea360c 100644
--- a/src/dawn/native/Toggles.h
+++ b/src/dawn/native/Toggles.h
@@ -195,6 +195,7 @@
     VulkanSleepAfterLostDeviceWait,
     UseSpirvReconvergenceMode,
     VulkanReplaceWorkgroupAtomicStoreWithExchange,
+    VulkanDisallowNPOTDepthStencilMipmaps,
 
     // Once all backends have been updated to be thread safe for waiting, we can remove this toggle.
     WaitIsThreadSafe,
diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
index caf6fbf..a91b535 100644
--- a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
+++ b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
@@ -1073,8 +1073,12 @@
     }
 
     if (MayBeImaginationProprietary()) {
-        // crbug.com/443906252: Polyfill for case switch with large ranges.
+        // crbug.com/443906252 - Polyfill for case switch with large ranges.
         deviceToggles->Default(Toggle::VulkanPolyfillSwitchWithIf, true);
+
+        // crbug.com/540087398 - Driver bug miscomputes mip sizes for NPOT depth/stencil textures.
+        // TODO(https://crbug.com/540087398): Limit this to old drivers once there's a driver fix.
+        deviceToggles->Default(Toggle::VulkanDisallowNPOTDepthStencilMipmaps, true);
     }
 
     // AMD Mesa front end optimizer bug for unary negation and abs.
diff --git a/webgpu-cts/expectations.txt b/webgpu-cts/expectations.txt
index 11b29ed..36a28cd 100644
--- a/webgpu-cts/expectations.txt
+++ b/webgpu-cts/expectations.txt
@@ -1879,6 +1879,11 @@
 crbug.com/538691038 [ intel mac ] webgpu:shader,validation,functions,alias_analysis:two_pointers_to_struct_members_indirect:address_space="private";member="a";aliased=false [ Failure ]
 crbug.com/538691038 [ intel mac ] webgpu:shader,validation,functions,alias_analysis:two_pointers_to_struct_members_indirect:address_space="private";member="a";aliased=true [ Failure ]
 
+crbug.com/540087398 [ android-pixel-10 ] webgpu:api,validation,createTexture:mipLevelCount,format:dimension="*";format="depth*" [ Failure ]
+crbug.com/540087398 [ android-pixel-10 ] webgpu:api,validation,createTexture:mipLevelCount,format:dimension="*";format="stencil*" [ Failure ]
+crbug.com/540087398 [ android-pixel-10 ] webgpu:shader,execution,expression,call,builtin,texture_utils:createTextureWithRandomDataAndGetTexels_with_generator:format="depth*";viewDimension="*" [ Failure ]
+crbug.com/540087398 [ android-pixel-10 ] webgpu:shader,execution,expression,call,builtin,texture_utils:createTextureWithRandomDataAndGetTexels_with_generator:format="stencil*";viewDimension="*" [ Failure ]
+
 ################################################################################
 # Autogenerated Failure expectations. Please triage.
 # ##ROLLER_AUTOGENERATED_FAILURES##
diff --git a/src/dawn/tests/end2end/QueueTests.cpp b/src/dawn/tests/end2end/QueueTests.cpp
index e09c8f8..5c5476a 100644
--- a/src/dawn/tests/end2end/QueueTests.cpp
+++ b/src/dawn/tests/end2end/QueueTests.cpp
@@ -546,6 +546,9 @@
 
 // Test writing to varying mips
 TEST_P(QueueWriteTextureTests, TextureWriteToMip) {
+    // TODO(crbug.com/540087398): NPOT mipmapped depth/stencil textures disallowed due to driver bug
+    DAWN_SUPPRESS_TEST_IF(IsImgTec());
+
     constexpr uint32_t kWidth = 259;
     constexpr uint32_t kHeight = 127;
 
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/dawn/tests/end2end/QueueTests.cpp b/src/dawn/tests/end2end/QueueTests.cpp
index e09c8f8..5c5476a 100644
--- a/src/dawn/tests/end2end/QueueTests.cpp
+++ b/src/dawn/tests/end2end/QueueTests.cpp
@@ -546,6 +546,9 @@
 
 // Test writing to varying mips
 TEST_P(QueueWriteTextureTests, TextureWriteToMip) {
+    // TODO(crbug.com/540087398): NPOT mipmapped depth/stencil textures disallowed due to driver bug
+    DAWN_SUPPRESS_TEST_IF(IsImgTec());
+
     constexpr uint32_t kWidth = 259;
     constexpr uint32_t kHeight = 127;
Loading diff…

Original Bug Report

reported by [email protected]

IMG: Heap OOB write in depth/stencil image layout computation

  • Attack surface: WebGPU, reachable from an untrusted web page in the default Chrome for Android configuration.
  • Impact: GPU-memory corruption in the unsandboxed Chrome GPU process.

Android internal bug: b/528131119

View on issue tracker