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 <slobodan@chromium.org>
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 <siyua@chromium.org>
Commit-Queue: Slobodan Pejic <slobodan@chromium.org>
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