CVE-2026-17992
Overview
Background
- Conic
- A rational quadratic curve segment in Skia defined by two control points plus a scalar weight
w, appended to a path throughSkPathBuilder::conicTo. - `SkPathBuilder`
- The mutable builder that accumulates path verbs, points, and conic weights before producing an immutable
SkPathviadetach. - Degenerate conic
- A conic whose weight is not a normal positive value (e.g. zero, negative, or non-finite), which
SkPath/SkGeometrycannot process as an ordinary curve and historically demoted to a line. - `SkGeometry`
- Skia’s low-level curve math (evaluation, chopping, measurement) that assumes conic weights are strictly positive.
Root Cause Analysis
SkPathBuilder::conicTo in src/core/SkPathBuilder.cpp only special-cased a weight w of exactly one (demoted to a quad) and a non-finite w (demoted to two lines), but otherwise appended a kConic_Verb with whatever w the caller supplied, including zero or negative values. This violated the invariant, documented in SkGeometry.h, that conic weights are strictly positive; per the commit message the older SkPath code path had explicitly demoted such degenerate conics to a lineTo, and that guard had been lost in the builder. Downstream consumers such as SkGeometry and SkPathMeasure then operated on a conic whose weight was zero (which the regression test shows can also arise when a transform like makeTransform collapses a previously positive weight to 0), producing an assertion failure and, per the advisory’s UninitializedMemory classification, use of uninitialized memory.
The fix restores the demotion by adding if (w <= 0) { return this->lineTo(pt2); } at the top of conicTo, so any non-positive weight becomes a well-defined line to pt2 instead of an invalid conic. This works because it re-establishes the positive-weight precondition before any conic verb, point, or weight is ever stored.
SkPathBuilder::conicTo failed to enforce SkGeometry’s positive-weight invariant, admitting w <= 0 conics that downstream math treats as valid; the fix simply demotes every non-positive-weight conic to a lineTo(pt2), eliminating the degenerate curve at the source.Attack Path
- Supply a path with a non-positive conic weight
Through any surface that builds a path via
SkPathBuilder::conicTo(e.g. graphics content that maps to Skia path construction), the attacker specifies a conic withw <= 0, or a weight that a later transform collapses to0. - Bypass the demotion guards
Because the original
conicToonly handledw == 1and non-finitew, the zero/negative weight is stored verbatim as akConic_Verbwith its point and weight, retained throughdetachinto anSkPath. - Trigger degenerate-conic math
Consuming the path (the regression test drives this via
makeTransformfollowed bySkPathMeasure::getLength) invokesSkGeometryroutines that assume a positive weight, hitting the assertion the test describes and the advisory’s uninitialized-memory condition. - Escalate from uninitialized use
Any read/write primitive, out-of-bounds access, or code execution beyond the uninitialized-memory use is not demonstrated by the diff
(inferred).
Impact Assessment
UninitializedMemory, “Uninitialized Use in Skia”), reachable during path evaluation such as transform and measurement. The preconditions are limited to being able to build or supply a path with a non-positive (or transform-nulled) conic weight. The vendor advisory rates this low severity and does not report in-the-wild exploitation; NVD scores it CVSS 6.5. The advisory provided no exploited-in-the-wild status or severity rating beyond the stated impact.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifsrc/core/SkPathBuilder.cpp |
modified | |
DEF_TESTtests/PathBuilderTest.cpp |
modified |
Files Changed
include/core/SkPathBuilder.hsrc/core/SkPathBuilder.cpptests/PathBuilderTest.cpp
Audit Directions
- Missing invariant checks at builder entry pointsAudit other
SkPathBuilderandSkPathmethods (and any curve constructors) to confirm they reject or normalize out-of-domain inputs before storing verbs, points, or weights. - Weight/parameter assumptions in low-level mathReview
SkGeometryandSkPathMeasureroutines that assume strictly positive conic weights (perSkGeometry.h) and verify every caller guarantees that precondition. - Transform-induced degeneracyLook for places where a previously valid parameter can become degenerate after operations like
makeTransform, and ensure such collapsed values are re-validated rather than fed directly into curve math.
Patch
From da379fdf722dc5df60caefed012952117a5ce4b4 Mon Sep 17 00:00:00 2001 From: Kaylee Lubick <[email protected]> Date: Tue, 16 Jun 2026 16:59:17 -0400 Subject: [PATCH] Handle w<=0 conics SkPath had special logic for degenerate conics [1] and SkGeometry has an assumption of positive weights [2][3] (possibly other places too), so this restores that logic to turn it into a lineTo. [1] https://github.com/google/skia/blob/19936eb1b23fef5187b07fb2e0e67dcf605c0672/src/core/SkPath.cpp#L754-L756 [2] https://github.com/google/skia/blob/d7196b0b493925df3be1e259f41a3c6678732b45/src/core/SkGeometry.h#L350 [3] https://review.skia.org/667436 [4] https://review.skia.org/1225597 Bug: 520506316 Change-Id: I6946e755c5cacade8a487bc882ba0ce5e34531a1 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1267718 Auto-Submit: Kaylee Lubick <[email protected]> Commit-Queue: Florin Malita <[email protected]> Reviewed-by: Florin Malita <[email protected]> --- diff --git a/include/core/SkPathBuilder.h b/include/core/SkPathBuilder.h index d4b1907..ad6882d 100644 --- a/include/core/SkPathBuilder.h +++ b/include/core/SkPathBuilder.h @@ -252,12 +252,14 @@ Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed. - If w is finite and not one, appends kConic_Verb to verb array; + If w is finite, positive, and not one, appends kConic_Verb to verb array; and pt1, pt2 to SkPoint array; and w to conic weights. If w is one, appends kQuad_Verb to verb array, and pt1, pt2 to SkPoint array. + If w is zero, this is the same as lineTo(pt2) + If w is not finite, appends kLine_Verb twice to verb array, and pt1, pt2 to SkPoint array. diff --git a/src/core/SkPathBuilder.cpp b/src/core/SkPathBuilder.cpp index 76d418a..833763d 100644 --- a/src/core/SkPathBuilder.cpp +++ b/src/core/SkPathBuilder.cpp @@ -202,6 +202,9 @@ SkPathBuilder& SkPathBuilder::conicTo(SkPoint pt1, SkPoint pt2, SkScalar w) { this->ensureMove(); + if (w <= 0) { + return this->lineTo(pt2); + } SkPoint* p = fPts.push_back_n(2); p[0] = pt1; p[1] = pt2; diff --git a/tests/PathBuilderTest.cpp b/tests/PathBuilderTest.cpp index ba22ad3..b152e8d 100644 --- a/tests/PathBuilderTest.cpp +++ b/tests/PathBuilderTest.cpp @@ -7,6 +7,7 @@ #include "include/core/SkPath.h" #include "include/core/SkPathBuilder.h" +#include "include/core/SkPathMeasure.h" #include "include/core/SkPathTypes.h" #include "include/core/SkPoint.h" #include "include/core/SkRRect.h" @@ -1098,11 +1099,73 @@ { // If w is not finite, appends kLine_Verb twice to verb array, and // pt1, pt2 to SkPoint array. - SkPath p = SkPathBuilder().conicTo({10, 5}, {10, 10}, SK_ScalarInfinity).detach(); + SkPath p = SkPathBuilder().conicTo({10, 5}, {10, 10}, SK_FloatInfinity).detach(); REPORTER_ASSERT(reporter, !p.isEmpty()); REPORTER_ASSERT(reporter, p.verbs().size() == 3u); // moveTo, lineTo, lineTo REPORTER_ASSERT(reporter, p.points().size() == 3u); REPORTER_ASSERT(reporter, p.conicWeights().empty()); REPORTER_ASSERT(reporter, p.getSegmentMasks() == SkPath::kLine_SegmentMask); } + + { + // If w is 0, appends kLine_Verb once to verb array, and + // pt2 to SkPoint array. + SkPath p = SkPathBuilder().conicTo({10, 5}, {10, 10}, 0).detach(); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, p.verbs().size() == 2u); // moveTo, lineTo + REPORTER_ASSERT(reporter, p.points().size() == 2u); + REPORTER_ASSERT(reporter, p.conicWeights().empty()); + REPORTER_ASSERT(reporter, p.getSegmentMasks() == SkPath::kLine_SegmentMask); + } +} + +DEF_TEST(SkPathBuilder_b520944501, reporter) { + SkPath path = SkPathBuilder(SkPathFillType::kInverseEvenOdd) + .moveTo(0, 0) + .cubicTo(8.51330895e-18f, + 4.80215861e+30f, + 4.80215347e+30f, + 4.73888368e-38f, + 2.36942783e-38f, + 7.54830852e-30f) + .moveTo(4.80215831e+30f, 1.03969141e-21f) + .conicTo(4.80215831e+30f, + 4.73888368e-38f, + 2.40107915e+30f, + 4.73888368e-38f, + 0.707106769f) + .conicTo(2.0779294e-21f, + 4.73888368e-38f, + 2.0779294e-21f, + 1.03969141e-21f, + 0.707106769f) + .conicTo(2.0779294e-21f, + 2.07938282e-21f, + 2.40107915e+30f, + 2.07938282e-21f, + 0.707106769f) + .conicTo(4.80215831e+30f, + 2.07938282e-21f, + 4.80215831e+30f, + 1.03969141e-21f, + 0.707106769f) + .close() + .detach(); + + SkMatrix m; + m.setAll(0.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 10159839284371128320.0f, + 36261335138304000.0f, + 0.0f, + -0.0f); + + // This previously caused an issue where one of the conic weights turned to + // 0, causing an assert later. We should be avoiding that. + SkPath transformed = path.makeTransform(m); + SkPathMeasure meas(transformed, false); + (void)meas.getLength(); }