CVE-2026-10020
Overview
Background
- `VulkanYCbCrInfo`
- the struct carrying Vulkan YCbCr sampler-conversion parameters, sent from the renderer to the GPU process over Mojo IPC.
- StructTraits `Read`
- the Mojo deserialization hook (
gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h) that copies wire fields into agpu::VulkanYCbCrInfo. - `suggested_ycbcr_model`
- one of the YCbCr enum-like fields; valid Vulkan model values are bounded (model ≤ 4).
Root Cause Analysis
The pre-fix Mojo Read for VulkanYCbCrInfo (gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h:45-55) copied each field directly — out->suggested_ycbcr_model = data.suggested_ycbcr_model(); and likewise suggested_ycbcr_range, suggested_xchroma_offset, suggested_ychroma_offset (:49-52) — then returned true unconditionally, performing no range validation. The fields are typed uint32_t (getters at :25-39), so a compromised renderer could place arbitrary out-of-range values (the advisory cites suggested_ycbcr_model = 0xFF) into the struct deserialized in the GPU process. The advisory describes those values later overflowing a bitfield in Skia’s Dawn backend and corrupting an adjacent chroma-filter field; that downstream bit-packing code is named by the reporter but is not part of this commit.
The fix adds, after the field copies, a bounds check against known Vulkan maximums — VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020 = 4, VK_SAMPLER_YCBCR_RANGE_ITU_NARROW = 1, VK_CHROMA_LOCATION_MIDPOINT = 1 — returning false (a deserialization failure) if suggested_ycbcr_model, suggested_ycbcr_range, suggested_xchroma_offset, or suggested_ychroma_offset exceed them. This enforces validation of the untrusted input at the IPC boundary so oversized values never reach the GPU-side consumers.
Read and rejects the message otherwise.Attack Path
- Send a malicious struct
A compromised renderer transmits a
VulkanYCbCrInfowith an out-of-rangesuggested_ycbcr_model(e.g.0xFF). - Accept without validation
Pre-fix
Readcopied the field and returnedtrue(vulkan_ycbcr_info_mojom_traits.h:49-54), so the bad value entered the GPU process. - Corrupt downstream packing The oversized value overflows a Skia/Dawn YCbCr bitfield and corrupts the chroma-filter field. (inferred — the bit-packing code is not in the shown diff.)
- Invalid Vulkan enum to driver
The corrupted descriptor yields an out-of-range
VkFilter/sampler value passed to the Vulkan driver. (inferred — the reporter’s claim.)
Impact Assessment
Files Changed
gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h
Audit Directions
- IPC enum-like fieldsValidate every renderer-supplied enum/
uint32_tfield in a MojoStructTraits::Readagainst its legal range before it crosses into a privileged process. - Defense in depth for bitfieldsWhere values are later packed into bitfields, also mask/validate at the packing site; the reporter’s suggested Skia/Dawn hardening remains separate from this IPC fix.
Patch
From 2540f0d8870b855bd58a696ccfd9c213a2e95c88 Mon Sep 17 00:00:00 2001 From: Vasiliy Telezhnikov <[email protected]> Date: Thu, 07 May 2026 13:33:56 -0700 Subject: [PATCH] Validate YCbCr info enum values Due to dependency issues, we passed uint32_t values instead of enum historically, until we can resolve this we should validate against known values. Bug: 496282591,496565479 Change-Id: I36e3429b19cd71c32e4317cad24dfb1299c3406f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7823022 Reviewed-by: Joe Mason <[email protected]> Commit-Queue: Vasiliy Telezhnikov <[email protected]> Cr-Commit-Position: refs/heads/main@{#1627197} --- diff --git a/gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h b/gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h index e349c8bd..0048aef 100644 --- a/gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h +++ b/gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h @@ -51,6 +51,22 @@ out->suggested_xchroma_offset = data.suggested_xchroma_offset(); out->suggested_ychroma_offset = data.suggested_ychroma_offset(); out->format_features = data.format_features(); + + // Values from Vulkan definitions, because we can't easy depend on vulkan + // here. + // https://source.chromium.org/chromium/chromium/src/+/main:third_party/vulkan-headers/src/include/vulkan/vulkan_core.h;drc=f6a6f7ab165cedbfa2a7d0c93fe27a2d01ce09c8;l=5438 + const uint32_t VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020 = 4; + const uint32_t VK_CHROMA_LOCATION_MIDPOINT = 1; + const uint32_t VK_SAMPLER_YCBCR_RANGE_ITU_NARROW = 1; + + if (out->suggested_ycbcr_model > + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020 || + out->suggested_ycbcr_range > VK_SAMPLER_YCBCR_RANGE_ITU_NARROW || + out->suggested_xchroma_offset > VK_CHROMA_LOCATION_MIDPOINT || + out->suggested_ychroma_offset > VK_CHROMA_LOCATION_MIDPOINT) { + return false; + } + return true; } };
Original Bug Report
Potential Sandbox Escape via Bit-Packing Overflow in Skia Graphite Dawn YCbCr Descriptor
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A compromised renderer can send an out-of-bounds VulkanYCbCrInfo.suggested_ycbcr_model value to the GPU process. This value overflows during bit-packing in Skia’s Dawn backend, corrupting the vkChromaFilter field and bypassing Dawn’s validation. This results in undefined behavior when constructing a Vulkan sampler, potentially leading to a sandbox escape in the graphics driver.
Affected files:
third_party/skia/src/gpu/graphite/dawn/DawnGraphiteUtils.cppthird_party/dawn/src/dawn/native/vulkan/UtilsVulkan.cppthird_party/dawn/src/dawn/native/Sampler.cppthird_party/skia/src/gpu/graphite/dawn/DawnSampler.cppgpu/command_buffer/service/shared_image/shared_image_format_service_utils.cc
Estimated timestamp from git blame: 2026-02-25
Summary
A bit-packing overflow vulnerability exists in Skia’s Graphite Dawn backend when processing YCbCr descriptors. A compromised renderer process can send a malicious VulkanYCbCrInfo struct via IPC to the GPU process, containing an out-of-bounds suggested_ycbcr_model value. This value is assigned to a Dawn descriptor and passed to Skia without proper validation. During serialization, Skia shifts this value into a bitfield, overflowing its intended bits and corrupting an adjacent field (vkChromaFilter). This invalid filter mode bypasses Dawn’s validation logic, leading to compiler-level undefined behavior when Dawn attempts to convert it to a Vulkan enum. The resulting out-of-bounds Vulkan enum is passed to the highly privileged Vulkan driver, potentially triggering an out-of-bounds array access and arbitrary memory corruption within the driver, constituting a sandbox escape.
Note: These are potential steps as the AI agent doesn’t yet have the ability to run a working proof of concept to verify the full exploit chain.
Root Cause Analysis
- Missing IPC Validation: The
VulkanYCbCrInfostruct is deserialized in the GPU process viagpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h. TheReadmethod copies the fields, includingsuggested_ycbcr_model, without any bounds checking. - Unvalidated Conversion: This malicious value (e.g.,
0xFF) is stored in aviz::TransferableResourceand later copied directly into thevkYCbCrModelfield of awgpu::YCbCrVkDescriptorinsidegpu/command_buffer/service/shared_image/shared_image_format_service_utils.cc:740(ToDawnYCbCrVkDescriptor). - SkASSERT Bypass: Skia receives this descriptor and attempts to validate the input in
third_party/skia/src/gpu/graphite/dawn/DawnGraphiteUtils.cpp:301:SkASSERT(desc.vkYCbCrModel < (1u << kYcbcrModelBits));. However,SkASSERTcompiles to a no-op in release builds, so the oversized0xFFvalue passes through. - Bit-Packing Overflow: The function
DawnDescriptorToImmutableSamplerInfopacks the descriptor fields into auint32_t. It shifts the oversized model value:((uint32_t)(desc.vkYCbCrModel) << kYcbcrModelShift).kYcbcrModelShiftis 1, andvkYCbCrModelis only supposed to occupy 3 bits. Shifting0xFFleft by 1 produces0x1FE, which overwrites bits 1 through 8. - Adjacent Field Corruption: The
vkChromaFilterfield occupies bits 7-8. The overflow from the model shift writes11into these bits, completely corrupting the filter mode and setting it to the invalid value3(valid values are0to2).
Exploit Path
- Reconstruction: Skia later unpacks the corrupted value from the bitfield using
DawnDescriptorFromImmutableSamplerInfo, extracting the invalidwgpu::FilterModeof3. - Validation Bypass: This invalid descriptor is passed back to Dawn to create a sampler. In
third_party/dawn/src/dawn/native/Sampler.cpp:74, Dawn validates the descriptor but crucially fails to check theycbcr->vkChromaFilterfield. - Undefined Behavior Sink: When Dawn prepares to call the Vulkan driver, it converts the
wgpu::FilterModeto aVkFilterusingToVulkanSamplerFilter(third_party/dawn/src/dawn/native/vulkan/UtilsVulkan.cpp:90). This switch statement does not have a case for3and falls through toDAWN_UNREACHABLE(), which expands to__builtin_unreachable()in release builds. - Driver Exploitation: Hitting
__builtin_unreachable()invokes compiler-level undefined behavior. The compiler will optimize the code assuming this path is never taken, typically resulting in an invalidVkFiltervalue (like2or3) being returned and assigned tovulkanYCbCrCreateInfo.chromaFilter. At line 447, Dawn passes this invalid struct to the Vulkan graphics driver viavkCreateSamplerYcbcrConversion. The driver will attempt to use this out-of-bounds enum, likely triggering an out-of-bounds array access within the highly privileged Vulkan driver, leading to a sandbox escape.
Suggested Fix
- IPC Validation: Add proper bounds checking to the
VulkanYCbCrInfodeserializer ingpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h. Ensure all enum-like fields, includingsuggested_ycbcr_model, are within their expected valid ranges. - Bit-Packing Masking: In
third_party/skia/src/gpu/graphite/dawn/DawnGraphiteUtils.cpp, updateDawnDescriptorToImmutableSamplerInfoto explicitly mask all input values before bitwise shifting them into the packed integer, preventing any out-of-bounds value from clobbering adjacent fields (e.g.,(desc.vkYCbCrModel & ((1u << kYcbcrModelBits) - 1)) << kYcbcrModelShift). - Dawn Validation: Add validation for the
ycbcr->vkChromaFilterfield within Dawn’sValidateSamplerDescriptorfunction.
Evaluated with Chrome root at commit: 0eb4855bda702feaaa8b899336664f97e3df88b8
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. Please feel free to reach out to me if you have concerns or feedback.