Chrome · Passwords
CVE-2026-87624
Logic Error in Passwords
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/password_manager/chrome_password_manager_client.cc |
modified |
Files Changed
chrome/browser/password_manager/chrome_password_manager_client.ccchrome/browser/password_manager/chrome_password_manager_client_unittest.cc
Patch
From 10c3ba83d3674d727b18f64e239bda152bb32e9e Mon Sep 17 00:00:00 2001
From: Friedrich Horschig <friedrichh@chromium.org>
Date: Thu, 30 Jul 2026 07:59:30 -0700
Subject: [PATCH] [Autofill] Check driver CanShowAutofillUi before showing TTF
This CL ensures that
ChromePasswordManagerClient::ContinueShowKeyboardReplacingSurface checks
weak_driver->CanShowAutofillUi() before proceeding to show Touch to Fill
or CredMan UI and setting the keyboard replacing surface visibility.
Fixed: 533044125
Change-Id: If69b90f8fbb48aadaefbdca4800ffd99344452ae
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8174004
Reviewed-by: Ioana Treib <ioanap@chromium.org>
Commit-Queue: Friedrich Hauser <friedrichh@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1671066}
---
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 1df9f1f..7fd808b4 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -653,7 +653,7 @@
// without being called.
auto split_delay_callback =
base::SplitOnceCallback(std::move(delay_callback));
- if (!weak_driver) {
+ if (!weak_driver || !weak_driver->CanShowAutofillUi()) {
return;
}
password_manager::ContentPasswordManagerDriver* driver =
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index 220675f..738ca2fb 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -92,6 +92,7 @@
#include "content/public/common/url_constants.h"
#include "content/public/test/mock_navigation_handle.h"
#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/prerender_test_util.h"
#include "content/public/test/web_contents_tester.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
@@ -2590,4 +2591,54 @@
base::RunLoop().RunUntilIdle();
}
+TEST_F(ChromePasswordManagerClientAndroidTest,
+ DoNotShowKeyboardReplacingSurfaceForInactiveFrame) {
+ webauthn::WebAuthnCredManDelegate::override_cred_man_support_for_testing(
+ webauthn::CredManSupport::DISABLED);
+ CreateManualFillingController(web_contents());
+
+ auto* ttf_controller = MakeMockTouchToFillPasswordManagerController();
+ EXPECT_CALL(*ttf_controller, InitData).Times(0);
+ EXPECT_CALL(*ttf_controller, Show).Times(0);
+
+ constexpr char kUrl[] = "https://www.foo.com/login.html";
+ NavigateAndCommit(GURL(kUrl));
+
+ content::RenderFrameHostWrapper old_rfh(main_rfh());
+ ContentAutofillDriver* autofill_driver =
+ ContentAutofillDriver::GetForRenderFrameHost(old_rfh.get());
+ ASSERT_TRUE(autofill_driver);
+
+ std::vector<FormFieldData> fields = {CreateTestFormField(
+ "Username:", "username", "", FormControlType::kInputText, "webauthn")};
+ FormData form =
+ CreateFormDataForRenderFrameHost(*old_rfh.get(), std::move(fields));
+ {
+ autofill::TestAutofillManagerWaiter waiter(
+ autofill_driver->GetAutofillManager(),
+ {autofill::AutofillManagerEvent::kFormsSeen});
+ autofill_driver->renderer_events().FormsSeen(/*updated_forms=*/{form},
+ /*removed_forms=*/{});
+ ASSERT_TRUE(waiter.Wait(/*num_expected_relevant_events=*/1));
+ }
+
+ auto* old_driver =
+ ContentPasswordManagerDriver::GetForRenderFrameHost(old_rfh.get());
+
+ GetClient()->ShowKeyboardReplacingSurface(
+ old_driver, GetFocusedFieldSuggestionRequest(form));
+
+ // Navigate to another site so old_rfh becomes inactive.
+ NavigateAndCommit(GURL("https://www.bar.com/page2.html"));
+ ASSERT_FALSE(old_rfh.get()->IsActive());
+
+ // Passkeys arrive for the old driver/frame after page navigation.
+ ChromeWebAuthnCredentialsDelegateFactory::GetFactory(web_contents())
+ ->GetDelegateForFrame(old_rfh.get())
+ ->OnCredentialsReceived(
+ std::vector<password_manager::PasskeyCredential>(),
+ ChromeWebAuthnCredentialsDelegate::SecurityKeyOrHybridFlowAvailable(
+ true));
+}
+
#endif // BUILDFLAG(IS_ANDROID)
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index 220675f..738ca2fb 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -92,6 +92,7 @@
#include "content/public/common/url_constants.h"
#include "content/public/test/mock_navigation_handle.h"
#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/prerender_test_util.h"
#include "content/public/test/web_contents_tester.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
@@ -2590,4 +2591,54 @@
base::RunLoop().RunUntilIdle();
}
+TEST_F(ChromePasswordManagerClientAndroidTest,
+ DoNotShowKeyboardReplacingSurfaceForInactiveFrame) {
+ webauthn::WebAuthnCredManDelegate::override_cred_man_support_for_testing(
+ webauthn::CredManSupport::DISABLED);
+ CreateManualFillingController(web_contents());
+
+ auto* ttf_controller = MakeMockTouchToFillPasswordManagerController();
+ EXPECT_CALL(*ttf_controller, InitData).Times(0);
+ EXPECT_CALL(*ttf_controller, Show).Times(0);
+
+ constexpr char kUrl[] = "https://www.foo.com/login.html";
+ NavigateAndCommit(GURL(kUrl));
+
+ content::RenderFrameHostWrapper old_rfh(main_rfh());
+ ContentAutofillDriver* autofill_driver =
+ ContentAutofillDriver::GetForRenderFrameHost(old_rfh.get());
+ ASSERT_TRUE(autofill_driver);
+
+ std::vector<FormFieldData> fields = {CreateTestFormField(
+ "Username:", "username", "", FormControlType::kInputText, "webauthn")};
+ FormData form =
+ CreateFormDataForRenderFrameHost(*old_rfh.get(), std::move(fields));
+ {
+ autofill::TestAutofillManagerWaiter waiter(
+ autofill_driver->GetAutofillManager(),
+ {autofill::AutofillManagerEvent::kFormsSeen});
+ autofill_driver->renderer_events().FormsSeen(/*updated_forms=*/{form},
+ /*removed_forms=*/{});
+ ASSERT_TRUE(waiter.Wait(/*num_expected_relevant_events=*/1));
+ }
+
+ auto* old_driver =
+ ContentPasswordManagerDriver::GetForRenderFrameHost(old_rfh.get());
+
+ GetClient()->ShowKeyboardReplacingSurface(
+ old_driver, GetFocusedFieldSuggestionRequest(form));
+
+ // Navigate to another site so old_rfh becomes inactive.
+ NavigateAndCommit(GURL("https://www.bar.com/page2.html"));
+ ASSERT_FALSE(old_rfh.get()->IsActive());
+
+ // Passkeys arrive for the old driver/frame after page navigation.
+ ChromeWebAuthnCredentialsDelegateFactory::GetFactory(web_contents())
+ ->GetDelegateForFrame(old_rfh.get())
+ ->OnCredentialsReceived(
+ std::vector<password_manager::PasskeyCredential>(),
+ ChromeWebAuthnCredentialsDelegate::SecurityKeyOrHybridFlowAvailable(
+ true));
+}
+
#endif // BUILDFLAG(IS_ANDROID)
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