Medium chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized Use in Skia
DescriptionUninitialized Use in Skia
ComponentSkia
Bug ClassUninitialized Memory
Tracker498204112
Fix commiteb79f275731c (skia) +14/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Changed Functions

FunctionChangeNotes
for
src/gpu/ganesh/ops/AAHairLinePathRenderer.cpp
modified

Files Changed

  • src/gpu/ganesh/ops/AAHairLinePathRenderer.cpp
From eb79f275731cae55a4eb999d47610f54e957e441 Mon Sep 17 00:00:00 2001
From: Greg Daniel <egdaniel@google.com>
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 <thomsmit@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
---

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);
     }
 }
 
Loading diff…

Original Bug Report

reported by vm...@google.com

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.cpp
  • third_party/skia/src/gpu/ganesh/GrBufferAllocPool.cpp
  • third_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

  1. Subdivision Math Overflow: When gather_lines_and_quads processes a quadratic curve with huge finite device coordinates (e.g., ~2e38), SkPointPriv::DistanceToLineBetweenSqd overflows to Inf. The function num_quad_subdivs handles this by reading the float exponent (which is 128 for Inf) and clamps it to kMaxSub (4 subdivisions).
  2. Vertex Allocation: onPrepareDraws allocates uninitialized vertex memory for these 16 sub-quads using target->makeVertexSpace from the GrBufferAllocPool. Because the GPU context is shared across origins in Chrome, this pool reuses memory that may contain data from other origins.
  3. NaN Generation and Silent Failure: During vertex generation, add_quads calls SkChopQuadAt. Interpolating between opposite-signed huge coordinates causes Inf - Inf evaluations, resulting in NaN values. The resulting points are passed to bloat_quad, which attempts to normalize the vectors. Normalization of NaN vectors fails, causing bloat_quad to return false.
  4. The Bug: When bloat_quad returns false, add_quads skips writing the vertices. Crucially, it does not advance the bezVerts pointer and does not backfill the memory with degenerate coordinates (unlike add_line in the same file, which safely writes SK_ScalarMax on failure).
  5. Stale Data Draw: The onPrepareDraws function remains unaware of the failure and issues a draw call (fMeshes[1]->setIndexedPatterned) using the original unadjusted quadCount. 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.

  1. Create an HTML page with a <canvas> element and get a 2D context.
  2. Apply a massive scale transform: ctx.setTransform(1e19, 0, 0, 1e19, 0, 0);
  3. Set a tiny positive lineWidth to force Skia to use AAHairlineOp: ctx.lineWidth = 1e-20;
  4. 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();
  5. 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.

View on issue tracker