CVE-2026-13033
Overview
Files Changed
content/browser/interest_group/interest_group_priority_util.cc
Patch
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;
}
Original Bug Report
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.cccontent/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:
- Initial Sort:
SortByPriorityAndGroupByJoinOrigin()callsstd::sortwith a comparator that usescalculated_priority. UsingNaNviolates the Strict Weak Ordering requirement. In libc++, this does not crash immediately but leaves the vector in a partially unsorted state. - Binary Search Failure:
std::lower_boundis used to find the range of interest groups with the same minimum priority (min_priority). Becausestd::lower_boundassumes 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 intendedsize_limit_index. - Vector Shrinkage:
bid_states_.resize(size_limit_)shrinks the vector, moving theend()iterator down. - Crossed Iterators:
std::sort(rand_begin, bid_states_.end(), ...)is called. Ifrand_beginwas calculated to be greater than the newend(), the call involves “crossed iterators” (wherefirst > 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.
- An attacker sets up an origin and uses
navigator.joinAdInterestGroup()to join multiple interest groups (e.g., 20). - The attacker configures the
prioritySignalsOverridesfor targeted groups with extremely large finite values (e.g.,1e300). - The attacker runs an ad auction (
navigator.runAdAuction()), settingperBuyerGroupLimitsto a value smaller than the total number of groups (e.g., 7) to triggerApplySizeLimitAndSort(). - The attacker’s trusted bidding signals server responds with a
priorityVectordesigned to overflow when multiplied with the overrides (producing+Infinityand-Infinity, summing toNaN). - The auction logic accepts the
NaNpriority. - When
ApplySizeLimitAndSort()executes, theNaNbreaks the initial sort, causeslower_boundto return an out-of-bounds iterator, and triggers the crossed-iteratorstd::sort. - 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.