Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds read in Blink
DescriptionOut of bounds read in Blink
ComponentBlink
Bug ClassOOB
Tracker491080830
Fix commita8e675e9a315 (chromium/src) +1/-1
CISA KEVNot listed
Creditedheapracer (@heapracer)
Disclosed2026-03-18

Files Changed

  • third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.cc
From a8e675e9a31596b695262732746738e475ccac9a Mon Sep 17 00:00:00 2001
From: Dominik Röttsches <drott@chromium.org>
Date: Wed, 11 Mar 2026 03:04:10 -0700
Subject: [PATCH] Fix U16_FWD_1 increment boundary in CaseMappingHarfBuzzBufferFiller

To work safely, the macro needs the boundary to be greater than the
start index at the time of use. If this precondition is not met, an
overrun can occur. Fix this by using the correct buffer boundary.

Fixed: 491080830
Change-Id: I707b95ccdc8294f722d627b9255540309b03a572
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7655112
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Auto-Submit: Dominik Röttsches <drott@chromium.org>
Commit-Queue: Dominik Röttsches <drott@chromium.org>
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1597614}
---

diff --git a/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.cc b/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.cc
index 44db6ba..bf936ea 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.cc
@@ -77,7 +77,7 @@
   for (unsigned char_index = start_index;
        char_index < start_index + num_characters;) {
     unsigned new_char_index = char_index;
-    UNSAFE_TODO(U16_FWD_1(buffer.data(), new_char_index, num_characters));
+    UNSAFE_TODO(U16_FWD_1(buffer.data(), new_char_index, buffer.size()));
     String char_by_char(
         buffer.subspan(char_index, new_char_index - char_index));
     String case_mapped_char;
Loading diff…

Original Bug Report

reported by sh...@gmail.com

Heap OOB read in Blink text shaping

VULNERABILITY DETAILS

A heap-buffer-overflow read occurs in Blink text shaping when CaseMappingHarfBuzzBufferFiller::FillSlowCase() advances UTF-16 with a wrong bound argument.

In third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.cc:80, the code calls:

U16_FWD_1(buffer.data(), new_char_index, num_characters);

inside a loop whose index is absolute (char_index starts at start_index, not 0).

When shaping a non-zero subrange (start_index > 0) that includes a trailing unpaired lead surrogate, U16_FWD_1 can read one UTF-16 code unit past the end of the backing buffer.

This vulnerability leads to limited OOB read adjacent to UTF-16 buffer end under attacker-influenced text/range shaping state.

VERSION

  • Chrome Version: Chromium 147.0.7725.0 (asan-debug, dev)
  • Operating System: Ubuntu 24.04.3 LTS (Noble Numbat), Linux kernel 6.14.0-37-generic (x86_64)

REPRODUCTION CASE

Attached minimal single-file PoC:

  • crash_20260309_050715_agent21_min.html

Opening it in Chrome would lead to the crash.

FOR CRASHES, ADDITIONAL INFORMATION

  • Type of crash: Renderer process crash (tab crash)
  • Crash state: ASAN heap-buffer-overflow (READ of size 2)
    • Top frame:
      • blink::CaseMappingHarfBuzzBufferFiller::FillSlowCase(...)
      • third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.cc:80:17
    • Call path includes:
      • CaseMappingHarfBuzzBufferFiller::CaseMappingHarfBuzzBufferFiller(...)
      • HarfBuzzShaper::ShapeSegment(...)
    • Full symbolized ASAN trace attached:
      • agent21_min_run6.stderr.log

CREDIT INFORMATION

  • Reporter credit: heapracer

Detailed root cause

Vulnerable location

  • third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.cc:66-101
  • Faulting statement at :80

Fault pattern

The loop is driven in absolute text coordinates:

for (unsigned char_index = start_index;
     char_index < start_index + num_characters;) {
  unsigned new_char_index = char_index;
  U16_FWD_1(buffer.data(), new_char_index, num_characters);
  ...
}

U16_FWD_1 expects a length bound in the same coordinate space as the index. Here, new_char_index is absolute but the supplied bound is only num_characters (subrange length), not start_index + num_characters (subrange end in absolute coordinates).

With start_index > 0, this mismatch allows out-of-range access during surrogate-pair probing at range boundaries.

View on issue tracker