CVE-2026-11039
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
forsrc/gpu/ganesh/ops/AAHairLinePathRenderer.cpp |
modified |
Files Changed
src/gpu/ganesh/ops/AAHairLinePathRenderer.cpp
Patch
From eb79f275731cae55a4eb999d47610f54e957e441 Mon Sep 17 00:00:00 2001 From: Greg Daniel <[email protected]> Date: Fri, 01 May 2026 15:08:47 +0000 Subject: [PATCH] Fix AAHairlineOp to fill in default degenerate quad information. When calls to bloat_quad fail, make sure we're stilling filling in default values and progressing the vert pointer so we aren't leaving uninitialized data in the buffer that gets read later on. Bug: b/498204112 Change-Id: Ic15a4ca9fbd2aa7f3e8a2fd142140f149fc4c721 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1223519 Reviewed-by: Thomas Smith <[email protected]> Commit-Queue: Greg Daniel <[email protected]> --- diff --git a/src/gpu/ganesh/ops/AAHairLinePathRenderer.cpp b/src/gpu/ganesh/ops/AAHairLinePathRenderer.cpp index c0413ed..de3f01f 100644 --- a/src/gpu/ganesh/ops/AAHairLinePathRenderer.cpp +++ b/src/gpu/ganesh/ops/AAHairLinePathRenderer.cpp @@ -681,6 +681,14 @@ } } +static void backfill_degenerate_bezier(BezierVertex** vert) { + memset(*vert, 0, kQuadNumVertices * sizeof(BezierVertex)); + for (int i = 0; i < kQuadNumVertices; ++i) { + (*vert)[i].fPos.set(SK_ScalarMax, SK_ScalarMax); + } + *vert += kQuadNumVertices; +} + void add_conics(const SkPoint p[3], const SkScalar weight, const SkMatrix* toDevice, @@ -689,6 +697,8 @@ if (bloat_quad(p, toDevice, toSrc, *vert)) { set_conic_coeffs(p, *vert, weight); *vert += kQuadNumVertices; + } else { + backfill_degenerate_bezier(vert); } } @@ -720,6 +730,8 @@ set_uv_quad(choppedQuadPts, outVerts); memcpy(*vert, outVerts, kQuadNumVertices * sizeof(BezierVertex)); *vert += kQuadNumVertices; + } else { + backfill_degenerate_bezier(vert); } --stepCount; } @@ -729,6 +741,8 @@ set_uv_quad(&choppedQuadPts[2], outVerts); memcpy(*vert, outVerts, kQuadNumVertices * sizeof(BezierVertex)); *vert += kQuadNumVertices; + } else { + backfill_degenerate_bezier(vert); } }
Original Bug Report
Cross-Origin Information Leak in Skia AAHairlineOp via Stale Vertex Buffers
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A vulnerability in Skia’s AAHairlineOp allows for the disclosure of uninitialized vertex buffer data. When rendering quadratic paths with extremely large coordinates, vertex generation fails but the mesh is drawn with the original primitive count, rendering stale cross-origin GPU memory.
Affected files:
third_party/skia/src/gpu/ganesh/ops/AAHairLinePathRenderer.cppthird_party/skia/src/gpu/ganesh/GrBufferAllocPool.cppthird_party/skia/src/gpu/ganesh/GrCpuBuffer.h
Estimated timestamp from git blame: 2021-11-18
Summary
A potential cross-origin information leak exists in Skia’s Ganesh backend within AAHairlineOp::onPrepareDraws. When preparing to render antialiased hairline paths, the operation allocates vertex space from a shared pool (GrBufferAllocPool) based on a calculated count of quadratic and conic segments.
If a segment contains extremely large finite coordinates, intermediate subdivision math produces NaN values, causing the vertex generation function (add_quads) to silently fail. Crucially, it fails to write any vertex data and fails to advance the vertex pointer. However, the subsequent GPU draw call (setIndexedPatterned) blindly uses the initially calculated segment count. This causes the GPU to render using uninitialized memory from the shared vertex pool, which may contain sensitive cross-origin geometry from other tabs. An attacker can read this data back using Canvas2D’s getImageData().
Technical Details
- Subdivision Math Overflow: When
gather_lines_and_quadsprocesses a quadratic curve with huge finite device coordinates (e.g.,~2e38),SkPointPriv::DistanceToLineBetweenSqdoverflows toInf. The functionnum_quad_subdivshandles this by reading the float exponent (which is128forInf) and clamps it tokMaxSub(4 subdivisions). - Vertex Allocation:
onPrepareDrawsallocates uninitialized vertex memory for these 16 sub-quads usingtarget->makeVertexSpacefrom theGrBufferAllocPool. Because the GPU context is shared across origins in Chrome, this pool reuses memory that may contain data from other origins. - NaN Generation and Silent Failure: During vertex generation,
add_quadscallsSkChopQuadAt. Interpolating between opposite-signed huge coordinates causesInf - Infevaluations, resulting inNaNvalues. The resulting points are passed tobloat_quad, which attempts to normalize the vectors. Normalization ofNaNvectors fails, causingbloat_quadto returnfalse. - The Bug: When
bloat_quadreturnsfalse,add_quadsskips writing the vertices. Crucially, it does not advance thebezVertspointer and does not backfill the memory with degenerate coordinates (unlikeadd_linein the same file, which safely writesSK_ScalarMaxon failure). - Stale Data Draw: The
onPrepareDrawsfunction remains unaware of the failure and issues a draw call (fMeshes[1]->setIndexedPatterned) using the original unadjustedquadCount. The GPU reads the uninitialized slots in the vertex buffer, rendering stale cross-origin data.
Potential Trigger Steps
Note: These are suggested steps based on code analysis; our tooling cannot execute code to provide a working PoC.
- Create an HTML page with a
<canvas>element and get a 2D context. - Apply a massive scale transform:
ctx.setTransform(1e19, 0, 0, 1e19, 0, 0); - Set a tiny positive
lineWidthto force Skia to useAAHairlineOp:ctx.lineWidth = 1e-20; - Draw a quadratic curve using large finite coordinates that will transform to values near
FLT_MAX:ctx.moveTo(-2e19, 0);ctx.quadraticCurveTo(2e19, 1e-10, -2e19, 2e-10);ctx.stroke(); - Use
ctx.getImageData(...)to read back the rendered pixels, which will contain representations of the stale cross-origin vertex memory.
Suggested Fix
Update add_quads and add_conics in third_party/skia/src/gpu/ganesh/ops/AAHairLinePathRenderer.cpp to defensively handle bloat_quad failures. Similar to the logic in add_line, if bloat_quad returns false, the code should fill the allocated BezierVertex slots with degenerate, off-screen coordinates (e.g., SK_ScalarMax) and explicitly advance the *vert pointer to ensure the memory is initialized and the pointer stays synchronized with the allocated quadCount.
Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33
Results from 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. And please feel free to reach out to me directly if you have concerns or feedback on the project.