CVE-2026-12028
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
OverlayImagegpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory.cc |
modified | |
ScopedHardwareBufferFenceSyncImplgpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory.cc |
modified |
Files Changed
gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory.cc
Patch
From 53fa357939c2c317e209ff2f9d9c89cb1ff25a99 Mon Sep 17 00:00:00 2001 From: Sunny Sachanandani <[email protected]> Date: Wed, 03 Jun 2026 07:35:32 -0700 Subject: [PATCH] [gpu] Make OverlayImage RefCountedThreadSafe Make OverlayImage RefCountedThreadSafe to prevent a potential double- free vulnerability during SkiaOutputDeviceBufferQueue teardown under DrDc on Android. OverlayImage is accessed concurrently on both the GPU main thread (via AHardwareBufferImageBacking) and the compositor thread (via the surface control queue). During teardown, concurrent releases of the same image can result in a double-free. Thread-safe ref-counting properly synchronizes the decrements and deletion. Bug: 517555461 Test: gpu_unittests Change-Id: I5d3a7e66c743b89f79e8acce8b1ce5dd6a6a6964 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7897338 Auto-Submit: Sunny Sachanandani <[email protected]> Commit-Queue: Vasiliy Telezhnikov <[email protected]> Reviewed-by: Vasiliy Telezhnikov <[email protected]> Cr-Commit-Position: refs/heads/main@{#1640923} --- diff --git a/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory.cc index 902e600..0aab3e6 100644 --- a/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory.cc +++ b/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory.cc @@ -21,6 +21,7 @@ #include "base/debug/dump_without_crashing.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_refptr.h" #include "base/posix/eintr_wrapper.h" #include "base/strings/string_number_conversions.h" @@ -69,7 +70,7 @@ namespace gpu { namespace { -class OverlayImage final : public base::RefCounted<OverlayImage> { +class OverlayImage final : public base::RefCountedThreadSafe<OverlayImage> { public: explicit OverlayImage(AHardwareBuffer* buffer) : handle_(base::android::ScopedHardwareBufferHandle::Create(buffer)) {} @@ -88,7 +89,7 @@ } private: - friend class base::RefCounted<OverlayImage>; + friend class base::RefCountedThreadSafe<OverlayImage>; class ScopedHardwareBufferFenceSyncImpl : public base::android::ScopedHardwareBufferFenceSync {
Original Bug Report
Potential non-atomic RefCounted<OverlayImage> race condition in SkiaOutputDeviceBufferQueue teardown
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 potential race condition exists in the unsandboxed Android GPU process due to the non-thread-safe reference counting of OverlayImage. During SkiaOutputDeviceBufferQueue teardown under DrDc, references to OverlayImage are released concurrently on both the CompositorGpuThread and the GPU main thread without synchronization. This can result in a concurrent delete-this and subsequent double-free of OverlayImage, causing memory corruption.
Affected files:
gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory.cccomponents/viz/service/display_embedder/skia_output_device_buffer_queue.hcomponents/viz/service/display_embedder/skia_output_device_buffer_queue.ccui/gl/gl_surface_egl_surface_control.cc
Estimated timestamp from git blame: 2022-10-28
Root Cause Analysis
In gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory.cc (at line 71), the helper class OverlayImage is declared as inheriting from the non-thread-safe base class base::RefCounted:
class OverlayImage final : public base::RefCounted<OverlayImage> {
Because base::RefCounted is not thread-safe, its reference count decrement and zero-check in Release() are non-atomic and not synchronized across threads.
When Dual Raster Dual Compositor (DrDc) is enabled (which is the default on Android), two separate threads hold references to the exact same OverlayImage instance:
- GPU Main Thread:
AHardwareBufferImageBackingholds ascoped_refptr<OverlayImage>in itsoverlay_image_member variable (line 423). - CompositorGpuThread: The display compositor’s surface control queue holds a
std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>(which wrapsOverlayImageinsideScopedHardwareBufferFenceSyncImpl::image_at line 112) inGLSurfaceEGLSurfaceControl’s frame resource lists (e.g.,current_frame_resources_orpending_transaction_acks_).
Potential Tear-down Race Condition
During the destruction of SkiaOutputDeviceBufferQueue on the CompositorGpuThread, its member variables are destroyed in the reverse order of their declaration in skia_output_device_buffer_queue.h:
std::unique_ptr<OutputPresenter> presenter_; // line 93 (declared first)
...
std::unordered_set<OverlayData, ...> overlays_; // line 123 (declared last)
Because overlays_ is declared after presenter_, it is destroyed first during teardown:
- On the CompositorGpuThread,
overlays_is cleared, destroyingOverlayAHBImageRepresentationand dropping the representation reference on the backing viaSharedImageManager::OnRepresentationDestroyed(). - Concurrently, a compromised renderer sends a Mojo request
mojom::DeferredRequest{DestroySharedImage}, which is processed on the GPU main thread and drops the final factory reference on the backing. This triggers the destruction ofAHardwareBufferImageBackingon the GPU main thread. - Concurrent Release:
- On the GPU Main Thread,
~AHardwareBufferImageBackingis executed. During the class member destruction phase (which runs after the destructor body completes and is therefore not protected by any internal backing lock),overlay_image_is destroyed, callingRelease()onOverlayImage. - Simultaneously, on the CompositorGpuThread, the teardown proceeds to destroy
presenter_(which ownsGLSurfaceEGLSurfaceControl). The destruction ofcurrent_frame_resources_releases theScopedHardwareBufferFenceSyncImplreference, calling a second concurrentRelease()on the sameOverlayImageinstance.
- On the GPU Main Thread,
Since the reference counter is non-atomic and lacks shared synchronization, both threads can concurrently decrement and observe the reference count as zero, resulting in a concurrent delete this (double-free) of the OverlayImage object.
Potential Steps to Trigger
Since our tooling does not currently have the ability to run code, the following are potential steps an attacker might follow from a compromised renderer to attempt to trigger this vulnerability:
- On Android with DrDc enabled, open a same-origin popup window to gain control over a
RootCompositorFrameSinklifetime. - Create a SharedImage using
gpu.mojom.SharedImageInterfacewith the flagsSHARED_IMAGE_USAGE_SCANOUT | DISPLAY_READ | RASTER_WRITEand formatRGBA_8888to create a backing. - Submit a
CompositorFramecontaining aTextureDrawQuadreferencing the created SharedImage and promote it to a SurfaceControl overlay. This sets up the concurrentOverlayImagereference states. - Close the popup window to force the browser to initiate teardown of the
RootCompositorFrameSinkand call~SkiaOutputDeviceBufferQueueon theCompositorGpuThread. - Simultaneously send a rapid stream of
DestroySharedImageMojo calls from the renderer to trigger destruction of the factory reference on the GPU main thread, aiming to hit the race window during the compositor thread’s teardown of the presenter.
Impact
If successfully exploited, a double-free of OverlayImage results in heap corruption. It also causes a double close() of the held fence file descriptors (end_read_fence_ and previous_end_read_fence_), which can close unrelated newly-opened file descriptors in the process. Because the Android GPU process is unsandboxed, this memory corruption vulnerability provides a privilege escalation pathway.
Suggested Fix
To resolve this potential race, make OverlayImage thread-safe refcounted by changing its inheritance from base::RefCounted<OverlayImage> to base::RefCountedThreadSafe<OverlayImage>:
class OverlayImage final : public base::RefCountedThreadSafe<OverlayImage> {
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.