CVE-2026-7949
Overview
Files Changed
src/text/gpu/SkChromeRemoteGlyphCache.cpp
Patch
From f04d09d1075926a5e5ef0a52171c9c12043fab03 Mon Sep 17 00:00:00 2001 From: Florin Malita <[email protected]> Date: Fri, 27 Mar 2026 12:07:04 -0400 Subject: [PATCH] Use a local data copy for strike deserialization The readStrikeData() input is volatile (shared memory) and untrusted. To avoid time-of-check to time-of-use issues during deserialization, always make a copy when transitioning to internal/non-volatile APIs. This is similar to the other defensive copies used in Chromium's cc/paint_op deserialization, e.g. [1]. [1] https://source.chromium.org/chromium/chromium/src/+/main:cc/paint/paint_op_reader.cc;drc=9c91b2494d4bf0a2d33b5985f7d1af79e72146f2;l=329 Bug: 496206134 Change-Id: I775d24b10ee7348b159016171ce044737f5bcbe0 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1197136 Commit-Queue: Florin Malita <[email protected]> Reviewed-by: Kaylee Lubick <[email protected]> --- diff --git a/src/text/gpu/SkChromeRemoteGlyphCache.cpp b/src/text/gpu/SkChromeRemoteGlyphCache.cpp index b0a37db..caf2869 100644 --- a/src/text/gpu/SkChromeRemoteGlyphCache.cpp +++ b/src/text/gpu/SkChromeRemoteGlyphCache.cpp @@ -632,9 +632,12 @@ SkASSERT(memorySize != 0); SkASSERT(memory != nullptr); + // Use a local copy to defend against volatile memory TOCTOU issues during deserialization. + sk_sp<SkData> safeMemory = SkData::MakeWithCopy(const_cast<const void*>(memory), memorySize); + // We do not need to set any SkDeserialProcs here because SkStrikeServerImpl::writeStrikeData // did not encode any SkImages. - SkReadBuffer buffer{const_cast<const void*>(memory), memorySize}; + SkReadBuffer buffer{safeMemory->data(), safeMemory->size()}; // Limit the kinds of effects that appear in a glyph's drawable (crbug.com/1442140): buffer.setAllowSkSL(false);
Original Bug Report
TOCTOU in Skia SkPathData::Make leads to Heap OOB Read in GPU process
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A Time-of-Check Time-of-Use (TOCTOU) vulnerability exists in Skia’s path deserialization when reading font data from shared memory in the GPU process. A compromised renderer can mutate path verbs between validation and copying, creating a malformed SkPath. Iterating this path during rasterization causes a heap out-of-bounds read, potentially leaking sensitive cross-origin data.
Affected files:
third_party/skia/src/core/SkPathData.cppthird_party/skia/src/core/SkPath_serial.cppthird_party/skia/src/core/SkReadBuffer.cppthird_party/skia/src/text/gpu/SkChromeRemoteGlyphCache.cppgpu/command_buffer/service/raster_decoder.cc
Estimated timestamp from git blame: 2025-12-23
Description
There is a potential Time-of-Check Time-of-Use (TOCTOU) vulnerability in Skia’s path deserialization logic when handling remote glyph cache data in the GPU process. This can lead to a heap out-of-bounds (OOB) read, allowing a compromised renderer to leak cross-origin GPU memory.
When a renderer sends font data to the GPU process via RasterDecoderImpl::DoRasterCHROMIUM, the data is passed via a shared memory buffer. The buffer is passed down as a volatile span to ServiceFontManager::Deserialize. However, Skia’s SkStrikeClient::readStrikeData explicitly casts away the volatile qualifier and wraps the shared memory directly in an SkReadBuffer:
// third_party/skia/src/text/gpu/SkChromeRemoteGlyphCache.cpp
SkReadBuffer buffer{const_cast<const void*>(memory), memorySize};
When deserializing an SkPath with the kVerbsAreStoredForward_Version format, SkPath::ReadFromMemory skips allocating temporary storage and instead obtains direct pointers into this shared memory for the points and verbs arrays.
These shared memory spans are passed to SkPathData::Make:
// third_party/skia/src/core/SkPathData.cpp
sk_sp<SkPathData> SkPathData::Make(SkSpan<const SkPoint> pts, SkSpan<const SkPathVerb> vbs, ...) {
if (!valid_path_data(pts, vbs, conics)) { // [1] Time of Check
return nullptr;
}
return MakeNoCheck(pts, vbs, conics, {}, {}); // [2] Time of Use
}
- Time of Check:
valid_path_dataiterates over thevbsspan (reading directly from shared memory) to calculate the expected number of points, verifying it matches the size of theptsarray. - Time of Use: If validation passes,
MakeNoCheckallocates a newSkPathDataobject sized according to the original array lengths. It then callsSkSpanPriv::Copyto copy the verbs and points from the shared memory into the new allocation.
A compromised renderer can continuously mutate the verbs in shared memory. If the renderer changes a verb from kLine (requires 1 point) to kCubic (requires 3 points) after valid_path_data reads it but before MakeNoCheck copies it, the resulting SkPathData object will contain a kCubic verb but only have memory allocated for a kLine.
When the GPU process later iterates over this path (e.g., via SkPathEdgeIter or SkPath::RawIter) to draw it, the iterator will read past the bounds of the allocated fPoints array, treating adjacent heap memory as float coordinates. This geometry can then be read back via a canvas to leak sensitive data.
Suggested Steps to Trigger
(Note: These are potential steps as our setup does not have the ability to run code or verify a live exploit)
- A compromised renderer process allocates a shared memory buffer for font glyph serialization.
- The renderer crafts a serialized
SkPathin this buffer usingkVerbsAreStoredForward_Version, populating it with $N$ points and $N$ verbs (all set tokLine). - The renderer issues a
RasterDecoderImpl::DoRasterCHROMIUMcommand to the GPU process, pointing to this shared memory. - While the GPU process begins deserialization, a racing thread in the renderer continuously overwrites the
kLine(0x01) verbs in the shared memory withkCubic(0x04) verbs. - If the race is won, the GPU process allocates an
SkPathDataobject with space for $N$ points but copies in $N$kCubicverbs. - The renderer requests the GPU process to draw the glyph and reads back the resulting pixels. The out-of-bounds heap memory affects the drawn geometry, allowing the attacker to infer the leaked memory contents.
Suggested Fix
To prevent TOCTOU vulnerabilities, untrusted data from shared memory should be copied into secure, local memory before any validation occurs.
In SkPath::ReadFromMemory (or SkPathData::Make), the verbs and points arrays should be copied out of the SkReadBuffer (shared memory) into local buffers before calling valid_path_data. Alternatively, MakeNoCheck could be modified to perform the validation after copying the data, or a new Make variant could be introduced that safely copies and validates in a single pass.
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.