High chrome UAF 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Payments
DescriptionUse after free in Payments
ComponentPayments
Bug ClassUAF
Tracker513830374
Fix commitc23114203584 (chromium/src) +36/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-08

Changed Functions

FunctionChangeNotes
if
components/autofill/core/browser/payments/payments_network_interface_base.cc
modified

Files Changed

  • components/autofill/core/browser/payments/payments_network_interface_base.cc
  • components/autofill/core/browser/payments/payments_network_interface_unittest.cc
  • components/autofill/core/browser/payments/payments_requests/payments_request.h
  • components/autofill/core/common/autofill_payments_features.cc
  • components/autofill/core/common/autofill_payments_features.h
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.