SkFontPriv::CountTextElements return type change (int → size_t) causes massive allocation on malformed UTF input

18 views
Skip to first unread message

mattl...@live.com

unread,
Apr 26, 2026, 6:14:16 PM (10 days ago) Apr 26
to skia-discuss

Title: SkFontPriv::CountTextElements return type change (int → size_t) causes massive allocation on malformed UTF input

Component: Skia > Text

Severity: High (potential OOM / crash)

Introduced in: 7e2374fa4053 "Use size_t for font/typeface return values (to match span's)"


Description

In commit 7e2374fa4053SkFontPriv::CountTextElements was changed from returning int to size_t, and SkFont::countText was changed similarly. However, the underlying SkUTF::CountUTF8 and SkUTF::CountUTF16 still return int and still use -1 as an error sentinel for malformed input.

In CountTextElements, the int return value from CountUTF8/CountUTF16 is implicitly converted to size_ton line 383 and line 385. When the input is malformed UTF, -1 becomes ~0ULL (18446744073709551615), and callers have no way to distinguish an error from a valid count.

Reproduction path

Pass malformed UTF-8 (e.g., an invalid lead byte or truncated sequence) to any API that goes through countText:

SkFont font; const char bad[] = { '\xC0' }; // invalid UTF-8 lead byte size_t count = font.countText(bad, 1, SkTextEncoding::kUTF8); // count is now ~0ULL (SIZE_MAX)

Impact

SkAutoToGlyphs calls font.countText() on line 99 and uses the result to allocate storage on line 100:

const size_t count = font.countText(text, length, encoding); fStorage.reset(count); // tries to allocate SIZE_MAX elements → OOM

This affects measureTextgetWidthsgetBoundsgetPos, and any other API that uses SkAutoToGlyphsinternally.

SkTypeface::textToGlyphs also calls CountTextElements on line 421 and uses the result for a size comparison and potential memcpy.

Suggested fix

Have CountTextElements guard against the signed→unsigned conversion:

size_t SkFontPriv::CountTextElements(const void* text, size_t byteLength, SkTextEncoding encoding) { switch (encoding) { case SkTextEncoding::kUTF8: { int n = SkUTF::CountUTF8(reinterpret_cast<const char*>(text), byteLength); return n < 0 ? 0 : static_cast<size_t>(n); } case SkTextEncoding::kUTF16: { int n = SkUTF::CountUTF16(reinterpret_cast<const uint16_t*>(text), byteLength); return n < 0 ? 0 : static_cast<size_t>(n); } case SkTextEncoding::kUTF32: return byteLength >> 2; case SkTextEncoding::kGlyphID: return byteLength >> 1; } SkASSERT(false); return 0; }


Reply all
Reply to author
Forward
0 new messages