CVE-2026-10011
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifsrc/gpu/ganesh/ops/AtlasTextOp.cpp |
modified |
Files Changed
src/gpu/ganesh/ops/AtlasTextOp.cpp
Patch
From 43f969a9bce8019354d392f384fe10328655f924 Mon Sep 17 00:00:00 2001 From: Michael Ludwig <[email protected]> Date: Tue, 19 May 2026 14:53:32 -0400 Subject: [PATCH] [ganesh] Require glyph padding to use linear sampling Bug: b/514017326 Change-Id: I9425c1f86aa8762333ba72b4ba30f5a056cdbfa9 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1239738 Commit-Queue: Michael Ludwig <[email protected]> Reviewed-by: Robert Phillips <[email protected]> --- diff --git a/src/gpu/ganesh/ops/AtlasTextOp.cpp b/src/gpu/ganesh/ops/AtlasTextOp.cpp index e1005ce..d335480 100644 --- a/src/gpu/ganesh/ops/AtlasTextOp.cpp +++ b/src/gpu/ganesh/ops/AtlasTextOp.cpp @@ -491,8 +491,12 @@ } else #endif { - auto filter = fNeedsGlyphTransform ? GrSamplerState::Filter::kLinear - : GrSamplerState::Filter::kNearest; + // Only use linear padding if the glyphs were also padded for it. If we somehow get a direct + // subrun with a corrupted transform, we should still use nearest neighbor since it was + // packed tightly. + const bool hasGlyphPadding = fHead->fSubRun.glyphSrcPadding() > 0; + auto filter = fNeedsGlyphTransform && hasGlyphPadding ? GrSamplerState::Filter::kLinear + : GrSamplerState::Filter::kNearest; // Bitmap text uses a single color, combineIfPossible ensures all geometries have the same // color, so we can use the first's without worry. flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make( @@ -647,8 +651,9 @@ } else #endif { - auto filter = fNeedsGlyphTransform ? GrSamplerState::Filter::kLinear - : GrSamplerState::Filter::kNearest; + const bool hasGlyphPadding = fHead->fSubRun.glyphSrcPadding() > 0; + auto filter = fNeedsGlyphTransform && hasGlyphPadding + ? GrSamplerState::Filter::kLinear : GrSamplerState::Filter::kNearest; reinterpret_cast<GrBitmapTextGeoProc*>(gp)->addNewViews(views, numActiveViews, filter); } } @@ -677,6 +682,12 @@ return CombineResult::kCannotCombine; } + // We use the same filter for every Geometry that is combined, but the filter choice only looks + // at the head's src padding, so we can only combine if we are consistent with that. + if (fHead->fSubRun.glyphSrcPadding() != that->fHead->fSubRun.glyphSrcPadding()) { + return CombineResult::kCannotCombine; + } + if (fProcessors != that->fProcessors) { return CombineResult::kCannotCombine; }
Original Bug Report
Potential cross-origin information disclosure via bilinear bleed in Skia Ganesh glyph atlas
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 logic flaw in Skia’s Ganesh backend allows a compromised renderer to force bilinear sampling on a shared GPU glyph atlas that lacks texel padding. By providing forged matrix metadata in serialized Slug objects, an attacker can bypass nearest-neighbor sampling constraints. The resulting sampling bleed enables the potential disclosure of cross-origin pixel data from the shared atlas.
Affected files:
third_party/skia/src/gpu/ganesh/ops/AtlasTextOp.cppthird_party/skia/src/text/gpu/VertexFiller.cppthird_party/skia/src/gpu/ganesh/text/GrAtlasManager.cppthird_party/skia/src/text/gpu/SubRunContainer.cppthird_party/skia/src/gpu/ganesh/text/GlyphData.cppthird_party/skia/src/gpu/ganesh/effects/GrBitmapTextGeoProc.cppthird_party/skia/src/gpu/ganesh/Device.cpp
Estimated timestamp from git blame: Unknown (Google3 checkout)
Background
In Chrome’s GPU process, Skia’s Ganesh backend (used for Out-Of-Process Rasterization, or OOP-R) manages a shared glyph atlas. This atlas is stored in GPU textures and is shared across all renderer processes and origins to improve performance and memory efficiency. By default, Chrome disables fSupportBilerpFromGlyphAtlas, causing glyphs to be packed into the atlas with zero border padding (0 texels) to conserve space. This design assumes that glyphs in the atlas will only be sampled using nearest-neighbor (kNearest) filtering.
The Vulnerability
The vulnerability lies in how sktext::gpu::AtlasTextOp determines the sampler filter mode and how the GPU process handles serialized Slug objects (representations of text draws).
-
Unvalidated Metadata: When a renderer sends a
cc::DrawSlugOp, the payload includes aSlugobject. This object containsVertexFillermetadata, specifically anfCreationMatrix. The GPU process deserializes this matrix directly from the renderer-supplied buffer inVertexFiller::MakeFromBuffer(third_party/skia/src/text/gpu/VertexFiller.cpp:61) without verifying its validity against the actual drawing context. -
Forced Bilinear Filtering: During rasterization,
AtlasTextOpdecides whether to usekNearestorkLinear(bilinear) filtering based on thefNeedsGlyphTransformflag. This flag is set totrueif the draw-time Canvas Transformation Matrix (CTM) does not match the storedfCreationMatrix(e.g., if there is a sub-pixel translation or scale). By forging thefCreationMatrixto mismatch the actual CTM, a compromised renderer can forcefNeedsGlyphTransformto betrue(third_party/skia/src/gpu/ganesh/ops/AtlasTextOp.cpp:274). -
Bilinear Bleed: When
fNeedsGlyphTransformis true, the GPU sampler is set toGrSamplerState::Filter::kLinear(third_party/skia/src/gpu/ganesh/ops/AtlasTextOp.cpp:494). Since the atlas was packed with zero padding, bilinear sampling at the glyph boundaries will interpolate between the attacker’s glyph and adjacent glyphs in the shared atlas belonging to other origins.
Impact
An attacker can read back the rasterized output (for example, by drawing to an accelerated 2D canvas and using getImageData()). Because the output contains a blend of the attacker’s pixels and the victim’s cross-origin glyph texels, the attacker can mathematically reconstruct cross-origin text or color-emoji data. This constitutes a high-severity cross-origin information leak in the GPU process.
Potential Exploit Steps
- From a compromised renderer, send commands to prime the shared atlas with glyphs from a victim origin (e.g., by navigating an iframe to a target site).
- Generate a malicious
Slugpayload where thefCreationMatrixis intentionally forged to mismatch the target draw CTM by a sub-pixel amount (e.g., 0.5 pixels). - Send a
cc::DrawSlugOpcontaining this Slug to the GPU process. - Execute a draw with the Slug using the CTM that triggers the mismatch.
- Read back the resulting pixels from the canvas and solve for the neighboring texel values from the victim’s origin.
Suggested Fix
The GPU process should validate that the initialPositionMatrix (or creationMatrix) within a Slug is consistent with the drawing context. Alternatively, if fSupportBilerpFromGlyphAtlas is false, Skia should strictly enforce kNearest filtering for direct mask subruns, regardless of the transform metadata provided by the renderer. A debug-only check currently exists as a SkASSERT in Device::drawSlug (third_party/skia/src/gpu/ganesh/Device.cpp:1440); this invariant should be enforced at runtime in release builds.
Evaluated with Chrome root at commit: 1a8d40fc44df2088d5945c0bf53584038aa1614a
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.
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/src/gpu/ganesh/Device.cpp;l=1440
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/src/gpu/ganesh/ops/AtlasTextOp.cpp;l=274
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/src/gpu/ganesh/ops/AtlasTextOp.cpp;l=494
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/src/text/gpu/VertexFiller.cpp;l=61