CVE-2026-13934
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fsrc/dawn/tests/unittests/validation/YCbCrValidationTests.cpp |
modified |
Files Changed
src/dawn/native/Sampler.cppsrc/dawn/native/vulkan/SamplerVk.cppsrc/dawn/tests/unittests/validation/YCbCrValidationTests.cpp
Patch
From 43055cbeadddca41a9c973a809f859d641077b2f Mon Sep 17 00:00:00 2001
From: Kyle Charbonneau <kylechar@google.com>
Date: Wed, 27 May 2026 00:16:26 -0700
Subject: [PATCH] Add YCbCr sampler validation
VUID-VkSamplerCreateInfo-addressModeU-01646 enforces constraints on
address mode and anisotropy being disabled for YCbCr samplers. Enforce
those constraints in dawn validation and when creating VkSampler.
Bug: 513006636
Change-Id: I608a29e7e468bc06baae3fed45192bc5ad9144b0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/309015
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Brandon Jones <bajones@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
---
diff --git a/src/dawn/native/Sampler.cpp b/src/dawn/native/Sampler.cpp
index e9509e4..706ef42 100644
--- a/src/dawn/native/Sampler.cpp
+++ b/src/dawn/native/Sampler.cpp
@@ -77,6 +77,17 @@
DAWN_INVALID_IF(ycbcr->externalFormat == 0 && ycbcr->vkFormat == 0,
"Both VkFormat and VkExternalFormatANDROID are undefined.");
+
+ DAWN_INVALID_IF(descriptor->addressModeU != wgpu::AddressMode::ClampToEdge,
+ "addressModeU must be ClampToEdge for YCbCr samplers.");
+ DAWN_INVALID_IF(descriptor->addressModeV != wgpu::AddressMode::ClampToEdge,
+ "addressModeV must be ClampToEdge for YCbCr samplers.");
+ DAWN_INVALID_IF(descriptor->addressModeW != wgpu::AddressMode::ClampToEdge,
+ "addressModeW must be ClampToEdge for YCbCr samplers.");
+
+ DAWN_INVALID_IF(descriptor->maxAnisotropy > 1,
+ "maxAnisotropy (%d) must be 1 for YCbCr samplers.",
+ descriptor->maxAnisotropy);
}
return {};
diff --git a/src/dawn/native/vulkan/SamplerVk.cpp b/src/dawn/native/vulkan/SamplerVk.cpp
index 11a97d4..439a41f 100644
--- a/src/dawn/native/vulkan/SamplerVk.cpp
+++ b/src/dawn/native/vulkan/SamplerVk.cpp
@@ -144,6 +144,13 @@
samplerYCbCrInfo.conversion = mSamplerYCbCrConversion;
createInfo.pNext = &samplerYCbCrInfo;
+
+ // VUID-VkSamplerCreateInfo-addressModeU-01646 requires CLAMP_TO_EDGE on every axis and
+ // anisotropy disabled when VkSamplerYcbcrConversionInfo is provided.
+ createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ createInfo.anisotropyEnable = VK_FALSE;
}
DAWN_TRY(CheckVkSuccess(
diff --git a/src/dawn/tests/unittests/validation/YCbCrValidationTests.cpp b/src/dawn/tests/unittests/validation/YCbCrValidationTests.cpp
index ab47124..58075fe 100644
--- a/src/dawn/tests/unittests/validation/YCbCrValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/YCbCrValidationTests.cpp
@@ -127,6 +127,41 @@
device.CreateSampler(&samplerDesc);
}
+// Test that creating a YCbCr sampler with address mode or max anisotropy set to something invalid
+// fails.
+TEST_F(YCbCrVulkanSamplersTest, YCbCrSamplerRequiredParams) {
+ wgpu::SamplerDescriptor samplerDesc = {};
+ wgpu::YCbCrVkDescriptor yCbCrDesc = {};
+ yCbCrDesc.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ samplerDesc.nextInChain = &yCbCrDesc;
+
+ // This should work.
+ device.CreateSampler(&samplerDesc);
+
+ {
+ wgpu::SamplerDescriptor invalidSamplerDesc = samplerDesc;
+ invalidSamplerDesc.addressModeU = wgpu::AddressMode::Repeat;
+ ASSERT_DEVICE_ERROR(device.CreateSampler(&invalidSamplerDesc));
+ }
+
+ {
+ wgpu::SamplerDescriptor invalidSamplerDesc = samplerDesc;
+ invalidSamplerDesc.addressModeV = wgpu::AddressMode::Repeat;
+ ASSERT_DEVICE_ERROR(device.CreateSampler(&invalidSamplerDesc));
+ }
+
+ {
+ wgpu::SamplerDescriptor invalidSamplerDesc = samplerDesc;
+ invalidSamplerDesc.addressModeW = wgpu::AddressMode::Repeat;
+ ASSERT_DEVICE_ERROR(device.CreateSampler(&invalidSamplerDesc));
+ }
+ {
+ wgpu::SamplerDescriptor invalidSamplerDesc = samplerDesc;
+ invalidSamplerDesc.maxAnisotropy = 2;
+ ASSERT_DEVICE_ERROR(device.CreateSampler(&invalidSamplerDesc));
+ }
+}
+
// Test that only OpaqueYCbCrAndroid textures can be used to create YCbCr views.
TEST_F(YCbCrVulkanSamplersTest, YCbCrTextureViewRequiresOpaqueYCbCrAndroid) {
wgpu::TextureViewDescriptor viewDesc{};
Regression Test / PoC
diff --git a/src/dawn/tests/unittests/validation/YCbCrValidationTests.cpp b/src/dawn/tests/unittests/validation/YCbCrValidationTests.cpp
index ab47124..58075fe 100644
--- a/src/dawn/tests/unittests/validation/YCbCrValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/YCbCrValidationTests.cpp
@@ -127,6 +127,41 @@
device.CreateSampler(&samplerDesc);
}
+// Test that creating a YCbCr sampler with address mode or max anisotropy set to something invalid
+// fails.
+TEST_F(YCbCrVulkanSamplersTest, YCbCrSamplerRequiredParams) {
+ wgpu::SamplerDescriptor samplerDesc = {};
+ wgpu::YCbCrVkDescriptor yCbCrDesc = {};
+ yCbCrDesc.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ samplerDesc.nextInChain = &yCbCrDesc;
+
+ // This should work.
+ device.CreateSampler(&samplerDesc);
+
+ {
+ wgpu::SamplerDescriptor invalidSamplerDesc = samplerDesc;
+ invalidSamplerDesc.addressModeU = wgpu::AddressMode::Repeat;
+ ASSERT_DEVICE_ERROR(device.CreateSampler(&invalidSamplerDesc));
+ }
+
+ {
+ wgpu::SamplerDescriptor invalidSamplerDesc = samplerDesc;
+ invalidSamplerDesc.addressModeV = wgpu::AddressMode::Repeat;
+ ASSERT_DEVICE_ERROR(device.CreateSampler(&invalidSamplerDesc));
+ }
+
+ {
+ wgpu::SamplerDescriptor invalidSamplerDesc = samplerDesc;
+ invalidSamplerDesc.addressModeW = wgpu::AddressMode::Repeat;
+ ASSERT_DEVICE_ERROR(device.CreateSampler(&invalidSamplerDesc));
+ }
+ {
+ wgpu::SamplerDescriptor invalidSamplerDesc = samplerDesc;
+ invalidSamplerDesc.maxAnisotropy = 2;
+ ASSERT_DEVICE_ERROR(device.CreateSampler(&invalidSamplerDesc));
+ }
+}
+
// Test that only OpaqueYCbCrAndroid textures can be used to create YCbCr views.
TEST_F(YCbCrVulkanSamplersTest, YCbCrTextureViewRequiresOpaqueYCbCrAndroid) {
wgpu::TextureViewDescriptor viewDesc{};
Original Bug Report
Potential VUID-01646 violation in Graphite-Dawn-Vulkan on Android leads to GPU process UB
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: The Graphite-Dawn-Vulkan path on Android fails to enforce the Vulkan requirement that samplers using YCbCr conversion must have their address modes set to CLAMP_TO_EDGE. A compromised renderer can trigger this by applying REPEAT tiling to a YCbCr image, leading to an invalid vkCreateSampler call. Since the GPU process is unsandboxed on Android, this driver-level undefined behavior could facilitate a sandbox escape.
Affected files:
third_party/dawn/src/dawn/native/vulkan/SamplerVk.cppthird_party/dawn/src/dawn/native/Sampler.cppthird_party/skia/src/gpu/graphite/dawn/DawnSampler.cpp
Estimated timestamp from git blame: Unknown (Google3 checkout)
Summary
A potential vulnerability exists in the Graphite-Dawn-Vulkan path on Android where the system fails to enforce Vulkan’s VUID-01646 requirement. The Vulkan specification states that if a sampler YCbCr conversion is enabled, the sampler’s address modes (addressModeU, addressModeV, and addressModeW) must be set to VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE.
Currently, multiple layers (Skia Graphite, Dawn Frontend, and Dawn Vulkan Backend) omit the necessary validation or overrides to ensure this constraint is met. A compromised renderer can exploit this to trigger driver-level undefined behavior in the unsandboxed GPU process.
Technical Analysis
1. Root Cause in Skia Graphite
In third_party/skia/src/gpu/graphite/KeyHelpers.cpp, the function can_do_tiling_in_hw (line 536) determines if tiling can be handled by the hardware sampler. It currently only checks if the subset contains the full image and if decal substitution is required. It fails to check if the image requires an immutable YCbCr sampler. Consequently, ImageShaderBlock::AddBlock (line 567) may create a SamplerDesc with SkTileMode::kRepeat even when YCbCr info is present.
2. Translation in Skia Dawn Backend
In third_party/skia/src/gpu/graphite/dawn/DawnSampler.cpp, DawnSampler::Make (line 73) translates the SamplerDesc into a wgpu::SamplerDescriptor. If kRepeat was chosen, it is translated to wgpu::AddressMode::Repeat (line 77), and the YCbCr info is attached via a wgpu::YCbCrVkDescriptor (line 96).
3. Missing Validation in Dawn
Dawn’s frontend validation in third_party/dawn/src/dawn/native/Sampler.cpp:ValidateSamplerDescriptor (line 40) does not verify that AddressMode is compatible with the YCbCrVkDescriptor. This allows the invalid descriptor to reach the Vulkan backend.
4. Backend Execution
In third_party/dawn/src/dawn/native/vulkan/SamplerVk.cpp, Sampler::Initialize (line 100) populates VkSamplerCreateInfo with VK_SAMPLER_ADDRESS_MODE_REPEAT and attaches the VkSamplerYcbcrConversionInfo. The subsequent call to vkCreateSampler (line 150) violates VUID-01646.
Impact and Reachability
This issue is reachable from a compromised renderer on Android. An attacker can:
- Obtain a
SharedImagemailbox referencing a YCbCr format (e.g., from a video element). - Construct a
cc::DrawRectOpusing aPaintShaderwithSkTileMode::kRepeatreferencing that mailbox. - Submit the command to the GPU process.
On Android, the GPU process is unsandboxed by default (confirmed in content/gpu/gpu_main.cc and sandbox/policy/features.cc). Driver-level undefined behavior resulting from a VUID violation can lead to memory corruption or cross-origin information leaks, providing a direct path for a sandbox escape from a compromised renderer to a high-privileged process.
Suggested Fix
-
Skia Graphite: Update
can_do_tiling_in_hwinthird_party/skia/src/gpu/graphite/KeyHelpers.cppto returnfalseif the image has immutable YCbCr info and the tile mode is notkClamp. This will force Skia to handle tiling in the shader instead of the hardware sampler. -
Dawn Frontend: Add validation in
third_party/dawn/src/dawn/native/Sampler.cpp:ValidateSamplerDescriptorto ensure that if aYCbCrVkDescriptoris present, the address modesu,v, andwmust bewgpu::AddressMode::ClampToEdge.
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.