CVE-2026-8577
Overview
Background
- HarfBuzz / GPOS cursive attachment
- HarfBuzz is the OpenType text-shaping engine bundled in Chromium (
third_party/harfbuzz-ng). The GPOSCursivePosFormat1lookup attaches glyphs cursively, chaining a child glyph to a parent via an attachment chain. - `attach_chain()` field
- A signed 16-bit (
int16_t) field onhb_glyph_position_tstoring the relative index (distance) from one glyph to the one it attaches to. - `reverse_cursive_minor_offset()`
- The routine that reverses attachment-chain direction for RTL by negating chain values (
attach_chain() = -chain). - DEPS roll
- Chromium pulls third-party fixes by bumping a pinned revision in
DEPS; the actual source fix lives in the HarfBuzz repository, not inchromium/src.
Root Cause Analysis
The advisory labels this an integer overflow in Fonts (CWE-472). The underlying defect, per the Project Fortify report, is a negation overflow of an int16_t in HarfBuzz’s cursive-attachment logic: when an attachment-chain distance is exactly INT16_MIN (-32768), it survives the truncation guard in CursivePosFormat1::apply because -32768 fits in int16_t. Later, reverse_cursive_minor_offset() computes pos[j].attach_chain() = -chain; since -(-32768) == 32768 cannot be represented in int16_t, it wraps back to -32768, corrupting the chain. Subsequent traversal computes j = (int)i + chain, and with chain == -32768 the index becomes a huge unsigned value (e.g. 4294934528) used for reads/writes into pos, i.e. a massive out-of-bounds access escalating from the integer-overflow root cause.
Important grounding caveat: the supplied chromium/src diff (8047bc40) is only the DEPS roll of third_party/harfbuzz-ng/src from 6f4c5cec to 4fc96139 (27 commits), updating DEPS, README.chromium (version 13.2.1-0 → 13.2.1-28), and the submodule pointer. The actual memory-safety fix is not shown by this diff; it is one of the rolled HarfBuzz commits, identified in the roll’s git log as "[gpos] Guard cursive attachment reversal" (behdad, 2026-03-26). The roll message states it “Contains improvements for security issue reported in crbug.com/496302307.” The mechanism above is therefore (inferred) from the report and corroborated by the commit-log subject line; the exact guard (reject INT16_MIN and/or bounds-check j in reverse_cursive_minor_offset(), as the report suggests) is not visible in this preview.
int16_t attachment-chain distance of INT16_MIN overflows to INT16_MIN again, corrupting the cursive chain so that traversal uses a wrapped, out-of-range index into the glyph-positions array — an integer-overflow bug that escalates to an out-of-bounds read/write.Attack Path
- Serve a malicious web font
Attacker embeds an OpenType font with a crafted GPOS
CursivePosFormat1lookup plus text producing a HarfBuzz buffer of ≥32,769 glyphs. (from report) - Plant the `INT16_MIN` chain
An LTR cursive lookup links glyph
0to glyph32768, storing distance-32768inattach_chain(), which passes the truncation guard. (from report; not shown by diff) - Reverse the chain
An RTL lookup triggers
reverse_cursive_minor_offset(), negating-32768and overflowing back to-32768, corrupting the chain. (from report) - Trigger OOB traversal
Re-traversal computes
j = i + chainwith the corrupted value, yielding a wrapped out-of-range index intoposused for reads/writes. (from report) - Platform-dependent primitive On 64-bit the offset (~85.8 GB) hits unmapped memory → renderer crash/DoS; on 32-bit the pointer arithmetic wraps to a predictable ~640 KB heap underflow, giving an OOB write primitive. (from report; freed-memory/escalation inferred)
- Escalation The 32-bit OOB write is used to corrupt adjacent metadata/vtables toward renderer RCE. (inferred, not shown by the diff)
Impact Assessment
pos array is a raw pointer not covered by MiraclePtr (per report), removing that mitigation. Precondition: the page loads and shapes attacker-controlled font+text. Note the fix reaches Chromium only via the HarfBuzz roll; the enforcing code is in the upstream “[gpos] Guard cursive attachment reversal” commit, not in this chromium/src diff.Files Changed
DEPSthird_party/harfbuzz-ng/README.chromiumthird_party/harfbuzz-ng/src
Audit Directions
- Signed-min negationAudit other HarfBuzz (and Blink/Skia font) code that negates signed fields (
int16_t/int8_t) for theINT_MINnegation-overflow pattern where-xis unrepresentable. - Chain/index traversal boundsReview cursive and mark-attachment traversals (
propagate_attachment_offsets, GPOS/GSUB chain walks) to ensure computed indices are bounds-checked againstbuffer->lenbefore array access. - 32-bit pointer arithmeticGiven the 32-bit heap-underflow amplification, review size/offset computations on glyph arrays for truncation/wrap differences between 32- and 64-bit builds.
- Roll contentsVerify no other security-relevant commit in the
6f4c5cec..4fc96139range (e.g. “[buffer] Bounds-check in add_utf()”, “[glyf] Reject simple glyphs with too few points”) maps to a separate advisory.
Patch
From 8047bc4000b040350d0532e0e99e1c13f550e0a3 Mon Sep 17 00:00:00 2001 From: Dominik Röttsches <[email protected]> Date: Fri, 27 Mar 2026 03:56:56 -0700 Subject: [PATCH] Roll src/third_party/harfbuzz-ng/src/ 6f4c5cec3..4fc961392 (27 commits) Contains improvements for security issue reported in crbug.com/496302307. https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git/+log/6f4c5cec306d..4fc96139259e $ git log 6f4c5cec3..4fc961392 --date=short --no-merges --format='%ad %ae %s' 2026-03-26 behdad [AGENTS.md] Allow Co-Authored-By 2026-03-26 behdad [vector] Use clear() instead of resize(0)/shrink(0) (#5876) 2026-03-26 behdad [raster] Guard direct SVG paint callbacks 2026-03-26 behdad [vector] Destruct items in clear() when vector is in error state 2026-03-26 behdad [gpos] Guard cursive attachment reversal 2026-03-26 behdad [raster] Increase max image buffer size 2026-03-25 behdad Remove two redundant conditionals 2026-03-24 behdad [glyf] Reject simple glyphs with too few points 2026-03-24 behdad [font] Fix typo in setting design coords (#5871) 2026-03-24 behdad [font] Avoid div-by-zero in parent scale 2026-03-24 behdad [font] Avoid creating a cycle in set_parent() 2026-03-24 behdad [zlib] Remove unused macro 2026-03-24 behdad Ignore -Warray-bounds 2026-03-23 behdad [vector] Fix clear() when vector is in error state 2026-03-23 behdad [raster] Use png_jmpbuf in PNG error callback (#5868) 2026-03-23 49699333+dependabot[bot] Bump hendrikmuhs/ccache-action from 1.2.20 to 1.2.21 2026-03-23 49699333+dependabot[bot] Bump github/codeql-action from 4.33.0 to 4.34.1 2026-03-22 behdad [buffer] Bounds-check in add_utf() (#5864) 2026-03-21 62257866+dejanyy [ot-font] Skip adjusting offsets when all origin values are zero (#5863) 2026-03-21 62257866+dejanyy Fix for issue #5847 (#5862) 2026-03-20 behdad Add a fuzzer test 2026-03-19 behdad Revert "[rust] Use design coords if available, to set on the Rust side" 2026-03-19 behdad [rust] Use design coords if available, to set on the Rust side 2026-03-19 behdad [vector] Reject errored SVG paint bodies 2026-03-19 behdad [vector] Fail closed on SVG paint body OOM 2026-03-19 behdad Add a fuzzer font 2026-03-19 behdad [docs] Note failing-malloc in fuzzing guidance Created with: roll-dep src/third_party/harfbuzz-ng/src [email protected],[email protected],[email protected],[email protected],[email protected] Fixed: 496302307 Change-Id: Ifb38a46c9e129cf5701559e112032e77839405f7 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7705953 Reviewed-by: Steinar H Gunderson <[email protected]> Commit-Queue: Dominik Röttsches <[email protected]> Cr-Commit-Position: refs/heads/main@{#1606081} --- diff --git a/DEPS b/DEPS index c140d42..b6a147aa4 100644 --- a/DEPS +++ b/DEPS @@ -363,7 +363,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling HarfBuzz # and whatever else without interference from each other. - 'harfbuzz_revision': '6f4c5cec306d31e6822303f5ba248a14293d588e', + 'harfbuzz_revision': '4fc96139259ebc35f40118e0382ac8037d928e5c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Emoji Segmenter # and whatever else without interference from each other. diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium index b1b4c66..976a591 100644 --- a/third_party/harfbuzz-ng/README.chromium +++ b/third_party/harfbuzz-ng/README.chromium @@ -1,10 +1,10 @@ Name: harfbuzz-ng Short Name: harfbuzz-ng URL: http://harfbuzz.org -Version: 13.2.1-0 +Version: 13.2.1-28 CPEPrefix: cpe:/a:harfbuzz_project:harfbuzz:13.2.1 -Date: 2026-03-19 -Revision: 6f4c5cec306d31e6822303f5ba248a14293d588e +Date: 2026-03-27 +Revision: 4fc96139259ebc35f40118e0382ac8037d928e5c Update Mechanism: Manual Security Critical: yes Shipped: yes diff --git a/third_party/harfbuzz-ng/src b/third_party/harfbuzz-ng/src index 6f4c5ce..4fc9613 160000 --- a/third_party/harfbuzz-ng/src +++ b/third_party/harfbuzz-ng/src @@ -1 +1 @@ -Subproject commit 6f4c5cec306d31e6822303f5ba248a14293d588e +Subproject commit 4fc96139259ebc35f40118e0382ac8037d928e5c
Original Bug Report
Potential negation overflow in HarfBuzz reverse_cursive_minor_offset leading to OOB access
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A negation overflow in HarfBuzz’s cursive attachment logic occurs when an attachment chain distance is exactly INT16_MIN (-32768). This bypasses truncation checks and leads to a massive out-of-bounds array access when the chain is reversed. On 32-bit platforms, this wraps to a predictable negative offset, potentially allowing reliable heap underflow writes and renderer RCE.
Affected files:
third_party/harfbuzz-ng/src/src/OT/Layout/GPOS/CursivePosFormat1.hh
Estimated timestamp from git blame: 2025-11-03
Summary
A potential vulnerability exists in HarfBuzz’s OpenType Layout engine (third_party/harfbuzz-ng/src/src/OT/Layout/GPOS/CursivePosFormat1.hh). A negation overflow can corrupt a glyph’s attachment chain, leading to out-of-bounds (OOB) memory accesses.
Because HarfBuzz processes untrusted web fonts inside the renderer process, and the affected array (hb_glyph_position_t *pos) is a raw C-style pointer unprotected by MiraclePtr (BackupRefPtr), this bug can be weaponized. On 64-bit systems, it causes a deterministic crash (Denial of Service). On 32-bit systems (like Chrome on 32-bit Android), integer wrapping turns this into a reliable heap underflow write, providing a strong primitive for Remote Code Execution (RCE).
Root Cause Analysis
The vulnerability stems from the handling of the 16-bit signed integer attach_chain().
- The Bypass: In
CursivePosFormat1::apply, the distance between a parent and child glyph is calculated and stored. If a Left-to-Right layout attaches a parent at index0to a child at index32768, the distance is0 - 32768 = -32768. Because-32768(INT16_MIN) perfectly fits into the signed 16-bitattach_chain()field, it survives the truncation guard check (pos[child].attach_chain() != (int) parent - (int) child) at line 234. - The Negation Overflow: When a subsequent Right-to-Left lookup forces HarfBuzz to reverse the attachment tree, it calls
reverse_cursive_minor_offset(). At line 75, it attempts to invert the chain direction:pos[j].attach_chain() = -chain;. Sincechainis-32768,-chainbecomes32768. Assigning32768to theint16_tfield overflows the maximum positive value (32767), wrapping back to-32768. - The OOB Access: The chain is now corrupted. When the engine traverses this chain again, it calculates the next index at line 62:
unsigned int j = (int) i + chain;. Ifi = 0andchain = -32768,jbecomes-32768, which casts to the unsigned integer4294934528. This massive index is then used for recursive reads and writes, such aspos[j].attach_chain() = -chain.
Platform Specific Impact
- 64-bit Platforms: The array element size is 20 bytes.
4294934528 * 20bytes results in an offset of approximately 85.8 GB. Accessing this address deterministically hits unmapped memory, crashing the renderer (DoS). - 32-bit Platforms: The pointer arithmetic (
4294934528 * 20) is constrained to 32 bits. Mathematically,0xFFFF8000 * 0x14wraps to0xFFF60000. Interpreted as a signed 32-bit offset, this is exactly-655360bytes. This creates a highly predictable640 KBheap underflow.
Suggested Attacker Steps
(Note: These are potential steps based on static analysis, as our setup does not run live exploit code.)
- Create a web page embedding a malicious OpenType font and a text string that generates a HarfBuzz buffer of at least 32,769 glyphs.
- Groom the 32-bit renderer heap such that attacker-controlled data is placed exactly 640 KB before the target HarfBuzz buffer allocation. Ensure this memory contains specific bytes to pass the early-return check (
type & ATTACH_TYPE_CURSIVE). - Trigger a Left-to-Right
CursivePosFormat1lookup linking glyph0and glyph32768to plant the-32768chain value. - Trigger a Right-to-Left
CursivePosFormat1lookup to reverse the chain, triggering the negation overflow and corrupting the chain. - Trigger another lookup to traverse the corrupted chain. The engine will perform an OOB read at the groomed
-640 KBoffset, followed by OOB writes (e.g., writing0and negated offsets) to that same location. - Use these OOB write primitives to corrupt adjacent object metadata or vtables, leading to renderer RCE.
Suggested Fix
- Reject INT16_MIN: In
CursivePosFormat1::apply, explicitly reject assignments where the calculated distance is-32768(since its negation cannot be represented in 16 bits). - Bounds Checking: Add an explicit bounds check inside
reverse_cursive_minor_offset()right after computingj = (int) i + chainto verify thatj < buffer->lenbefore performing any recursive calls or array accesses. (Similar to the checks inpropagate_attachment_offsets()).
Evaluated with Chrome root at commit: a3f5fcb392f2902650ca2b71820e7e418787e18b
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.