CVE-2026-2315
Overview
Background
- Workgroup memory zero-initialization
- WGSL requires
workgroup(andfunction/private) variables to start zeroed, so Dawn must guarantee that shader-visible memory never exposes stale contents from earlier GPU work. - `VK_KHR_zero_initialize_workgroup_memory`
- a Vulkan extension (
DeviceExt::ZeroInitializeWorkgroupMemory) that lets the driver perform this zeroing, instead of Dawn/Tint emitting explicit initialization code. - `Toggle::VulkanUseZeroInitializeWorkgroupMemoryExtension`
- the Dawn device toggle that selects whether to rely on the driver extension; when force-set to
false, Dawn falls back to its own initialization (the context comment mentionsOpConstantNull). - `IsPixel10()` / `IsAndroidARM()`
- Dawn device-identification predicates used to apply per-device driver workarounds.
Root Cause Analysis
In src/dawn/native/vulkan/PhysicalDeviceVk.cpp (the enclosing function is not named in the hunk), Dawn decides whether to trust the driver’s VK_KHR_zero_initialize_workgroup_memory support; it already refused to use it when the extension is missing, when shaderZeroInitializeWorkgroupMemory == VK_FALSE, or on IsAndroidARM() devices because of a known Mali bug. On Pixel 10 the extension is advertised and enabled, but the added comment states that its “workgroup zero init does not always work as expected”, so shaders could observe non-zero initial values where WGSL guarantees zero. The removed webgpu-cts/expectations.txt lines confirm the symptom: webgpu:shader,execution,zero_init:compute,zero_init cases for addressSpace="function" and addressSpace="private" were failing on android-pixel-10.
The fix tightens the condition by adding || IsPixel10(), so the toggle is ForceSet(..., false) on Pixel 10 and Dawn uses its own zero-initialization path instead of trusting the driver; with that change the CTS failure expectations could be deleted.
Attack Path
- Run on an affected device
The victim uses Chrome with WebGPU on a Pixel 10, where the Vulkan backend previously enabled
VulkanUseZeroInitializeWorkgroupMemoryExtension. - Submit a compute shader A web page creates a WebGPU compute pipeline whose shader declares variables it reads before writing, relying on WGSL’s zero-initialization guarantee.
- Read non-zero initial values
Because driver zero-init “does not always work as expected”, the shader observes values that should have been zero, as shown by the failing
zero_initCTS cases. - Exfiltrate stale GPU data The page copies those values into a buffer and reads them back; if the stale contents originate from other GPU work (other origins, the compositor, other apps), this would be a cross-origin information leak (inferred — the patch does not show where the stale data comes from).
Impact Assessment
Files Changed
src/dawn/native/vulkan/PhysicalDeviceVk.cppwebgpu-cts/expectations.txt
Audit Directions
- Driver-feature trust listsReview other places where Dawn enables a toggle purely because a Vulkan/Metal/D3D feature bit is advertised (robustness, zero-init, robust buffer access) and check per-vendor/per-device CTS failure expectations for hints of broken drivers.
- CTS expectations as security signals
webgpu-cts/expectations.txtentries forzero_init,robust_accessor out-of-bounds tests on a specific device may indicate unenforced security invariants, not just correctness gaps. - All address spacesConfirm that the fallback initialization covers
workgroup,functionandprivatevariables, since the removed failures spanfunctionandprivateaddress spaces as well.
Patch
From 4c7d0f5ecc872898bd236e423fbd2be5f8666992 Mon Sep 17 00:00:00 2001 From: Peter McNeeley <[email protected]> Date: Wed, 28 Jan 2026 06:05:12 -0800 Subject: [PATCH] [dawn] Disable extension workgroup zero init on pixel 10 Bug: 479242793 Change-Id: Ie53495eb7d2b9939abb3a17691e49a9f6369b9be Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/287195 Reviewed-by: dan sinclair <[email protected]> Reviewed-by: Loko Kung <[email protected]> Commit-Queue: Peter McNeeley <[email protected]> --- diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp index c9fa553..f925819 100644 --- a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp +++ b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp @@ -1089,10 +1089,11 @@ // extension is available. Override the decision if it is not applicable or // zeroInitializeWorkgroupMemoryFeatures.shaderZeroInitializeWorkgroupMemory == VK_FALSE. // Never use the extension on Mali devices due to a known bug (see crbug.com/tint/2101). + // Pixel 10 workgroup zero init does not always work as expected (see crbug.com/479242793). if (!GetDeviceInfo().HasExt(DeviceExt::ZeroInitializeWorkgroupMemory) || GetDeviceInfo().zeroInitializeWorkgroupMemoryFeatures.shaderZeroInitializeWorkgroupMemory == VK_FALSE || - IsAndroidARM()) { + IsAndroidARM() || IsPixel10()) { deviceToggles->ForceSet(Toggle::VulkanUseZeroInitializeWorkgroupMemoryExtension, false); } // By default try to initialize workgroup memory with OpConstantNull according to the Vulkan diff --git a/webgpu-cts/expectations.txt b/webgpu-cts/expectations.txt index 9ed6a1a..5419551 100644 --- a/webgpu-cts/expectations.txt +++ b/webgpu-cts/expectations.txt @@ -1716,30 +1716,6 @@ crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,statement,discard:three_quarters:useStorageBuffers=true [ Failure ] crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,statement,discard:uniform_read_loop:useStorageBuffers=true [ Failure ] crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,statement,phony:executes:case="loop" [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="function";workgroupSize=[1,1,1];batch__=18 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="function";workgroupSize=[1,1,1];batch__=19 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="function";workgroupSize=[1,1,1];batch__=21 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="function";workgroupSize=[1,1,1];batch__=22 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="function";workgroupSize=[1,1,1];batch__=23 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="function";workgroupSize=[1,1,1];batch__=24 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="function";workgroupSize=[1,1,1];batch__=25 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="function";workgroupSize=[1,1,1];batch__=26 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="function";workgroupSize=[1,1,1];batch__=28 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="function";workgroupSize=[1,1,1];batch__=29 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=15 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=16 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=17 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=19 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=20 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=21 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=22 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=23 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=24 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=25 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=26 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=27 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=28 [ Failure ] -crbug.com/0000 [ android-pixel-10 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=29 [ Failure ] crbug.com/0000 [ android-pixel-10 ] webgpu:shader,validation,expression,call,builtin,textureSampleBaseClampToEdge:* [ Failure ] crbug.com/0000 [ android-pixel-10 ] webgpu:shader,validation,expression,call,builtin,textureSampleBias:* [ Failure ] crbug.com/0000 [ android-pixel-10 ] webgpu:shader,validation,expression,call,builtin,textureSampleCompare:* [ Failure ]
Original Bug Report
Use dawn workgroup zero init fallback for pixel 10