CVE-2026-8579
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifsrc/effects/SkTableMaskFilter.cpp |
modified | |
ifsrc/utils/SkMultiPictureDocument.cpp |
modified | |
forsrc/utils/SkMultiPictureDocument.cpp |
modified |
Files Changed
src/effects/SkTableMaskFilter.cppsrc/utils/SkMultiPictureDocument.cpp
Patch
From 4320748aa7d3dc82b7064b23625a78d621f21963 Mon Sep 17 00:00:00 2001
From: Kaylee Lubick <kjlubick@google.com>
Date: Mon, 30 Mar 2026 08:44:58 -0700
Subject: [PATCH] Validate sizes in mskp reading and SkTableMaskFilter
The max dimension for mskps is approximately the sqrt of INT32_MAX
which felt "big enough"
Bug: b/496526419
Change-Id: I6850c28e18f7427d85ea0ea724c5d056a4682970
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1198616
Commit-Queue: Kaylee Lubick <kjlubick@google.com>
Reviewed-by: Jorge Betancourt <jmbetancourt@google.com>
---
diff --git a/src/effects/SkTableMaskFilter.cpp b/src/effects/SkTableMaskFilter.cpp
index d422ce0..ad42372 100644
--- a/src/effects/SkTableMaskFilter.cpp
+++ b/src/effects/SkTableMaskFilter.cpp
@@ -78,14 +78,22 @@
if (src.fFormat != SkMask::kA8_Format) {
return false;
}
-
+ // SkAlign4 overflows when too close to INT32_MAX, so reject when too big.
+ constexpr int32_t kMaxWidth = 1 << 30;
+ if (src.fBounds.width() > kMaxWidth) {
+ return false;
+ }
dst->bounds() = src.fBounds;
dst->rowBytes() = SkAlign4(dst->fBounds.width());
dst->format() = SkMask::kA8_Format;
dst->image() = nullptr;
if (src.fImage) {
- dst->image() = SkMaskBuilder::AllocImage(dst->computeImageSize());
+ auto imgSize = dst->computeImageSize();
+ if (imgSize == 0) {
+ return false;
+ }
+ dst->image() = SkMaskBuilder::AllocImage(imgSize);
const uint8_t* srcP = src.fImage;
uint8_t* dstP = dst->image();
diff --git a/src/utils/SkMultiPictureDocument.cpp b/src/utils/SkMultiPictureDocument.cpp
index 5f327e6..72377d0 100644
--- a/src/utils/SkMultiPictureDocument.cpp
+++ b/src/utils/SkMultiPictureDocument.cpp
@@ -50,7 +50,9 @@
static constexpr char kEndPage[] = "SkMultiPictureEndPage";
-const uint32_t kVersion = 2;
+static constexpr uint32_t kVersion = 2;
+
+static constexpr uint32_t kMaxDimension = 1 << 16;
static SkSize join(const TArray<SkSize>& sizes) {
SkSize joined = {0, 0};
@@ -169,28 +171,34 @@
return 0;
}
uint32_t pageCount;
- if (!src->readU32(&pageCount) || pageCount > INT_MAX) {
+ if (!src->readU32(&pageCount) || pageCount > (INT_MAX / sizeof(SkSize))) {
return 0;
}
// leave stream position right here.
return SkTo<int>(pageCount);
}
-bool ReadPageSizes(SkStreamSeekable* stream,
+bool ReadPageSizes(SkStreamSeekable* src,
SkDocumentPage* dstArray,
int dstArrayCount) {
if (!dstArray || dstArrayCount < 1) {
return false;
}
- int pageCount = ReadPageCount(stream);
+ int pageCount = ReadPageCount(src);
if (pageCount < 1 || pageCount != dstArrayCount) {
return false;
}
+ if (src->hasLength() && src->getLength() < (static_cast<size_t>(pageCount) * sizeof(SkSize))) {
+ return false; // not enough memory to read all the sizes
+ }
for (int i = 0; i < pageCount; ++i) {
SkSize& s = dstArray[i].fSize;
- if (sizeof(s) != stream->read(&s, sizeof(s))) {
+ if (sizeof(s) != src->read(&s, sizeof(s))) {
return false;
}
+ if (s.isEmpty() || s.width() >= kMaxDimension || s.height() >= kMaxDimension) {
+ return false; // invalid sizes
+ }
}
// leave stream position right here.
return true;
Original Bug Report
Unbounded page dimensions in SkMultiPictureDocument lead to DoS in PrintCompositor
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: SkMultiPictureDocument::ReadPageSizes deserializes page dimensions without checking for excessively large values. A compromised renderer can send a malicious MSKP file with huge page sizes, causing massive memory allocations and integer overflows in Skia, leading to a Denial of Service in the PrintCompositor process.
Affected files:
third_party/skia/src/utils/SkMultiPictureDocument.cppthird_party/skia/src/pdf/SkPDFDocument.cppthird_party/skia/src/core/SkDocument.cppcomponents/services/print_compositor/print_compositor_impl.cc
Estimated timestamp from git blame: 2025-03-24
Summary
The SkMultiPictureDocument::ReadPageSizes function in Skia lacks upper-bound validation for page dimensions read from its input stream. This enables a compromised renderer to specify extremely large page sizes that are subsequently used by the PrintCompositor process. This issue leads to a Denial of Service via memory exhaustion and triggers integer overflows in downstream Skia components like mask filters.
Technical Details
When processing a SkMultiPictureDocument (MSKP) in the PrintCompositor utility process, the following data flow occurs:
- Deserialization: In
third_party/skia/src/utils/SkMultiPictureDocument.cpp, theReadPageSizesfunction readsSkSizevalues (floats) directly from the stream without any validation against upper bounds, infinity, or NaN. - Page Initiation: These unvalidated sizes are passed through
PrintCompositorImpltoSkDocument::beginPageinthird_party/skia/src/core/SkDocument.cpp. - PDF Generation: In
third_party/skia/src/pdf/SkPDFDocument.cpp, theonBeginPagemethod multiplies the dimensions byfRasterScale. The resulting value is rounded to an integer viatoRound(), which saturates toINT32_MAX. This results in anSkPDFDevicebeing initialized with massive bounds. - Integer Overflows in Filters: If the attacker also includes a drawing command with an
SkTableMaskFilter, the large device bounds propagate toSkPDFDevice::internalDrawPathWithFilter. The bounds are used to compute the size of masks. For example, inSkTableMaskFilterImpl::filterMask, the calculationdst->rowBytes() = SkAlign4(dst->fBounds.width())can overflowint32_t, anddst->computeImageSize()returns 0 due tosafeMul32returning 0 for sizes> INT32_MAX. This leads to a 0-byte allocation followed by a massive linear out-of-bounds write of >2GB, causing an immediate segmentation fault.
Impact
An attacker with control over a renderer process can send a malicious MSKP via the DidPrintContentParams.metafile_data_region IPC. This reachability allows the attacker to reliably crash the sandboxed PrintCompositor utility process by triggering massive out-of-bounds writes (which are guaranteed to hit unmapped guard pages) or OOMs, resulting in a Denial of Service.
Reproduction Steps (Suggested)
- In a compromised renderer, generate an MSKP file.
- Add a page with
SkSizeset to dimensions that scale to approximatelyINT32_MAX(e.g., width =1073741821, height =2). - Add a path to the page that fills the dimensions, and apply an
SkTableMaskFilterto the paint. - Send the MSKP via IPC to the
PrintCompositor. - Observe the
PrintCompositorprocess crash due to an OOM or a segmentation fault when Skia attempts to allocate or write to the massive mask buffer.
Suggested Fix
In third_party/skia/src/utils/SkMultiPictureDocument.cpp, ReadPageSizes should validate the deserialized SkSize values. If the width or height is negative, non-finite, or exceeds a reasonable maximum document size, the function should return false to reject the malformed document.
Evaluated with Chrome root at commit: 0eb4855bda702feaaa8b899336664f97e3df88b8
Results 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. Please feel free to reach out to me if you have concerns or feedback.