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 <drott@chromium.org>
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
R=behdad@chromium.org,bungeman@chromium.org,drott@chromium.org,jshin@chromium.org,kojii@chromium.org
Fixed: 496302307
Change-Id: Ifb38a46c9e129cf5701559e112032e77839405f7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7705953
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
Commit-Queue: Dominik Röttsches <drott@chromium.org>
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.