Medium watch ✔︎ verified CVSS 8.3 chrome Input Validation 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
8.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Skia
DescriptionInsufficient validation of untrusted input in Skia
ComponentSkia
Sub-componentInternals>Skia
Bug ClassInput Validation
Action tierwatch — ub-class only — no demonstrated corruption path — factors set by independent review by AI
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.
Independent review by AIConfirmed
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker496565479
Fix commit2540f0d8870b (chromium/src) +16/-0
CWECWE-20 (Improper input validation)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-03-26 (per vendor advisory)
Fix landed2026-05-07 (public git)
Disclosed2026-05-27
Patch gap20 days — fix public in source before the advisory

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 a gpu::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.

Key insight
The GPU process accepted renderer-supplied YCbCr enum fields with no range check; the fix validates them against their Vulkan maximums inside the Mojo Read and rejects the message otherwise.

Attack Path

  1. Send a malicious struct A compromised renderer transmits a VulkanYCbCrInfo with an out-of-range suggested_ycbcr_model (e.g. 0xFF).
  2. Accept without validation Pre-fix Read copied the field and returned true (vulkan_ycbcr_info_mojom_traits.h:49-54), so the bad value entered the GPU process.
  3. 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.)
  4. 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

The advisory records insufficient validation of untrusted input in Skia, medium severity, not in the wild. By injecting an out-of-range YCbCr value a compromised renderer could, per the reporter, drive undefined behavior when the GPU process builds a Vulkan sampler and reach the privileged graphics driver; that driver-level escalation is (inferred). The fix blocks the input at the IPC boundary.

Files Changed

  • gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h

Audit Directions

  • IPC enum-like fields
    Validate every renderer-supplied enum/uint32_t field in a Mojo StructTraits::Read against its legal range before it crosses into a privileged process.
  • Defense in depth for bitfields
    Where 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.
From 2540f0d8870b855bd58a696ccfd9c213a2e95c88 Mon Sep 17 00:00:00 2001
From: Vasiliy Telezhnikov <vasilyt@chromium.org>
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 <joenotcharles@google.com>
Commit-Queue: Vasiliy Telezhnikov <vasilyt@chromium.org>
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;
   }
 };
Loading diff…

Original Bug Report

reported by vm...@google.com

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.cpp
  • third_party/dawn/src/dawn/native/vulkan/UtilsVulkan.cpp
  • third_party/dawn/src/dawn/native/Sampler.cpp
  • third_party/skia/src/gpu/graphite/dawn/DawnSampler.cpp
  • gpu/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

  1. Missing IPC Validation: The VulkanYCbCrInfo struct is deserialized in the GPU process via gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h. The Read method copies the fields, including suggested_ycbcr_model, without any bounds checking.
  2. Unvalidated Conversion: This malicious value (e.g., 0xFF) is stored in a viz::TransferableResource and later copied directly into the vkYCbCrModel field of a wgpu::YCbCrVkDescriptor inside gpu/command_buffer/service/shared_image/shared_image_format_service_utils.cc:740 (ToDawnYCbCrVkDescriptor).
  3. 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, SkASSERT compiles to a no-op in release builds, so the oversized 0xFF value passes through.
  4. Bit-Packing Overflow: The function DawnDescriptorToImmutableSamplerInfo packs the descriptor fields into a uint32_t. It shifts the oversized model value: ((uint32_t)(desc.vkYCbCrModel) << kYcbcrModelShift). kYcbcrModelShift is 1, and vkYCbCrModel is only supposed to occupy 3 bits. Shifting 0xFF left by 1 produces 0x1FE, which overwrites bits 1 through 8.
  5. Adjacent Field Corruption: The vkChromaFilter field occupies bits 7-8. The overflow from the model shift writes 11 into these bits, completely corrupting the filter mode and setting it to the invalid value 3 (valid values are 0 to 2).

Exploit Path

  1. Reconstruction: Skia later unpacks the corrupted value from the bitfield using DawnDescriptorFromImmutableSamplerInfo, extracting the invalid wgpu::FilterMode of 3.
  2. 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 the ycbcr->vkChromaFilter field.
  3. Undefined Behavior Sink: When Dawn prepares to call the Vulkan driver, it converts the wgpu::FilterMode to a VkFilter using ToVulkanSamplerFilter (third_party/dawn/src/dawn/native/vulkan/UtilsVulkan.cpp:90). This switch statement does not have a case for 3 and falls through to DAWN_UNREACHABLE(), which expands to __builtin_unreachable() in release builds.
  4. 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 invalid VkFilter value (like 2 or 3) being returned and assigned to vulkanYCbCrCreateInfo.chromaFilter. At line 447, Dawn passes this invalid struct to the Vulkan graphics driver via vkCreateSamplerYcbcrConversion. 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

  1. IPC Validation: Add proper bounds checking to the VulkanYCbCrInfo deserializer in gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h. Ensure all enum-like fields, including suggested_ycbcr_model, are within their expected valid ranges.
  2. Bit-Packing Masking: In third_party/skia/src/gpu/graphite/dawn/DawnGraphiteUtils.cpp, update DawnDescriptorToImmutableSamplerInfo to 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).
  3. Dawn Validation: Add validation for the ycbcr->vkChromaFilter field within Dawn’s ValidateSamplerDescriptor function.

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.

View on issue tracker