Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Graphite
DescriptionUse after free in Graphite
ComponentGraphite
Bug ClassUAF
Tracker496393742
Fix commit3f9969421ad5 (skia) +1/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-04-15

Files Changed

  • include/gpu/graphite/Context.h
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;
Loading diff…

Original Bug Report

reported by [email protected]

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.h
  • third_party/skia/src/gpu/graphite/Context.cpp
  • third_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:

  1. fMappedBufferManager is destroyed, leaving the raw pointer in the pending callback dangling.
  2. fQueueManager is destroyed. Its destructor (QueueManager::~QueueManager) calls checkForFinishedWork(SyncToCpu::kYes).
  3. checkForFinishedWork waits for the GPU to finish outstanding work by blocking and pumping Dawn WebGPU events (e.g., instance.ProcessEvents()).
  4. Once the work finishes, the QueueManager destroys the GpuWorkSubmission, which unconditionally executes the registered finish callback with a success status.
  5. The callback dereferences the dangling manager pointer, performing a UAF read (manager->ownerID()) and a UAF write (manager->insert()).
  6. manager->insert() calls emplace_front() on a std::forward_list located 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:

  1. Request an asynchronous pixel readback via WebGL/WebGPU to queue the vulnerable callback.
  2. Trigger a GPU context destruction (e.g., by forcing a channel teardown or simulating a context loss).
  3. 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 freed ClientMappedBufferManager.
  4. This predictable, same-thread reallocation window allows the attacker to reliably reclaim the freed memory chunk.
  5. 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

  1. Swap the declaration order in third_party/skia/include/gpu/graphite/Context.h so that fQueueManager is destroyed first:
std::unique_ptr<ClientMappedBufferManager> fMappedBufferManager;
std::unique_ptr<QueueManager> fQueueManager;
  1. Alternatively, implement a mechanism to explicitly cancel or fail pending callbacks during Context teardown before members are destroyed, similar to the abandonContext logic 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.

View on issue tracker