Chrome · Skia
CVE-2026-14410
Logic Error in Skia
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTStests/graphite/crbug_513836996.cpp |
modified |
Files Changed
gn/tests.gnisrc/gpu/graphite/Device.cpptests/graphite/crbug_513836996.cpp
Patch
From 17b4bab488449ed4aaca0a406bc950dcc88fa357 Mon Sep 17 00:00:00 2001
From: Thomas Smith <thomsmit@google.com>
Date: Tue, 19 May 2026 15:33:06 -0400
Subject: [PATCH] [graphite] correctly advance index in drawEdgeAAImageSet
Bug: b/513836996
Change-Id: Iff633e0d99443cfc431352cea05051ff567b8f64
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1238179
Reviewed-by: Kaylee Lubick <kjlubick@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
---
diff --git a/gn/tests.gni b/gn/tests.gni
index f4a4605..3430e2d 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -336,6 +336,7 @@
"$_tests/graphite/UpdateBackendTextureTest.cpp",
"$_tests/graphite/UploadBufferManagerTest.cpp",
"$_tests/graphite/VerticesPaddingTest.cpp",
+ "$_tests/graphite/crbug_513836996.cpp",
]
precompile_tests_sources = [
diff --git a/src/gpu/graphite/Device.cpp b/src/gpu/graphite/Device.cpp
index 218c763..c75b096 100644
--- a/src/gpu/graphite/Device.cpp
+++ b/src/gpu/graphite/Device.cpp
@@ -1309,32 +1309,31 @@
dstToDraw.setEmpty();
}
}
- if (dstToDraw.isEmpty()) {
- continue; // Nothing to draw for this set entry
+ if (!dstToDraw.isEmpty()) {
+ PaintParams::SimpleImage imageShader{
+ set[i].fImage.get(),
+ &localMatrix,
+ constraint == SkCanvas::kStrict_SrcRectConstraint ? subset : imageBounds,
+ sampling};
+
+ // NOTE: See drawEdgeAAQuad for details, we do not snap non-AA quads.
+ SkEnumBitMask<EdgeAAQuad::Flags> flags =
+ static_cast<EdgeAAQuad::Flags>(set[i].fAAFlags);
+ EdgeAAQuad quad = set[i].fHasClip ? EdgeAAQuad(dstClips + dstClipIndex, flags)
+ : EdgeAAQuad(dstToDraw, flags);
+
+ // TODO: Calling drawGeometry() for each entry re-evaluates the clip stack every time,
+ // which is consistent with Ganesh's behavior. It also matches the behavior if edge-AA
+ // images were submitted one at a time by SkiaRenderer (a nice client simplification).
+ // However, we should explore the performance trade off with doing one bulk evaluation
+ // for the whole set
+ const SkMatrix* xtraXform =
+ set[i].fMatrixIndex < 0 ? nullptr : &preViewMatrices[set[i].fMatrixIndex];
+ this->drawGeometry(xtraXform ? localToDevice.concat(SkM44(*xtraXform)) : localToDevice,
+ Geometry(quad),
+ PaintParams(paint, imageShader, set[i].fAlpha),
+ DefaultFillStyle());
}
-
- PaintParams::SimpleImage imageShader{set[i].fImage.get(),
- &localMatrix,
- constraint == SkCanvas::kStrict_SrcRectConstraint ?
- subset : imageBounds,
- sampling};
-
- // NOTE: See drawEdgeAAQuad for details, we do not snap non-AA quads.
- SkEnumBitMask<EdgeAAQuad::Flags> flags = static_cast<EdgeAAQuad::Flags>(set[i].fAAFlags);
- EdgeAAQuad quad = set[i].fHasClip ? EdgeAAQuad(dstClips + dstClipIndex, flags)
- : EdgeAAQuad(dstToDraw, flags);
-
- // TODO: Calling drawGeometry() for each entry re-evaluates the clip stack every time, which
- // is consistent with Ganesh's behavior. It also matches the behavior if edge-AA images were
- // submitted one at a time by SkiaRenderer (a nice client simplification). However, we
- // should explore the performance trade off with doing one bulk evaluation for the whole set
- const SkMatrix* xtraXform = set[i].fMatrixIndex < 0 ? nullptr
- : &preViewMatrices[set[i].fMatrixIndex];
- this->drawGeometry(xtraXform ? localToDevice.concat(SkM44(*xtraXform)) : localToDevice,
- Geometry(quad),
- PaintParams(paint, imageShader, set[i].fAlpha),
- DefaultFillStyle());
-
dstClipIndex += 4 * set[i].fHasClip;
}
}
diff --git a/tests/graphite/crbug_513836996.cpp b/tests/graphite/crbug_513836996.cpp
new file mode 100644
index 0000000..436468f
--- /dev/null
+++ b/tests/graphite/crbug_513836996.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkBitmap.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkColor.h"
+#include "include/core/SkImageInfo.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSamplingOptions.h"
+#include "include/core/SkSurface.h"
+#include "include/gpu/graphite/Context.h"
+#include "include/gpu/graphite/Image.h"
+#include "include/gpu/graphite/Recorder.h"
+#include "include/gpu/graphite/Recording.h"
+#include "include/gpu/graphite/Surface.h"
+#include "tests/Test.h"
+
+using namespace skgpu::graphite;
+
+DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(crbug_513836996, reporter, context, CtsEnforcement::kNever) {
+ constexpr int kW = 200;
+ constexpr int kH = 100;
+ const SkImageInfo ii = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+
+ std::unique_ptr<Recorder> recorder = context->makeRecorder();
+ sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(recorder.get(), ii);
+ SkCanvas* canvas = surface->getCanvas();
+ canvas->clear(SK_ColorBLACK);
+
+ auto src_ii = SkImageInfo::Make(32, 32, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+ SkBitmap src_bm;
+ src_bm.allocPixels(src_ii);
+ src_bm.eraseColor(SK_ColorGREEN);
+
+ sk_sp<SkImage> rasterImage = SkImages::RasterFromBitmap(src_bm);
+ sk_sp<SkImage> green = SkImages::TextureFromImage(recorder.get(), rasterImage);
+
+ const SkPoint dstClips[8] = {
+ {10, 10}, {60, 10}, {60, 60}, {10, 60}, // LEFT
+ {110, 10}, {190, 10}, {190, 90}, {110, 90}, // RIGHT
+ };
+
+ SkCanvas::ImageSetEntry entries[2];
+ entries[0].fImage = green;
+ entries[0].fSrcRect = SkRect::MakeLTRB(-100, -100, -50, -50);
+ entries[0].fDstRect = SkRect::MakeWH(kW, kH);
+ entries[0].fMatrixIndex = -1;
+ entries[0].fAlpha = 1.f;
+ entries[0].fAAFlags = SkCanvas::kNone_QuadAAFlags;
+ entries[0].fHasClip = true;
+
+ entries[1].fImage = green;
+ entries[1].fSrcRect = SkRect::MakeWH(32, 32);
+ entries[1].fDstRect = SkRect::MakeWH(kW, kH);
+ entries[1].fMatrixIndex = -1;
+ entries[1].fAlpha = 1.f;
+ entries[1].fAAFlags = SkCanvas::kNone_QuadAAFlags;
+ entries[1].fHasClip = true;
+
+ canvas->experimental_DrawEdgeAAImageSet(
+ entries, 2, dstClips, nullptr, SkSamplingOptions(),
+ nullptr, SkCanvas::kFast_SrcRectConstraint);
+
+ std::unique_ptr<Recording> recording = recorder->snap();
+ context->insertRecording({recording.get()});
+
+ SkBitmap bm;
+ bm.allocPixels(ii);
+ REPORTER_ASSERT(reporter, surface->readPixels(bm, 0, 0), "Failed to read pixels");
+
+ SkColor left_px = bm.getColor(35, 35);
+ SkColor right_px = bm.getColor(150, 50);
+
+ REPORTER_ASSERT(reporter, left_px == SK_ColorBLACK,
+ "Left pixel should be BLACK, got 0x%08x", left_px);
+ REPORTER_ASSERT(reporter, right_px == SK_ColorGREEN,
+ "Right pixel should be GREEN, got 0x%08x", right_px);
+}
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page