Critical watch CVSS 8.8 chrome OOB 📄 Reporter bug report 🔧 Commit mapped

Overview

Critical
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds read in Blink>InterestGroups
DescriptionOut of bounds read in Blink>InterestGroups
ComponentChromium
Bug ClassOOB
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker523677844
Fix commit8ecc3fa50aad (chromium/src) +6/-0
CWECWE-125, CWE-787 (Out-of-bounds read, Out-of-bounds write)
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-06-13 (per vendor advisory)
Fix landed2026-06-15 (public git)
Disclosed2026-06-23
Patch gap8 days — fix public in source before the advisory

Background

Protected Audience (FLEDGE) Interest Groups
a Blink/content browser feature (Blink>InterestGroups) that stores advertiser-defined bidding groups whose members carry a computed priority used to select which groups participate in an on-device ad auction.
Priority vector
a set of named signal-to-weight entries submitted by a site via JSON or WebIDL that is combined with signals to produce a single scalar priority for an interest group.
`std::isfinite`
a C++ standard-library predicate that returns false for NaN and +/-inf, used by the patch to detect a priority that has escaped the normal numeric range.

Root Cause Analysis

The patched function computes a scalar priority (caclulated_priority) by iterating over priority-vector entries in a loop (the break and closing braces at the top of the hunk are the tail of that loop) and then, pre-patch, returned that value directly with return caclulated_priority;. Because the accumulation could produce a non-finite result (NaN or infinity) from an aberrant priority calculation, the function could hand a non-finite priority back to its caller, and per the commit message such non-finite values “are not permitted in priority vectors submitted via JSON or WebIDL,” so their appearance signals a calculation that “went out of bounds.” The advisory classifies the resulting condition as an out-of-bounds read in Blink>InterestGroups, meaning downstream consumers of this priority evidently mishandle the non-finite value in a way that reads past a valid range (inferred).

The fix adds if (!std::isfinite(caclulated_priority)) { return -1; } immediately before the final return, so any NaN/infinity is replaced with a fixed sentinel (-1) and never propagates. This works because the guard runs before the value leaves the function, normalizing out-of-range results into an in-range value.

Key insight
The single mistake was returning an unvalidated computed priority that could be NaN or infinite; the fix intercepts any non-finite result with std::isfinite and substitutes a bounded sentinel (-1) so the aberrant value never reaches downstream priority handling.

Attack Path

  1. Submit or induce an aberrant priority configuration A site registers a Protected Audience interest group with priority-vector/signal inputs that drive the in-browser priority computation toward a non-finite result (e.g., values whose combination overflows to infinity or produces NaN).
  2. Trigger the priority calculation During interest-group processing the vulnerable function iterates its priority entries and computes caclulated_priority, which becomes non-finite and, pre-patch, is returned unchanged via return caclulated_priority;.
  3. Non-finite priority propagates to a consumer The caller uses the non-finite priority for downstream selection/ordering logic that assumes a finite, in-range value.
  4. Out-of-bounds read in a downstream consumer A comparison, index, or bucketing step that treats the non-finite priority as valid reads outside the intended bounds, matching the advisory’s “out of bounds read” classification. (inferred) — the diff does not contain the sink, index, offset, or object involved.

Impact Assessment

An attacker who controls a page’s Protected Audience interest-group inputs can cause an out-of-bounds read within the browser process’s Blink>InterestGroups handling, with the precondition that the site can register interest groups and trigger the priority computation. The concrete gain from the OOB read — information disclosure of adjacent memory or a step toward a stronger primitive — is (inferred), as the diff shows only the non-finite priority origin and not the read itself. The vendor advisory rates this critical severity and does not report in-the-wild exploitation; NVD scores it CVSS 8.8. Any escalation beyond the reported OOB read is (inferred).

Files Changed

  • content/browser/interest_group/interest_group_priority_util.cc

Audit Directions

  • Unvalidated computed floating-point results
    Search content/browser/interest_group/ and other Protected Audience math for scalar values returned from accumulation/loop logic that lack a std::isfinite/NaN/infinity check before being consumed as indices, comparators, or sort keys.
  • Attacker-influenced numeric inputs from JSON/WebIDL
    Audit paths where site-supplied priority vectors, signals, or weights feed arithmetic, since inputs “not permitted” by the spec may still arrive through aberrant intermediate calculations rather than direct submission.
  • Downstream consumers of priority/score values
    Review callers that use these priorities for ordering, selection, or array/bucket indexing to confirm they defend against out-of-range or sentinel values rather than assuming a finite, valid range.
From 8ecc3fa50aad399fe7a40b5daba71615196cf4ec Mon Sep 17 00:00:00 2001
From: Paul Jensen <pauljensen@chromium.org>
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 <behamilton@google.com>
Auto-Submit: Paul Jensen <pauljensen@chromium.org>
Commit-Queue: Russ Hamilton <behamilton@google.com>
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 rj...@google.com

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