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 <[email protected]> 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 <[email protected]> Reviewed-by: Jorge Betancourt <[email protected]> --- 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.