Medium CVSS 8.8 chrome Integer Overflow 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInteger overflow in Fonts
DescriptionInteger overflow in Fonts
ComponentFonts
Bug ClassInteger Overflow
Tracker496302307
Fix commit4fc96139259e (harfbuzz) +5/-5
CWECWE-472
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-03-25 (per vendor advisory)
Disclosed2026-05-12

Background

HarfBuzz / GPOS cursive attachment
HarfBuzz is the OpenType text-shaping engine bundled in Chromium (third_party/harfbuzz-ng). The GPOS CursivePosFormat1 lookup attaches glyphs cursively, chaining a child glyph to a parent via an attachment chain.
`attach_chain()` field
A signed 16-bit (int16_t) field on hb_glyph_position_t storing 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 in chromium/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.

Key insight
Negating an 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

  1. Serve a malicious web font Attacker embeds an OpenType font with a crafted GPOS CursivePosFormat1 lookup plus text producing a HarfBuzz buffer of ≥32,769 glyphs. (from report)
  2. Plant the `INT16_MIN` chain An LTR cursive lookup links glyph 0 to glyph 32768, storing distance -32768 in attach_chain(), which passes the truncation guard. (from report; not shown by diff)
  3. Reverse the chain An RTL lookup triggers reverse_cursive_minor_offset(), negating -32768 and overflowing back to -32768, corrupting the chain. (from report)
  4. Trigger OOB traversal Re-traversal computes j = i + chain with the corrupted value, yielding a wrapped out-of-range index into pos used for reads/writes. (from report)
  5. 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)
  6. Escalation The 32-bit OOB write is used to corrupt adjacent metadata/vtables toward renderer RCE. (inferred, not shown by the diff)

Impact Assessment

Renderer process (HarfBuzz shapes untrusted web fonts there), web-reachable with no special privilege (UI:R, CVSS 8.8 H/H/H). On 64-bit platforms the realistic primitive is a deterministic OOB access causing a renderer crash (DoS); on 32-bit platforms (e.g. 32-bit Android) the integer wrap yields a predictable heap underflow write, a strong primitive that could be escalated to RCE (escalation inferred, not demonstrated by the diff). The affected 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

  • DEPS
  • third_party/harfbuzz-ng/README.chromium
  • third_party/harfbuzz-ng/src

Audit Directions

  • Signed-min negation
    Audit other HarfBuzz (and Blink/Skia font) code that negates signed fields (int16_t/int8_t) for the INT_MIN negation-overflow pattern where -x is unrepresentable.
  • Chain/index traversal bounds
    Review cursive and mark-attachment traversals (propagate_attachment_offsets, GPOS/GSUB chain walks) to ensure computed indices are bounds-checked against buffer->len before array access.
  • 32-bit pointer arithmetic
    Given the 32-bit heap-underflow amplification, review size/offset computations on glyph arrays for truncation/wrap differences between 32- and 64-bit builds.
  • Roll contents
    Verify no other security-relevant commit in the 6f4c5cec..4fc96139 range (e.g. “[buffer] Bounds-check in add_utf()”, “[glyf] Reject simple glyphs with too few points”) maps to a separate advisory.
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
Loading diff…

Original Bug Report

reported by [email protected]

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().

  1. 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 index 0 to a child at index 32768, the distance is 0 - 32768 = -32768. Because -32768 (INT16_MIN) perfectly fits into the signed 16-bit attach_chain() field, it survives the truncation guard check (pos[child].attach_chain() != (int) parent - (int) child) at line 234.
  2. 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;. Since chain is -32768, -chain becomes 32768. Assigning 32768 to the int16_t field overflows the maximum positive value (32767), wrapping back to -32768.
  3. 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;. If i = 0 and chain = -32768, j becomes -32768, which casts to the unsigned integer 4294934528. This massive index is then used for recursive reads and writes, such as pos[j].attach_chain() = -chain.

Platform Specific Impact

  • 64-bit Platforms: The array element size is 20 bytes. 4294934528 * 20 bytes 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 * 0x14 wraps to 0xFFF60000. Interpreted as a signed 32-bit offset, this is exactly -655360 bytes. This creates a highly predictable 640 KB heap underflow.

Suggested Attacker Steps

(Note: These are potential steps based on static analysis, as our setup does not run live exploit code.)

  1. Create a web page embedding a malicious OpenType font and a text string that generates a HarfBuzz buffer of at least 32,769 glyphs.
  2. 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).
  3. Trigger a Left-to-Right CursivePosFormat1 lookup linking glyph 0 and glyph 32768 to plant the -32768 chain value.
  4. Trigger a Right-to-Left CursivePosFormat1 lookup to reverse the chain, triggering the negation overflow and corrupting the chain.
  5. Trigger another lookup to traverse the corrupted chain. The engine will perform an OOB read at the groomed -640 KB offset, followed by OOB writes (e.g., writing 0 and negated offsets) to that same location.
  6. Use these OOB write primitives to corrupt adjacent object metadata or vtables, leading to renderer RCE.

Suggested Fix

  1. Reject INT16_MIN: In CursivePosFormat1::apply, explicitly reject assignments where the calculated distance is -32768 (since its negation cannot be represented in 16 bits).
  2. Bounds Checking: Add an explicit bounds check inside reverse_cursive_minor_offset() right after computing j = (int) i + chain to verify that j < buffer->len before performing any recursive calls or array accesses. (Similar to the checks in propagate_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.

View on issue tracker