CVE-2026-6304
Overview
Files Changed
include/gpu/graphite/Context.h
Patch
From 3f9969421ad5d0b544b2013ea1386864589511e5 Mon Sep 17 00:00:00 2001 From: Kalvin Lee <[email protected]> Date: Fri, 27 Mar 2026 16:37:30 +0900 Subject: [PATCH] Terracotta-Phase-1: Reorder member destruction This is a speculative patch. Please see the bug for details. Bug: b/496393742 Change-Id: Ib574a0086f92abda83715b36a0d1e7a99e9edd67 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1196676 Reviewed-by: Michael Ludwig <[email protected]> Commit-Queue: Michael Ludwig <[email protected]> Reviewed-by: Thomas Smith <[email protected]> --- diff --git a/include/gpu/graphite/Context.h b/include/gpu/graphite/Context.h index 84ec802..d81e3fe 100644 --- a/include/gpu/graphite/Context.h +++ b/include/gpu/graphite/Context.h @@ -406,8 +406,8 @@ sk_sp<SharedContext> fSharedContext; std::unique_ptr<ResourceProvider> fResourceProvider; - std::unique_ptr<QueueManager> fQueueManager; std::unique_ptr<ClientMappedBufferManager> fMappedBufferManager; + std::unique_ptr<QueueManager> fQueueManager; std::unique_ptr<const skcpu::ContextImpl> fCPUContext; PersistentPipelineStorage* fPersistentPipelineStorage;
Original Bug Report
Potential Use-After-Free in Skia Graphite Context via Member Destruction Order
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A potential Use-After-Free vulnerability exists in Skia’s Graphite backend during Context destruction. Because fMappedBufferManager is destroyed before fQueueManager, pending asynchronous readback callbacks executed during QueueManager teardown access the freed manager. This could provide an attacker with a reliable heap overwrite primitive in the GPU process.
Affected files:
third_party/skia/include/gpu/graphite/Context.hthird_party/skia/src/gpu/graphite/Context.cppthird_party/skia/src/gpu/graphite/QueueManager.cpp
Estimated timestamp from git blame: 2023-11-27
Summary
A potential Use-After-Free (UAF) vulnerability exists in the skgpu::graphite::Context class within Skia’s Graphite backend. The root cause is a C++ member destruction order issue where fMappedBufferManager is destroyed before fQueueManager. Pending GPU callbacks are executed during fQueueManager’s destruction, leading them to access the already-freed fMappedBufferManager.
Root Cause Analysis
In third_party/skia/include/gpu/graphite/Context.h, the members are declared as follows:
std::unique_ptr<QueueManager> fQueueManager;
std::unique_ptr<ClientMappedBufferManager> fMappedBufferManager;
According to the C++ standard, class members are destroyed in the reverse order of their declaration. Therefore, when Context::~Context() executes, fMappedBufferManager is destroyed and its memory is freed before fQueueManager is destroyed.
When a client requests an asynchronous pixel readback (e.g., via Context::finalizeAsyncReadPixels), a callback is created and registered with the QueueManager. This callback stores a raw pointer to fMappedBufferManager.get().
During context teardown, the following sequence occurs:
fMappedBufferManageris destroyed, leaving the raw pointer in the pending callback dangling.fQueueManageris destroyed. Its destructor (QueueManager::~QueueManager) callscheckForFinishedWork(SyncToCpu::kYes).checkForFinishedWorkwaits for the GPU to finish outstanding work by blocking and pumping Dawn WebGPU events (e.g.,instance.ProcessEvents()).- Once the work finishes, the
QueueManagerdestroys theGpuWorkSubmission, which unconditionally executes the registered finish callback with a success status. - The callback dereferences the dangling manager pointer, performing a UAF read (
manager->ownerID()) and a UAF write (manager->insert()). manager->insert()callsemplace_front()on astd::forward_listlocated inside the freed object, writing a new heap node pointer directly into the freed memory.
Potential Attacker Steps
Note: These are theoretical steps, as our setup does not have the ability to run code or construct a working proof-of-concept.
An attacker with control over a compromised Renderer process could potentially exploit this to achieve a Renderer-to-GPU sandbox escape:
- Request an asynchronous pixel readback via WebGL/WebGPU to queue the vulnerable callback.
- Trigger a GPU context destruction (e.g., by forcing a channel teardown or simulating a context loss).
- During the
QueueManager’s synchronous wait loop, Dawn events are pumped. The attacker can queue Dawn IPC tasks immediately prior to destruction to allocate memory of the exact same size as the freedClientMappedBufferManager. - This predictable, same-thread reallocation window allows the attacker to reliably reclaim the freed memory chunk.
- When the callback fires, the
emplace_front()call writes a valid heap pointer into the attacker-controlled reclaimed memory, providing a powerful primitive to hijack control flow or corrupt further GPU process state.
MiraclePtr (BRP) does not mitigate this vulnerability because Skia utilizes un-rewritten raw pointers for these internal components.
Proposed Fix
- Swap the declaration order in
third_party/skia/include/gpu/graphite/Context.hso thatfQueueManageris destroyed first:
std::unique_ptr<ClientMappedBufferManager> fMappedBufferManager;
std::unique_ptr<QueueManager> fQueueManager;
- Alternatively, implement a mechanism to explicitly cancel or fail pending callbacks during
Contextteardown before members are destroyed, similar to theabandonContextlogic utilized in Skia’s Ganesh backend.
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.