CVE-2026-79112
Overview
Background
- `SkTextBlob`
- the Skia object that holds one or more runs of positioned glyphs together with optional metadata such as UTF-8 text and per-glyph cluster indices.
- Cluster index
- an entry in the
buf->clustersarray that maps a glyph back to a byte offset within the run’s UTF-8 text buffer, and so must always fall within the text’s size. - `MakeFromBuffer`
- the Skia deserialization entry point that reconstructs an
SkTextBlobfrom an untrusted serialized buffer via a reader.
Root Cause Analysis
When deserializing an SkTextBlob, the code path in src/core/SkTextBlob.cpp read the run’s UTF-8 text (buf->utf8text, of length textSize) and its cluster array (buf->clusters) directly from the buffer without validating the cluster values against textSize. Cluster indices are supposed to be byte offsets into the textSize-byte UTF-8 text, so the invariant that every buf->clusters[i] is strictly less than textSize must hold, but nothing enforced it on deserialization. A compromised renderer could therefore supply cluster indices that overflow or underflow relative to textSize, causing later code that indexes the text using those clusters to read out of bounds.
The fix adds a loop over all glyphCount glyphs that rejects the blob (returns nullptr) if any buf->clusters[i] is >= textSize (compared as uint32_t), so an oversized or wrapped index can no longer be accepted. Because the comparison is unsigned and against the exact textSize, both the overflow (too-large index) and underflow (wrapped negative treated as a huge unsigned value) cases described in the advisory are caught before the blob is used.
buf->clusters[i] against textSize and returning nullptr on any out-of-range value.Attack Path
- Compromise the renderer An attacker first gains code execution or memory control in a Chrome renderer process, the source of the serialized data the advisory assumes is malicious.
- Craft a malicious text blob
The attacker builds a serialized
SkTextBlobin which a run declares a UTF-8 text oftextSizebytes but sets one or morebuf->clusters[i]entries to a value>=textSize(or a wrapped/underflowing value). - Trigger deserialization
The malformed buffer is passed to
MakeFromBufferinsrc/core/SkTextBlob.cpp, which (before the patch) accepts the out-of-range cluster indices without validation. - Out-of-bounds read
Downstream code that uses the cluster indices to index the
textSize-byte text buffer reads past its bounds, producing the out-of-bounds read the advisory reports.(inferred)The exact sink that dereferencesbuf->clusters[i]againstbuf->utf8textis not shown in the diff.
Impact Assessment
SkTextBlob buffer reaches MakeFromBuffer. The advisory classifies this as an out-of-bounds read in Skia (bug class OOB) and states no exploited-in-the-wild status, so none is claimed here. (inferred) Any use of the leaked memory for information disclosure or as a step toward further escalation goes beyond what the patch itself demonstrates.Changed Functions
| Function | Change | Notes |
|---|---|---|
forsrc/core/SkTextBlob.cpp |
modified |
Files Changed
src/core/SkTextBlob.cpp
Audit Directions
- Unvalidated deserialized indicesReview other Skia
MakeFromBuffer/reader paths where array indices or offsets (likebuf->clusters) are read from an untrusted buffer and later used to index a separately sized buffer without a bounds check. - Signed/unsigned length comparisonsLook for places that compare buffer-derived indices against a size without an explicit
uint32_t(or matching-width) cast, since underflowed or sign-mismatched values can slip past a naive check. - Cross-field size invariantsAudit structures where one deserialized field’s size (such as
textSize) constrains the valid range of another field’s contents (such as cluster or offset arrays), ensuring the dependent field is validated against the declared size on every load path.
Patch
From 9468e96cc40f6c18e62caf9a4b4a0a700fa7f24a Mon Sep 17 00:00:00 2001
From: Alexis Cruz-Ayala <alexisdavidc@google.com>
Date: Fri, 17 Jul 2026 09:55:43 -0400
Subject: [PATCH] [Security] Added checks to MakeFromBuffer for Cluster size
If a renderer is compromised, an OOB cluster index can be provided, which would then lead to an OOB memory read if it underflows / overflows.
The solution is to check each glyph within the clusters array (buf->clusters) when deserializing such taht all cluster indices are not greater than textsize. Otherwise return nullptr.
Bug: b/524864599
Change-Id: Ib30a36a42ff4509533297eabd4eadfd6168f349a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1295057
Reviewed-by: Florin Malita <fmalita@google.com>
Commit-Queue: Alexis Cruz-Ayala <alexisdavidc@google.com>
---
diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp
index 6695a39..c95badb 100644
--- a/src/core/SkTextBlob.cpp
+++ b/src/core/SkTextBlob.cpp
@@ -778,6 +778,12 @@
!reader.readByteArray(buf->utf8text, textSize)) {
return nullptr;
}
+
+ for (int i = 0; i < glyphCount; ++i) {
+ if (buf->clusters[i] >= static_cast<uint32_t>(textSize)) {
+ return nullptr;
+ }
+ }
}
}