CVE-2026-14152
Overview
Files Changed
gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc
Patch
From 9ed0db473ba13db46bc2a067cb4b4a6a444bce7f Mon Sep 17 00:00:00 2001 From: Stacy Gaikovaia <[email protected]> Date: Thu, 28 May 2026 16:12:33 -0700 Subject: [PATCH] [viz] Add FILTER_LINEAR to shared image vk backing factory [email protected] Bug: 517534944 Change-Id: I7cb8caab9ac0278928350e4f0c13a8b644316754 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7882232 Reviewed-by: Shahbaz Youssefi <[email protected]> Commit-Queue: Stacy Gaikovaia <[email protected]> Reviewed-by: Vasiliy Telezhnikov <[email protected]> Auto-Submit: Stacy Gaikovaia <[email protected]> Cr-Commit-Position: refs/heads/main@{#1638023} --- diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc index 2185056..c6ecbb3 100644 --- a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc +++ b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc @@ -42,10 +42,14 @@ // support is required when SAMPLED_IMAGE is supported. In Vulkan 1.0 all // formats support these features implicitly. See discussion in // https://github.com/KhronosGroup/Vulkan-Docs/issues/1223 - if (feature_flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) + // FILTER_LINEAR is additionally required to match ANGLE as the + // image may be exported to it. + if ((feature_flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && + (feature_flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { usage_flags |= VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } // VUID-VkImageViewCreateInfo-usage-02652: support for INPUT_ATTACHMENT is // implied by both of COLOR_ATTACHNENT and DEPTH_STENCIL_ATTACHMENT
Original Bug Report
Potential GPU OOB memory access via R4G4B4A4 format widening in ANGLE Vulkan
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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A format validation discrepancy between Chromium and ANGLE can cause a size mismatch when importing RGBA4444 external images. On Vulkan drivers lacking linear filtering for R4G4B4A4, ANGLE falls back to R8G8B8A8, binding a larger VkImage to a smaller imported memory allocation. This can potentially result in out-of-bounds GPU heap access in release builds where size assertions are compiled out.
Affected files:
third_party/angle/src/libANGLE/renderer/vulkan/MemoryObjectVk.cppthird_party/angle/src/libANGLE/renderer/vulkan/vk_format_utils.cpp
Estimated timestamp from git blame: 2021-09-01
Potential Root Cause Analysis
There is a discrepancy in the format feature validation predicates between Chromium’s external image allocator and ANGLE’s Vulkan backend. This difference can cause a mismatch where Chromium allocates memory for a smaller, lower bpp format, while ANGLE widens the format (e.g., from 2 bpp R4G4B4A4 to 4 bpp R8G8B8A8), creating a mismatch when binding the memory to a VkImage via vkBindImageMemory.
In third_party/angle/src/libANGLE/renderer/vulkan/MemoryObjectVk.cpp (line 187), ANGLE resolves the fallback format for external memory image creation by querying getActualImageFormatID with vk::ImageFormatSupport::SampleOnly:
const vk::Format &vkFormat = renderer->getFormat(internalFormat);
const angle::FormatID intendedFormatID = vkFormat.getIntendedFormatID();
angle::FormatID actualFormatID =
vkFormat.getActualImageFormatID(vk::ImageFormatSupport::SampleOnly);
However, in third_party/angle/src/libANGLE/renderer/vulkan/vk_format_utils.cpp (line 165), the format selection logic resolves mActualSampleOnlyImageFormatID using HasNonRenderableTextureFormatSupport as the test function for filterable formats:
bool HasNonRenderableTextureFormatSupport(vk::Renderer *renderer, angle::FormatID formatID)
{
constexpr uint32_t kBitsColor =
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
constexpr uint32_t kBitsDepth = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
return renderer->hasImageFormatFeatureBits(formatID, kBitsColor) || ...;
}
This predicate strictly requires both SAMPLED_IMAGE_BIT and SAMPLED_IMAGE_FILTER_LINEAR_BIT on the format.
In contrast, Chromium’s allocator (gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc, line 45) queries the exact same optimalTilingFeatures but only requires VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT:
VkImageUsageFlags GetMaximalImageUsageFlags(VkFormatFeatureFlags feature_flags) {
VkImageUsageFlags usage_flags = 0;
if (feature_flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)
usage_flags |= VK_IMAGE_USAGE_SAMPLED_BIT | TRANSFER_SRC | TRANSFER_DST;
...
}
The Trigger Case (RGBA_4444)
Per the Vulkan specification, VK_FORMAT_R4G4B4A4_UNORM_PACK16 has zero mandatory optimal tiling features. Conforming Vulkan drivers are allowed to advertise SAMPLED_IMAGE support without supporting FILTER_LINEAR for this format. On such a driver:
- Chromium side:
VK_FORMAT_R4G4B4A4_UNORM_PACK16is used, and becauseSAMPLED_IMAGE_BITis supported, Chromium proceeds to allocate a 2 bpp external image memory backing (~32 MiB for a 4096x4096 size). - ANGLE side:
HasNonRenderableTextureFormatSupportevaluates to false becauseFILTER_LINEARis missing. The fallback format chain advances toR8G8B8A8_UNORM(4 bpp). - Binding mismatch: ANGLE creates a
VK_FORMAT_R8G8B8A8_UNORMimage requiring 4 bpp (~64 MiB for 4096x4096). It imports the 32 MiB external memory allocation withallocationSize = 64 MiBand binds it.
This violates VUID-vkBindImageMemory-size-01049. The check ensuring memory requirements match in ANGLE (ASSERT(externalMemoryRequirements.size == mSize)) is a debug-only assert and is completely compiled out in release/production builds.
Potential Impact
An attacker in control of a compromised renderer process could potentially trigger out-of-bounds (OOB) reads and writes on the GPU heap within the GPU process:
- OOB Write: By submitting
glTexSubImage2Dcommands, ANGLE performs staging uploads into the 64 MiB image space, writing past the boundaries of the 32 MiB physical backing into adjacent GPU driver allocations. - OOB Read: By rendering with the texture as a source or using
glCopyTexSubImage2Dinto a readable attachment, an attacker could potentially leak adjacent GPU-heap memory allocations.
Note that the following steps are theoretical/potential as our tooling does not currently have the capability to run code to produce a working proof-of-concept.
Suggested Steps to Trigger the Potential Vulnerability
- From a compromised renderer, send an IPC command
gpu.mojom.SharedImageInterface::CreateSharedImagewith:format = viz.mojom.SingleplanarFormat::RGBA_4444size = {4096, 4096}usage = SHARED_IMAGE_USAGE_GLES2_READ | SHARED_IMAGE_USAGE_GLES2_WRITE
- The GPU process selects
ExternalVkImageBackingFactoryand allocates a nativeVK_FORMAT_R4G4B4A4_UNORM_PACK16external-memory image (~32 MiB) and exports its opaque handle (e.g., FD). - Request GL interop by sending a command-buffer IPC calling
glCreateAndTexStorage2DSharedImageINTERNAL. The GPU process callsglImportMemoryFdEXTon the ~32 MiB FD followed byglTexStorageMemFlags2DANGLEwithGL_RGBA4and size4096, 4096. - Inside ANGLE, the
GL_RGBA4is resolved via SampleOnly fallback toR8G8B8A8_UNORM(due to the lack of linear filter support). ANGLE creates aVK_FORMAT_R8G8B8A8_UNORMimage (~64 MiB required) and binds it to the imported 32 MiB allocation. - Issue
glTexSubImage2Dwrites or texture sampling read-backs to attempt to corrupt or leak adjacent GPU heap allocations.
Suggested Fix
To prevent this discrepancy and potential OOB memory access, the debug-only assertion in third_party/angle/src/libANGLE/renderer/vulkan/MemoryObjectVk.cpp (line 261) should be converted into a runtime check that returns an error on release builds:
if (externalMemoryRequirements.size != mSize)
{
ANGLE_VK_CHECK(contextVk, false, VK_ERROR_INITIALIZATION_FAILED);
}
Evaluated with Chrome root at commit: 5133b93d189b383c37805b1cf3a9d2dbfe8d7379
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.