Chrome · Payments
CVE-2026-11664
UAF in Payments
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcomponents/autofill/core/browser/payments/payments_network_interface_base.cc |
modified |
Files Changed
components/autofill/core/browser/payments/payments_network_interface_base.cccomponents/autofill/core/browser/payments/payments_network_interface_unittest.cccomponents/autofill/core/browser/payments/payments_requests/payments_request.hcomponents/autofill/core/common/autofill_payments_features.cccomponents/autofill/core/common/autofill_payments_features.h
Patch
From c2311420358454efe0395cbcd82202dd2d394804 Mon Sep 17 00:00:00 2001 From: Slobodan Pejic <[email protected]> Date: Wed, 27 May 2026 07:21:59 -0700 Subject: [PATCH] Ensure request lives long enough to call RespondToDelegate Before this change, * when a PaymentsRequest::RespondToDelegate() call resulted in PaymentNetworkInterfaceBase::IssueRequest() then the request object would be destroyed during the execution of its PaymentsRequest::RespondToDelegate(). * When a PaymentsRequest::RespondToDelegate() call did not result in IssueRequest being called, then the request would be destroyed at some future point (when the PaymentNetworkInterface object is destroyed or the next IssueRequest() call). After this change, * The request object is moved to a local variable prior calling PaymentsRequest::RespondToDelegate(). * The local keeps the request alive until RespondToDelegate() finishes regardless of whether RespondToDelegate() resulted in IssueRequest() being called. * The PaymentsRequest object is immediately destroyed after RespondToDelegate() returns at the end of the local scope. Bug: 513830374 Change-Id: I4610210731f1ae9d584565b541889e09da655ce6 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7871469 Reviewed-by: Siyu An <[email protected]> Commit-Queue: Slobodan Pejic <[email protected]> Cr-Commit-Position: refs/heads/main@{#1636934} --- diff --git a/components/autofill/core/browser/payments/payments_network_interface_base.cc b/components/autofill/core/browser/payments/payments_network_interface_base.cc index 601ff99..8d9a09a 100644 --- a/components/autofill/core/browser/payments/payments_network_interface_base.cc +++ b/components/autofill/core/browser/payments/payments_network_interface_base.cc @@ -22,6 +22,7 @@ #include "components/autofill/core/browser/payments/payments_autofill_client.h" #include "components/autofill/core/browser/payments/payments_requests/payments_request.h" #include "components/autofill/core/browser/payments/payments_service_url.h" +#include "components/autofill/core/common/autofill_payments_features.h" #include "components/signin/public/base/oauth_consumer_id.h" #include "components/signin/public/identity_manager/access_token_fetcher.h" #include "components/signin/public/identity_manager/access_token_info.h" @@ -257,6 +258,17 @@ << " with data: " << data; } + if (base::FeatureList::IsEnabled( + features::kAllowReentryFromRespondToDelegate)) { + // Move the request to a local variable before invoking RespondToDelegate. + // If RespondToDelegate triggers a new request (reentry) via IssueRequest, + // `request_` will be reset, which would destroy the request during the call + // to its RespondToDelegate(). + std::unique_ptr<PaymentsRequest> local_request = std::move(request_); + local_request->RespondToDelegate(result); + return; + } + request_->RespondToDelegate(result); } @@ -284,6 +296,17 @@ simple_url_loader_.reset(); } if (request_) { + if (base::FeatureList::IsEnabled( + features::kAllowReentryFromRespondToDelegate)) { + // Move the request to a local variable before invoking RespondToDelegate. + // If RespondToDelegate triggers a new request (reentry) via IssueRequest, + // `request_` will be reset, which would destroy the request during the + // call to its RespondToDelegate(). + std::unique_ptr<PaymentsRequest> local_request = std::move(request_); + local_request->RespondToDelegate(PaymentsRpcResult::kPermanentFailure); + return; + } + request_->RespondToDelegate(PaymentsRpcResult::kPermanentFailure); } } diff --git a/components/autofill/core/browser/payments/payments_network_interface_unittest.cc b/components/autofill/core/browser/payments/payments_network_interface_unittest.cc index 4e07cee..c215511 100644 --- a/components/autofill/core/browser/payments/payments_network_interface_unittest.cc +++ b/components/autofill/core/browser/payments/payments_network_interface_unittest.cc @@ -1076,13 +1076,14 @@ secondary_account_info); StartUploading(); - ReturnResponse(payments_network_interface_.get(), net::HTTP_OK, "{}"); // Issue a token for the secondary account. identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( secondary_account_info.account_id, "secondary_account_token", AutofillClock::Now() + base::Days(10)); + ReturnResponse(payments_network_interface_.get(), net::HTTP_OK, "{}"); + // Verify the auth header. EXPECT_THAT( intercepted_headers_.GetHeader(net::HttpRequestHeaders::kAuthorization), diff --git a/components/autofill/core/browser/payments/payments_requests/payments_request.h b/components/autofill/core/browser/payments/payments_requests/payments_request.h index aaee533..eb2072a 100644 --- a/components/autofill/core/browser/payments/payments_requests/payments_request.h +++ b/components/autofill/core/browser/payments/payments_requests/payments_request.h @@ -60,6 +60,9 @@ // Invokes the appropriate callback in the delegate based on what type of // request this is. + // Note: In `PaymentsNetworkInterfaceBase`, the request object is destroyed + // immediately after this method returns. Callers must not rely on the + // request object or any of its members staying alive after this call. virtual void RespondToDelegate( PaymentsAutofillClient::PaymentsRpcResult result) = 0; diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc index b2316bb4..ed429866 100644 --- a/components/autofill/core/common/autofill_payments_features.cc +++ b/components/autofill/core/common/autofill_payments_features.cc @@ -6,6 +6,11 @@ namespace autofill::features { +// Enables the fix to allow reentry in PaymentsNetworkInterface::IssueRequest() +// from PaymentsRequest::RespondToDelegate(). +BASE_FEATURE(kAllowReentryFromRespondToDelegate, + base::FEATURE_ENABLED_BY_DEFAULT); + // When enabled, the BNPL flow acts as if the user has not yet seen the AI // terms. This allows the AI terms to be shown as bold font repeatedly for // testing purposes, regardless of the actual stored user preference. diff --git a/components/autofill/core/common/autofill_payments_features.h b/components/autofill/core/common/autofill_payments_features.h index aafb911..9418b345 100644 --- a/components/autofill/core/common/autofill_payments_features.h +++ b/components/autofill/core/common/autofill_payments_features.h @@ -15,6 +15,9 @@ // All features in alphabetical order. COMPONENT_EXPORT(AUTOFILL) +BASE_DECLARE_FEATURE(kAllowReentryFromRespondToDelegate); + +COMPONENT_EXPORT(AUTOFILL) BASE_DECLARE_FEATURE(kAutofillAiBasedAmountExtractionIgnoreSeenTermsForTesting); #if BUILDFLAG(IS_IOS) COMPONENT_EXPORT(AUTOFILL)
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/components/autofill/core/browser/payments/payments_network_interface_unittest.cc b/components/autofill/core/browser/payments/payments_network_interface_unittest.cc
index 4e07cee..c215511 100644
--- a/components/autofill/core/browser/payments/payments_network_interface_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_network_interface_unittest.cc
@@ -1076,13 +1076,14 @@
secondary_account_info);
StartUploading();
- ReturnResponse(payments_network_interface_.get(), net::HTTP_OK, "{}");
// Issue a token for the secondary account.
identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
secondary_account_info.account_id, "secondary_account_token",
AutofillClock::Now() + base::Days(10));
+ ReturnResponse(payments_network_interface_.get(), net::HTTP_OK, "{}");
+
// Verify the auth header.
EXPECT_THAT(
intercepted_headers_.GetHeader(net::HttpRequestHeaders::kAuthorization),
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page