CVE-2026-11052
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fgpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc |
modified |
Files Changed
gpu/command_buffer/service/shared_image/compound_image_backing.ccgpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
Patch
From 1f7736e2f3be118a7732bb78416e24b2f1ad785d Mon Sep 17 00:00:00 2001 From: vikas soni <[email protected]> Date: Tue, 07 Apr 2026 14:19:09 -0700 Subject: [PATCH] [GPU] Guard against type confusion in CompoundImageBacking. When kUseCompoundImageBackingAsDefault is on, WrapExternalBacking wraps arbitrary backing types and gives them AccessStreamSet::All() (including kMemory). GetShmElement() then returns that wrapped backing regardless of its actual type. This CL adds a CHECK to guard against potential type confusion in GetSharedMemoryPixmaps(). Bug: 498834967 Change-Id: I43c8a306d154d8569240f5c2a88aa006b25bd928 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7728969 Reviewed-by: Vasiliy Telezhnikov <[email protected]> Commit-Queue: Vikas Soni <[email protected]> Cr-Commit-Position: refs/heads/main@{#1610988} --- diff --git a/gpu/command_buffer/service/shared_image/compound_image_backing.cc b/gpu/command_buffer/service/shared_image/compound_image_backing.cc index fce45e5..abea87d 100644 --- a/gpu/command_buffer/service/shared_image/compound_image_backing.cc +++ b/gpu/command_buffer/service/shared_image/compound_image_backing.cc @@ -1602,6 +1602,15 @@ auto* shm_backing = GetShmElement().GetBacking(); DCHECK(shm_backing); + // SECURITY: When kUseCompoundImageBackingAsDefault is on, WrapExternalBacking + // wraps arbitrary backing types and gives them AccessStreamSet::All() + // (including kMemory). GetShmElement() then returns that wrapped backing + // here regardless of its actual type. Guard against the resulting type + // confusion in the static_cast below. Note marking a backing to support all + // access stream is expected behavior wheres ::GetSharedMemoryPixmaps should + // only be invoked on SharedImageBackingType::kSharedMemory currently. + CHECK_EQ(shm_backing->GetType(), SharedImageBackingType::kSharedMemory); + return static_cast<SharedMemoryImageBacking*>(shm_backing)->pixmaps(); } diff --git a/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc b/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc index 4b91671..c0edd97 100644 --- a/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc +++ b/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc @@ -161,6 +161,23 @@ return false; } + // Construct a CompoundImageBacking via the WrapExternalBacking constructor + // (private). This mirrors CompoundImageBacking::WrapExternalBacking exactly, + // minus the SharedImageFactory consultation. + std::unique_ptr<CompoundImageBacking> WrapExternal( + std::unique_ptr<SharedImageBacking> backing) { + backing->SetNotRefCounted(); + return std::unique_ptr<CompoundImageBacking>(new CompoundImageBacking( + /*is_thread_safe=*/false, + /*buffer_usage=*/std::nullopt, std::move(backing), copy_manager_, + /*shared_image_factory=*/base::WeakPtr<SharedImageFactory>())); + } + + const std::vector<SkPixmap>& CallGetSharedMemoryPixmaps( + CompoundImageBacking* backing) { + return backing->GetSharedMemoryPixmaps(); + } + // Create a compound backing containing shared memory + GPU backing. std::unique_ptr<SharedImageBacking> CreateCompoundBacking( SharedImageUsageSet usage) { @@ -442,6 +459,24 @@ EXPECT_FALSE(HasGpuCreateBackingCallback(compound_backing)); } +TEST_F(CompoundImageBackingTest, + GetSharedMemoryPixmaps_ChecksOnWrongBackingType) { + auto tiny = std::make_unique<TestImageBacking>( + Mailbox::Generate(), viz::SinglePlaneFormat::kRGBA_8888, + gfx::Size(10, 10), gfx::ColorSpace(), kTopLeft_GrSurfaceOrigin, + kOpaque_SkAlphaType, + SharedImageUsageSet({SHARED_IMAGE_USAGE_DISPLAY_READ}), kTestBackingSize); + + // WrapExternalBacking constructor sets elements_[0].access_streams = + // AccessStreamSet::All(), which includes kMemory. GetSharedMemoryPixmaps() + // then performs an unchecked static_cast to SharedMemoryImageBacking*. + auto compound = WrapExternal(std::move(tiny)); + + // Verify that the security fix correctly triggers a CHECK failure when + // the backing is not of type SharedMemoryImageBacking. + EXPECT_DEATH(CallGetSharedMemoryPixmaps(compound.get()), ""); +} + TEST_F(CompoundImageBackingTest, Multiplanar) { auto backing = CreateMultiplanarCompoundBacking(); auto* compound_backing = static_cast<CompoundImageBacking*>(backing.get());
Regression Test / PoC
diff --git a/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc b/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
index 4b91671..c0edd97 100644
--- a/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
@@ -161,6 +161,23 @@
return false;
}
+ // Construct a CompoundImageBacking via the WrapExternalBacking constructor
+ // (private). This mirrors CompoundImageBacking::WrapExternalBacking exactly,
+ // minus the SharedImageFactory consultation.
+ std::unique_ptr<CompoundImageBacking> WrapExternal(
+ std::unique_ptr<SharedImageBacking> backing) {
+ backing->SetNotRefCounted();
+ return std::unique_ptr<CompoundImageBacking>(new CompoundImageBacking(
+ /*is_thread_safe=*/false,
+ /*buffer_usage=*/std::nullopt, std::move(backing), copy_manager_,
+ /*shared_image_factory=*/base::WeakPtr<SharedImageFactory>()));
+ }
+
+ const std::vector<SkPixmap>& CallGetSharedMemoryPixmaps(
+ CompoundImageBacking* backing) {
+ return backing->GetSharedMemoryPixmaps();
+ }
+
// Create a compound backing containing shared memory + GPU backing.
std::unique_ptr<SharedImageBacking> CreateCompoundBacking(
SharedImageUsageSet usage) {
@@ -442,6 +459,24 @@
EXPECT_FALSE(HasGpuCreateBackingCallback(compound_backing));
}
+TEST_F(CompoundImageBackingTest,
+ GetSharedMemoryPixmaps_ChecksOnWrongBackingType) {
+ auto tiny = std::make_unique<TestImageBacking>(
+ Mailbox::Generate(), viz::SinglePlaneFormat::kRGBA_8888,
+ gfx::Size(10, 10), gfx::ColorSpace(), kTopLeft_GrSurfaceOrigin,
+ kOpaque_SkAlphaType,
+ SharedImageUsageSet({SHARED_IMAGE_USAGE_DISPLAY_READ}), kTestBackingSize);
+
+ // WrapExternalBacking constructor sets elements_[0].access_streams =
+ // AccessStreamSet::All(), which includes kMemory. GetSharedMemoryPixmaps()
+ // then performs an unchecked static_cast to SharedMemoryImageBacking*.
+ auto compound = WrapExternal(std::move(tiny));
+
+ // Verify that the security fix correctly triggers a CHECK failure when
+ // the backing is not of type SharedMemoryImageBacking.
+ EXPECT_DEATH(CallGetSharedMemoryPixmaps(compound.get()), "");
+}
+
TEST_F(CompoundImageBackingTest, Multiplanar) {
auto backing = CreateMultiplanarCompoundBacking();
auto* compound_backing = static_cast<CompoundImageBacking*>(backing.get());
Original Bug Report
Potential type confusion in CompoundImageBacking::GetSharedMemoryPixmaps
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 security team.
Overview: When CompoundImageBacking wraps a non-SHM backing, it incorrectly flags the backing as supporting kMemory. A subsequent call to GetSharedMemoryPixmaps results in an unchecked static_cast to SharedMemoryImageBacking*, causing an out-of-bounds read that can be leveraged for an arbitrary memory write in the GPU process.
Affected files:
gpu/command_buffer/service/shared_image/compound_image_backing.ccgpu/command_buffer/service/shared_image/shared_memory_image_backing.hgpu/command_buffer/service/shared_image/wrapped_graphite_texture_backing.cc
Estimated timestamp from git blame: 2026-02-26
Summary
A potential type confusion vulnerability exists in CompoundImageBacking::GetSharedMemoryPixmaps due to incorrect initialization in the WrapExternalBacking path. When a non-SharedMemoryImageBacking (such as a WrappedGraphiteTextureBacking) is wrapped, it is incorrectly marked as supporting SharedImageAccessStream::kMemory. Subsequent calls to GetSharedMemoryPixmaps perform an unchecked static_cast to SharedMemoryImageBacking*, leading to an out-of-bounds read. By grooming the GPU process heap, a compromised renderer could exploit this out-of-bounds read to forge a std::vector<SkPixmap> object and achieve an arbitrary memory write in the GPU process.
Vulnerability Details
-
Incorrect Initialization: In
gpu/command_buffer/service/shared_image/compound_image_backing.cc, theCompoundImageBackingconstructor used byWrapExternalBacking(lines 947-997) initializes the wrapped backing’s access streams withelement.access_streams = AccessStreamSet::All()(line 982). This incorrectly includesSharedImageAccessStream::kMemory, regardless of the actual backing type. -
Type Confusion: The method
GetShmElement()returns the first element whoseaccess_streamscontainskMemory. Due to the incorrect initialization, this returns the wrapped non-SHM backing. The methodGetSharedMemoryPixmaps()then performs astatic_cast<SharedMemoryImageBacking*>on this element (line 1605) and calls.pixmaps(). -
Out-of-Bounds Read: Because
SharedMemoryImageBackingis larger than a backing likeWrappedGraphiteTextureBacking(by roughly 120-180 bytes depending on the platform), the access to thepixmaps_member at the end of the class layout results in reading memory past the end of the actual object allocation.
Potential Exploitation Scenario
The following is a theoretical exploitation path, as we have not executed a full proof-of-concept:
- Heap Grooming: A compromised renderer process grooms the GPU process heap to place a fake
std::vector<SkPixmap>structure immediately adjacent to where a newWrappedGraphiteTextureBackingwill be allocated. This fake vector points to fakeSkPixmapobjects with attacker-controlled destination addresses in theirfPixels(writable_addr()) members. - Backing Creation: The renderer requests a non-SHM SharedImage. The GPU process allocates a
WrappedGraphiteTextureBacking, which is then wrapped byCompoundImageBacking::WrapExternalBacking(due to thekUseCompoundImageBackingAsDefaultfeature). - State Invalidation: The renderer accesses the SharedImage via a stream (e.g.,
kGL) that triggers a dynamic backing allocation (due to thekUseDynamicBackingAllocationsfeature). Writing to this new backing makes the original “SHM” element stale. - Trigger IPC: The renderer calls the Windows-specific Mojo IPC
GpuChannel::CopyToGpuMemoryBufferAsync. - Arbitrary Write:
CompoundImageBacking::CopyToGpuMemoryBufferAsyncdetects the stale SHM element, retrieves the new GPU backing, and callsGetSharedMemoryPixmaps(). This triggers the type confusion and out-of-bounds read, returning a reference to the attacker’s fakestd::vector. This reference is passed togpu_backing->ReadbackToMemoryAsync(). The underlying implementation (e.g.,GLTextureImageBacking::ReadbackToMemory) iterates over the fake vector and executes a readback (likeglReadPixels) to the attacker-specified memory addresses, resulting in a controlled memory write in the GPU process.
Note: MiraclePtr (BRP) does not mitigate this issue, as the vulnerability relies on an unchecked static_cast causing a deterministic memory offset read, rather than out-of-bounds indexing on a protected raw_ptr.
Suggested Fix
The initialization in CompoundImageBacking should be updated to ensure kMemory is not blindly added to access_streams for backings that do not genuinely support it. Additionally, GetSharedMemoryPixmaps() could be fortified by replacing the static_cast with a type check or a dynamic cast to ensure the underlying backing is indeed a SharedMemoryImageBacking before accessing its members.
Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33
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. And please feel free to reach out to me directly if you have concerns or feedback on the project.