Overview

Critical
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds read in Blink>InterestGroups
DescriptionOut of bounds read in Blink>InterestGroups
ComponentChromium
Bug ClassOOB
Tracker523677844
Fix commit8ecc3fa50aad (chromium/src) +6/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-23

Files Changed

  • content/browser/interest_group/interest_group_priority_util.cc
From 8ecc3fa50aad399fe7a40b5daba71615196cf4ec Mon Sep 17 00:00:00 2001
From: Paul Jensen <[email protected]>
Date: Mon, 15 Jun 2026 07:39:55 -0700
Subject: [PATCH] Protected Audience: Ignore non-finite interest group priorities

Non-finite values are not permitted in priority vectors submitted
via JSON or WebIDL so this likely indicates aberrant priority
calculations that went out of bounds.

Fixed: 523677844
Change-Id: Ie201fafc7b11c34b236595afc369ac74f317b128
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7941151
Reviewed-by: Russ Hamilton <[email protected]>
Auto-Submit: Paul Jensen <[email protected]>
Commit-Queue: Russ Hamilton <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1646794}
---

diff --git a/content/browser/interest_group/interest_group_priority_util.cc b/content/browser/interest_group/interest_group_priority_util.cc
index d4d40fd..e1298100 100644
--- a/content/browser/interest_group/interest_group_priority_util.cc
+++ b/content/browser/interest_group/interest_group_priority_util.cc
@@ -89,6 +89,12 @@
       break;
     }
   }
+
+  // Ignore non-finite results.
+  if (!std::isfinite(caclulated_priority)) {
+    return -1;
+  }
+
   return caclulated_priority;
 }
 
Loading diff…

Original Bug Report

reported by [email protected]

Heap Corruption via Arithmetic NaN in CalculateInterestGroupPriority

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: Arithmetic overflow in dot-product calculation can produce a NaN priority, bypassing input validation. This NaN violates Strict Weak Ordering in std::sort, leading to out-of-bounds memory access and potential heap corruption in the browser process.

Affected files:

  • content/browser/interest_group/interest_group_auction.cc
  • content/browser/interest_group/interest_group_priority_util.cc

Estimated timestamp from git blame: Unknown (Google3 checkout)

Summary

A potential vulnerability exists in the Protected Audience API’s interest group prioritization logic. An attacker can synthesize a NaN value for an interest group’s calculated priority. This NaN value bypasses existing input validation and subsequently causes a Strict Weak Ordering (SWO) violation during std::sort operations. This violation can lead to out-of-bounds memory corruption in the browser process, potentially allowing for arbitrary code execution.

Root Cause

The vulnerability stems from an arithmetic overflow in CalculateInterestGroupPriority located in content/browser/interest_group/interest_group_priority_util.cc. The function calculates a priority using a dot product:

caclulated_priority += signals_pair->second * priority_pair.second;

While the inputs in the priority_vector are checked to be finite in content/browser/interest_group/interest_group_auction.cc, the result of the multiplication can still overflow to +Infinity or -Infinity via IEEE 754 arithmetic if large finite values (e.g., 1e300) are provided. If the calculation results in both +Infinity and -Infinity for different components of the dot product, adding them together results in NaN.

The validation logic in OnBiddingSignalsReceived only validates the inputs, not the result:

// content/browser/interest_group/interest_group_auction.cc
for (const auto& [unused_signal_name, value] : priority_vector) {
  if (!std::isfinite(value)) {
    valid_priority_vector = false;
    break;
  }
}
if (valid_priority_vector) {
  new_priority = CalculateInterestGroupPriority(...);
  // Missing check for std::isfinite(*new_priority) here
  if (*new_priority < 0) { ... }
}

Because comparisons such as NaN < 0 always evaluate to false, the NaN priority is accepted and stored in state->calculated_priority.

Memory Corruption Mechanism

The NaN value is later used in InterestGroupAuction::BuyerHelper::ApplySizeLimitAndSort(), which performs the following steps:

  1. Initial Sort: SortByPriorityAndGroupByJoinOrigin() calls std::sort with a comparator that uses calculated_priority. Using NaN violates the Strict Weak Ordering requirement. In libc++, this does not crash immediately but leaves the vector in a partially unsorted state.
  2. Binary Search Failure: std::lower_bound is used to find the range of interest groups with the same minimum priority (min_priority). Because std::lower_bound assumes a strictly sorted range, operating on the unsorted array causes it to behave unpredictably. Depending on the array’s state, it can return an iterator (rand_begin) that points past the intended size_limit_ index.
  3. Vector Shrinkage: bid_states_.resize(size_limit_) shrinks the vector, moving the end() iterator down.
  4. Crossed Iterators: std::sort(rand_begin, bid_states_.end(), ...) is called. If rand_begin was calculated to be greater than the new end(), the call involves “crossed iterators” (where first > last).

In Chromium’s libc++ implementation, calling std::sort with crossed iterators results in undefined behavior. The __introsort length calculation (__last - __first) yields a negative value, which bypasses length checks and forces a fallback to __insertion_sort. The main loop in __insertion_sort (for (++__i; __i != __last; ++__i)) starts with an iterator already greater than __last. Incrementing it causes it to diverge infinitely, leading to an unbounded loop of out-of-bounds reads and writes of std::unique_ptr<BidState> elements on the heap.

Potential Attacker Steps

Note: These are suggested steps to trigger the vulnerability. We do not currently have a working proof of concept to verify execution.

  1. An attacker sets up an origin and uses navigator.joinAdInterestGroup() to join multiple interest groups (e.g., 20).
  2. The attacker configures the prioritySignalsOverrides for targeted groups with extremely large finite values (e.g., 1e300).
  3. The attacker runs an ad auction (navigator.runAdAuction()), setting perBuyerGroupLimits to a value smaller than the total number of groups (e.g., 7) to trigger ApplySizeLimitAndSort().
  4. The attacker’s trusted bidding signals server responds with a priorityVector designed to overflow when multiplied with the overrides (producing +Infinity and -Infinity, summing to NaN).
  5. The auction logic accepts the NaN priority.
  6. When ApplySizeLimitAndSort() executes, the NaN breaks the initial sort, causes lower_bound to return an out-of-bounds iterator, and triggers the crossed-iterator std::sort.
  7. By grooming the browser process heap, the attacker could theoretically control the out-of-bounds unique_ptrs that are shifted by __insertion_sort, achieving arbitrary object injection or Use-After-Free.

Impact

The impact is high-privilege memory corruption in the browser process. An attacker triggering this could potentially achieve a full sandbox escape and Remote Code Execution (RCE).

Suggested Fix

The return value of CalculateInterestGroupPriority must be validated to ensure it is finite before it is used or stored.

// In InterestGroupAuction::BuyerHelper::OnBiddingSignalsReceived
new_priority = CalculateInterestGroupPriority(...);
if (new_priority.has_value() && !std::isfinite(*new_priority)) {
  new_priority = std::nullopt; 
}

Evaluated with Chrome root at commit: 65b3256311f3ab6fb9870eaa522de7e6dd2663bb


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker