Medium chrome Memory Corruption 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMemory corruption in Transactions Platform
DescriptionMemory corruption in Transactions Platform
ComponentTransactions Platform
Sub-componentAutofill
Bug ClassMemory Corruption
Tracker563089510
Fix commitbf1633dbb56c (chromium/src) +373/-146
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-20

Changed Functions

FunctionChangeNotes
if
chrome/browser/ui/autofill/at_memory_suggestion_controller.cc
modified
if
chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
modified
GetAnchorFrameToken
chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
modified
if
chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
modified
switch
components/autofill/core/browser/ui/autofill_external_delegate.cc
modified

Files Changed

  • chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.h
  • components/autofill/core/browser/foundations/autofill_client.cc
  • chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc
  • chrome/browser/ui/autofill/popup_controller_common.h
  • chrome/browser/ui/autofill/chrome_autofill_client.cc
  • components/autofill/core/browser/ui/autofill_external_delegate_unittest.cc
  • chrome/browser/ui/autofill/autofill_popup_controller_impl_mac_unittest.mm
  • components/autofill/ios/browser/autofill_agent_unittest.mm
  • components/autofill/core/browser/ui/autofill_external_delegate.h
  • chrome/browser/ui/autofill/autofill_suggestion_controller.cc
  • components/autofill/core/browser/ui/payments/omnibox_autofill_delegate_unittest.cc
  • components/autofill/core/browser/ui/mock_autofill_suggestion_delegate.h
  • chrome/browser/ui/autofill/popup_controller_common.cc
  • components/autofill/core/browser/ui/test_autofill_external_delegate.cc
  • chrome/browser/ui/android/autofill/at_memory_bottom_sheet_bridge_unittest.cc
  • components/password_manager/core/browser/password_autofill_manager.h
  • chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.cc
  • components/password_manager/core/browser/password_manual_fallback_flow.h
  • components/autofill/core/browser/ui/autofill_external_delegate.cc
  • components/autofill/core/browser/foundations/autofill_client.h
  • chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
  • chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
  • chrome/browser/ui/autofill/at_memory_suggestion_controller_unittest.cc
  • components/password_manager/core/browser/password_autofill_manager.cc
  • components/autofill/core/browser/ui/payments/omnibox_autofill_delegate.h
  • chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc
  • components/autofill/core/browser/ui/payments/omnibox_autofill_delegate.cc
  • chrome/browser/ui/autofill/autofill_popup_controller_impl_unittest.cc
  • components/autofill/core/browser/ui/test_autofill_external_delegate.h
  • chrome/browser/ui/autofill/at_memory_suggestion_controller.cc
  • components/autofill/core/browser/ui/autofill_suggestion_delegate.h
  • components/autofill/ios/browser/autofill_agent.mm
  • components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc
  • chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
  • chrome/browser/ui/autofill/chrome_autofill_client_interactive_uitest.cc
  • components/password_manager/core/browser/password_manual_fallback_flow.cc
From bf1633dbb56cf28aee80ccd4d9eba3549c409415 Mon Sep 17 00:00:00 2001
From: Christoph Schwering <schwering@google.com>
Date: Sat, 19 Sep 2026 15:48:40 -0700
Subject: [PATCH] [Autofill] Pass FormGlobalId and FieldGlobalId to suggestion delegates

This CL replaces
  Popup{ControllerCommon,OpenArgs}::anchor_frame_token
with
  Popup{ControllerCommon,OpenArgs}::form_id
  Popup{ControllerCommon,OpenArgs}::field_id
and passes them to
AutofillSuggestionDelegate::Did{Accept,Select}Suggestion().

The CL is a no-op: The role of `anchor_frame_token` is now filled by
`field_id.frame_token`. And the parameters are not used yet in
Did{Accept,Select}Suggestion().

This is in preparation for crrev.com/c/8422781.

TAG=agy
CONV=87860559-e016-44b4-81dd-8635b7409a43

Bug: 563089510, 553128833
Change-Id: If311adbb6f47ccd685eec72ebcb73c5cc04af0e1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8419078
Reviewed-by: Mohamed Amir Yosef <mamir@chromium.org>
Commit-Queue: Christoph Schwering <schwering@google.com>
Cr-Commit-Position: refs/heads/main@{#1701425}
---
diff --git a/chrome/browser/ui/autofill/at_memory_suggestion_controller.cc b/chrome/browser/ui/autofill/at_memory_suggestion_controller.cc
index 01606b6..859fef4 100644
--- a/chrome/browser/ui/autofill/at_memory_suggestion_controller.cc
+++ b/chrome/browser/ui/autofill/at_memory_suggestion_controller.cc
@@ -92,8 +92,10 @@
   Suggestion suggestion = suggestions_[index];
   if (delegate_) {
     delegate_->DidAcceptSuggestion(
-        suggestion, AutofillSuggestionDelegate::SuggestionMetadata{
-                        .multi_index = {static_cast<size_t>(index)}});
+        suggestion,
+        AutofillSuggestionDelegate::SuggestionMetadata{
+            .multi_index = {static_cast<size_t>(index)}},
+        controller_common_.form_id, controller_common_.field_id);
   }
 }
 
@@ -171,7 +173,7 @@
 
 const LocalFrameToken& AtMemorySuggestionController::GetAnchorFrameToken()
     const {
-  return controller_common_.anchor_frame_token;
+  return controller_common_.field_id.frame_token;
 }
 
 void AtMemorySuggestionController::HideViewAndDie() {
@@ -290,9 +292,11 @@
   Suggestion suggestion = parent_suggestion.children[child_position];
   if (delegate_) {
     delegate_->DidAcceptSuggestion(
-        suggestion, AutofillSuggestionDelegate::SuggestionMetadata{
-                        .multi_index = {static_cast<size_t>(parent_position),
-                                        static_cast<size_t>(child_position)}});
+        suggestion,
+        AutofillSuggestionDelegate::SuggestionMetadata{
+            .multi_index = {static_cast<size_t>(parent_position),
+                            static_cast<size_t>(child_position)}},
+        controller_common_.form_id, controller_common_.field_id);
   }
 }
 
diff --git a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
index e4b856a..ff83f370 100644
--- a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
@@ -593,8 +593,10 @@
     mouse_metrics_recorder_->RecordAccepted(suggestions_filling_product_);
   }
   delegate_->DidAcceptSuggestion(
-      suggestion, AutofillSuggestionDelegate::SuggestionMetadata{
-                      .multi_index = {static_cast<size_t>(index)}});
+      suggestion,
+      AutofillSuggestionDelegate::SuggestionMetadata{
+          .multi_index = {static_cast<size_t>(index)}},
+      controller_common_.form_id, controller_common_.field_id);
 }
 
 bool AutofillKeyboardAccessoryControllerImpl::RemoveSuggestion(int index) {
@@ -741,7 +743,7 @@
   }
 
   content::RenderFrameHost* rfh = FindRenderFrameHostByToken(
-      *web_contents_, controller_common_.anchor_frame_token);
+      *web_contents_, controller_common_.field_id.frame_token);
   if (!rfh) {
     Hide(SuggestionHidingReason::kNoFrameHasFocus);
     return;
@@ -855,7 +857,7 @@
 
 const LocalFrameToken&
 AutofillKeyboardAccessoryControllerImpl::GetAnchorFrameToken() const {
-  return controller_common_.anchor_frame_token;
+  return controller_common_.field_id.frame_token;
 }
 
 bool AutofillKeyboardAccessoryControllerImpl::HasSuggestions() const {
@@ -1029,7 +1031,8 @@
     if (mouse_metrics_recorder_) {
       mouse_metrics_recorder_->RecordSelected(suggestions_filling_product_);
     }
-    delegate_->DidSelectSuggestion(suggestion);
+    delegate_->DidSelectSuggestion(suggestion, controller_common_.form_id,
+                                   controller_common_.field_id);
   } else {
     delegate_->ClearPreviewedForm();
   }
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
index 9be4b1b..60a24350 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -331,7 +331,7 @@
   }
 
   content::RenderFrameHost* rfh = FindRenderFrameHostByToken(
-      *web_contents_, controller_common_.anchor_frame_token);
+      *web_contents_, controller_common_.field_id.frame_token);
   if (!rfh) {
     Hide(SuggestionHidingReason::kNoFrameHasFocus);
     return;
@@ -468,7 +468,7 @@
 
 const LocalFrameToken& AutofillPopupControllerImpl::GetAnchorFrameToken()
     const {
-  return controller_common_.anchor_frame_token;
+  return controller_common_.field_id.frame_token;
 }
 
 bool AutofillPopupControllerImpl::IsViewVisibilityAcceptingThresholdEnabled()
@@ -571,7 +571,9 @@
   base::UmaHistogramEnumeration("Autofill.SuggestionAccepted.Method",
                                 accept_method);
 
-  delegate_->DidAcceptSuggestion(suggestion, GetSuggestionMetadata(index));
+  delegate_->DidAcceptSuggestion(suggestion, GetSuggestionMetadata(index),
+                                 controller_common_.form_id,
+                                 controller_common_.field_id);
 }
 
 AutofillSuggestionDelegate::SuggestionUiMetadata
@@ -930,7 +932,9 @@
   }
 
   any_suggestion_selected_ = true;
-  delegate_->DidSelectSuggestion(GetSuggestionAt(index));
+  delegate_->DidSelectSuggestion(GetSuggestionAt(index),
+                                 controller_common_.form_id,
+                                 controller_common_.field_id);
 }
 
 void AutofillPopupControllerImpl::UnselectSuggestion() {
diff --git a/chrome/browser/ui/autofill/autofill_suggestion_controller.cc b/chrome/browser/ui/autofill/autofill_suggestion_controller.cc
index 5ddaf13..641314c 100644
--- a/chrome/browser/ui/autofill/autofill_suggestion_controller.cc
+++ b/chrome/browser/ui/autofill/autofill_suggestion_controller.cc
@@ -78,7 +78,7 @@
     int32_t form_control_ax_id,
     AutofillSuggestionTriggerSource trigger_source) {
   if (previous && previous->MayRecycle(delegate, web_contents,
-                                       controller_common.anchor_frame_token,
+                                       controller_common.field_id.frame_token,
                                        trigger_source)) {
     previous->Recycle(std::move(controller_common), form_control_ax_id);
     return previous;
diff --git a/chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.cc b/chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.cc
index dd51e99..1a1e295 100644
--- a/chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.cc
+++ b/chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.cc
@@ -50,7 +50,8 @@
     : AutofillSuggestionControllerForTestBase(
           external_delegate,
           web_contents,
-          PopupControllerCommon(frame_token,
+          PopupControllerCommon(FormGlobalId(frame_token, FormRendererId()),
+                                FieldGlobalId(frame_token, FieldRendererId()),
                                 element_bounds,
                                 base::i18n::UNKNOWN_DIRECTION)) {}
 
diff --git a/chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.h b/chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.h
index 882913f..d037578 100644
--- a/chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.h
+++ b/chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.h
@@ -272,11 +272,16 @@
                const AutofillSuggestionDelegate::SuggestionUiMetadata&),
               (override));
   MOCK_METHOD(void, OnSuggestionsHidden, (SuggestionHidingReason), (override));
-  MOCK_METHOD(void, DidSelectSuggestion, (const Suggestion&), (override));
+  MOCK_METHOD(void,
+              DidSelectSuggestion,
+              (const Suggestion&, const FormGlobalId&, const FieldGlobalId&),
+              (override));
   MOCK_METHOD(void,
               DidAcceptSuggestion,
               (const Suggestion&,
-               const AutofillSuggestionDelegate::SuggestionMetadata&),
+               const AutofillSuggestionDelegate::SuggestionMetadata&,
+               const FormGlobalId&,
+               const FieldGlobalId&),
               (override));
   MOCK_METHOD(bool, RemoveSuggestion, (const Suggestion&), (override));
   MOCK_METHOD(void, OnTabSelected, (TabbedPaneTabType), (override));
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 7ac6d9aa..b12118cc 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -1445,7 +1445,7 @@
   suggestion_controller_ = AutofillSuggestionController::GetOrCreate(
       suggestion_controller_, delegate, web_contents(),
       PopupControllerCommon(
-          open_args.anchor_frame_token, element_bounds_in_screen_space,
+          open_args.form_id, open_args.field_id, element_bounds_in_screen_space,
           open_args.text_direction, open_args.anchor_type,
           open_args.show_tabbed_popup,
           open_args.prefer_prev_arrow_side_on_suggestions_update),
diff --git a/chrome/browser/ui/autofill/popup_controller_common.cc b/chrome/browser/ui/autofill/popup_controller_common.cc
index fd4393d..1497f1a5 100644
--- a/chrome/browser/ui/autofill/popup_controller_common.cc
+++ b/chrome/browser/ui/autofill/popup_controller_common.cc
@@ -11,13 +11,15 @@
 namespace autofill {
 
 PopupControllerCommon::PopupControllerCommon(
-    LocalFrameToken anchor_frame_token,
+    FormGlobalId form_id,
+    FieldGlobalId field_id,
     gfx::RectF element_bounds,
     base::i18n::TextDirection text_direction,
     PopupAnchorType anchor_type,
     bool show_tabbed_popup,
     bool prefer_prev_arrow_side_on_suggestions_update)
-    : anchor_frame_token(std::move(anchor_frame_token)),
+    : form_id(std::move(form_id)),
+      field_id(std::move(field_id)),
       element_bounds(std::move(element_bounds)),
       text_direction(text_direction),
       anchor_type(anchor_type),
diff --git a/chrome/browser/ui/autofill/popup_controller_common.h b/chrome/browser/ui/autofill/popup_controller_common.h
index 85050ea8..3fc2a636 100644
--- a/chrome/browser/ui/autofill/popup_controller_common.h
+++ b/chrome/browser/ui/autofill/popup_controller_common.h
@@ -18,7 +18,8 @@
 struct PopupControllerCommon {
  public:
   PopupControllerCommon(
-      LocalFrameToken anchor_frame_token,
+      FormGlobalId form_id,
+      FieldGlobalId field_id,
       gfx::RectF element_bounds,
       base::i18n::TextDirection text_direction,
       PopupAnchorType anchor_type = PopupAnchorType::kField,
@@ -31,9 +32,9 @@
 
   ~PopupControllerCommon();
 
-  // The frame in which the popup is anchored. Typically this is the frame of
-  // the field on which the user triggered Autofill.
-  LocalFrameToken anchor_frame_token;
+  // The form and field in which the popup is anchored.
+  FormGlobalId form_id;
+  FieldGlobalId field_id;
 
   // The bounds of the DOM element that is the focus of the popup.
   // These coordinates are in screen space.
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
index cd519b8e..3e4463c 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
@@ -139,7 +139,10 @@
           autofill::FormControlType::kInputPassword)),
       generation_element_id_(ui_data.generation_element_id),
       max_length_(ui_data.max_length),
-      controller_common_(autofill::LocalFrameToken(*frame->GetFrameToken()),
+      controller_common_(ui_data.form_data.global_id(),
+                         autofill::FieldGlobalId(
+                             autofill::LocalFrameToken(*frame->GetFrameToken()),
+                             ui_data.generation_element_id),
                          bounds,
                          ui_data.text_direction),
       state_(kOfferGeneration),
diff --git a/components/autofill/core/browser/foundations/autofill_client.cc b/components/autofill/core/browser/foundations/autofill_client.cc
index 447d2aa..aaa3881 100644
--- a/components/autofill/core/browser/foundations/autofill_client.cc
+++ b/components/autofill/core/browser/foundations/autofill_client.cc
@@ -46,7 +46,8 @@
 
 AutofillClient::PopupOpenArgs::PopupOpenArgs() = default;
 AutofillClient::PopupOpenArgs::PopupOpenArgs(
-    LocalFrameToken anchor_frame_token,
+    FormGlobalId form_id,
+    FieldGlobalId field_id,
     const gfx::RectF& element_bounds,
     base::i18n::TextDirection text_direction,
     std::vector<Suggestion> suggestions,
@@ -56,7 +57,8 @@
     bool show_tabbed_popup,
     bool prefer_prev_arrow_side_on_suggestions_update,
     std::u16string search_bar_initial_value)
-    : anchor_frame_token(std::move(anchor_frame_token)),
+    : form_id(std::move(form_id)),
+      field_id(std::move(field_id)),
       element_bounds(element_bounds),
       text_direction(text_direction),
       suggestions(std::move(suggestions)),
diff --git a/components/autofill/core/browser/foundations/autofill_client.h b/components/autofill/core/browser/foundations/autofill_client.h
index 9874a25..ad42d57 100644
--- a/components/autofill/core/browser/foundations/autofill_client.h
+++ b/components/autofill/core/browser/foundations/autofill_client.h
@@ -289,7 +289,8 @@
   // Required arguments to create a dropdown showing autofill suggestions.
   struct PopupOpenArgs {
     PopupOpenArgs();
-    PopupOpenArgs(LocalFrameToken anchor_frame_token,
+    PopupOpenArgs(FormGlobalId form_id,
+                  FieldGlobalId field_id,
                   const gfx::RectF& element_bounds,
                   base::i18n::TextDirection text_direction,
                   std::vector<Suggestion> suggestions,
@@ -304,9 +305,9 @@
     PopupOpenArgs& operator=(const PopupOpenArgs&);
     PopupOpenArgs& operator=(PopupOpenArgs&&);
     ~PopupOpenArgs();
-    // The frame which the popup is anchored to. Typically this is the frame of
-    // the field on which the user triggered Autofill.
-    LocalFrameToken anchor_frame_token;
+    // The form and field to which the popup is anchored.
+    FormGlobalId form_id;
+    FieldGlobalId field_id;
     // TODO(crbug.com/340817507): Update this member name since bounds can now
     // refer to the caret bounds and elements gives the idea of HTML elements
     // only.
diff --git a/components/autofill/core/browser/ui/autofill_external_delegate.cc b/components/autofill/core/browser/ui/autofill_external_delegate.cc
index 7291464..316d62e8 100644
--- a/components/autofill/core/browser/ui/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/ui/autofill_external_delegate.cc
@@ -607,7 +607,7 @@
 #endif
 
   AutofillClient::PopupOpenArgs open_args(
-      trigger_field->global_id().frame_token,
+      last_query_.form_id, trigger_field->global_id(),
       should_use_caret_bounds ? gfx::RectF(caret_bounds_)
                               : trigger_field->bounds(),
       trigger_field->text_direction(), std::move(suggestions), trigger_source_,
@@ -744,7 +744,9 @@
 }
 
 void AutofillExternalDelegate::DidSelectSuggestion(
-    const Suggestion& suggestion) {
+    const Suggestion& suggestion,
+    const FormGlobalId& form_id,
+    const FieldGlobalId& field_id) {
   ClearPreviewedForm();
 
   switch (suggestion.type) {
@@ -906,7 +908,9 @@
 
 void AutofillExternalDelegate::DidAcceptSuggestion(
     const Suggestion& suggestion,
-    const SuggestionMetadata& metadata) {
+    const SuggestionMetadata& metadata,
+    const FormGlobalId& form_id,
+    const FieldGlobalId& field_id) {
   CHECK(suggestion.IsAcceptable());
   // TODO(crbug.com/552871965): Extract the logging here to a separate function.
   base::UmaHistogramEnumeration("Autofill.Suggestions.AcceptedType",
diff --git a/components/autofill/core/browser/ui/autofill_external_delegate.h b/components/autofill/core/browser/ui/autofill_external_delegate.h
index 2ea75fb..0f8662a 100644
--- a/components/autofill/core/browser/ui/autofill_external_delegate.h
+++ b/components/autofill/core/browser/ui/autofill_external_delegate.h
@@ -79,9 +79,13 @@
   bool OnFilterChanged(const std::u16string& filter) override;
   bool OnSearchSubmitted(const std::u16string& filter) override;
   bool IsSearching() const override;
-  void DidSelectSuggestion(const Suggestion& suggestion) override;
+  void DidSelectSuggestion(const Suggestion& suggestion,
+                           const FormGlobalId& form_id,
+                           const FieldGlobalId& field_id) override;
   void DidAcceptSuggestion(const Suggestion& suggestion,
-                           const SuggestionMetadata& metadata) override;
+                           const SuggestionMetadata& metadata,
+                           const FormGlobalId& form_id,
+                           const FieldGlobalId& field_id) override;
   bool RemoveSuggestion(const Suggestion& suggestion) override;
   void ClearPreviewedForm() override;
 
diff --git a/components/autofill/core/browser/ui/autofill_suggestion_delegate.h b/components/autofill/core/browser/ui/autofill_suggestion_delegate.h
index ee33d84e..6e4c396 100644
--- a/components/autofill/core/browser/ui/autofill_suggestion_delegate.h
+++ b/components/autofill/core/browser/ui/autofill_suggestion_delegate.h
@@ -103,11 +103,15 @@
 
   // Called when the autofill `suggestion` has been temporarily selected (e.g.,
... (file diff truncated)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/ui/android/autofill/at_memory_bottom_sheet_bridge_unittest.cc b/chrome/browser/ui/android/autofill/at_memory_bottom_sheet_bridge_unittest.cc
index f09f169..4e590763 100644
--- a/chrome/browser/ui/android/autofill/at_memory_bottom_sheet_bridge_unittest.cc
+++ b/chrome/browser/ui/android/autofill/at_memory_bottom_sheet_bridge_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/autofill/at_memory_suggestion_controller.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/autofill/core/common/autofill_test_util.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -23,7 +24,8 @@
       : AtMemorySuggestionController(
             nullptr,
             nullptr,
-            PopupControllerCommon({},
+            PopupControllerCommon(test::MakeFormGlobalId(),
+                                  test::MakeFieldGlobalId(),
                                   gfx::RectF(),
                                   base::i18n::UNKNOWN_DIRECTION)) {}
   MOCK_METHOD(void, OnDismissed, (), (override));
@@ -39,6 +41,7 @@
   }
 
   content::BrowserTaskEnvironment task_environment_;
+  autofill::test::AutofillUnitTestEnvironment autofill_environment_;
   TestingProfile profile_;
   std::unique_ptr<ui::WindowAndroid::ScopedWindowAndroidForTesting> window_;
   std::unique_ptr<MockAtMemorySuggestionController> controller_;
diff --git a/chrome/browser/ui/autofill/at_memory_suggestion_controller_unittest.cc b/chrome/browser/ui/autofill/at_memory_suggestion_controller_unittest.cc
index 659c034..3095f10a 100644
--- a/chrome/browser/ui/autofill/at_memory_suggestion_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/at_memory_suggestion_controller_unittest.cc
@@ -13,6 +13,7 @@
 #include "components/autofill/core/browser/suggestions/suggestion_hiding_reason.h"
 #include "components/autofill/core/browser/suggestions/suggestion_type.h"
 #include "components/autofill/core/browser/ui/mock_autofill_suggestion_delegate.h"
+#include "components/autofill/core/common/autofill_test_util.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_renderer_host.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -26,6 +27,16 @@
 using ::testing::ElementsAreArray;
 using ::testing::Eq;
 
+FormGlobalId MakeFormGlobalId(AutofillManager& manager) {
+  return FormGlobalId(manager.driver().GetFrameToken(),
+                      test::MakeFormRendererId());
+}
+
+FieldGlobalId MakeFieldGlobalId(AutofillManager& manager) {
+  return FieldGlobalId(manager.driver().GetFrameToken(),
+                       test::MakeFieldRendererId());
+}
+
 class MockAtMemoryBottomSheetBridge : public AtMemoryBottomSheetBridge {
  public:
   explicit MockAtMemoryBottomSheetBridge(
@@ -49,7 +60,8 @@
     if (!suggestion_controller_) {
       auto* controller = new AtMemorySuggestionController(
           manager.external_delegate().GetWeakPtrForTest(), &GetWebContents(),
-          PopupControllerCommon(manager.driver().GetFrameToken(), {},
+          PopupControllerCommon(MakeFormGlobalId(manager),
+                                MakeFieldGlobalId(manager), {},
                                 base::i18n::UNKNOWN_DIRECTION));
       suggestion_controller_ = controller->GetWeakPtr();
     }
@@ -167,7 +179,8 @@
       AutofillSuggestionController::GetOrCreate(
           /*previous=*/nullptr,
           manager1.external_delegate().GetWeakPtrForTest(), web_contents(),
-          PopupControllerCommon(manager1.driver().GetFrameToken(), {},
+          PopupControllerCommon(MakeFormGlobalId(manager1),
+                                MakeFieldGlobalId(manager1), {},
                                 base::i18n::UNKNOWN_DIRECTION),
           /*form_control_ax_id=*/0,
           AutofillSuggestionTriggerSource::kAtMemoryDoubleCtrl);
@@ -184,7 +197,8 @@
       AutofillSuggestionController::GetOrCreate(
           controller1_weak, manager2.external_delegate().GetWeakPtrForTest(),
           web_contents(),
-          PopupControllerCommon(manager2.driver().GetFrameToken(), {},
+          PopupControllerCommon(MakeFormGlobalId(manager2),
+                                MakeFieldGlobalId(manager2), {},
                                 base::i18n::UNKNOWN_DIRECTION),
           /*form_control_ax_id=*/0,
           AutofillSuggestionTriggerSource::kAtMemoryDoubleCtrl);
@@ -203,7 +217,8 @@
       AutofillSuggestionController::GetOrCreate(
           /*previous=*/nullptr,
           manager1.external_delegate().GetWeakPtrForTest(), web_contents(),
-          PopupControllerCommon(manager1.driver().GetFrameToken(), {},
+          PopupControllerCommon(MakeFormGlobalId(manager1),
+                                MakeFieldGlobalId(manager1), {},
                                 base::i18n::UNKNOWN_DIRECTION),
           /*form_control_ax_id=*/0,
           AutofillSuggestionTriggerSource::kAtMemoryDoubleCtrl);
@@ -212,7 +227,8 @@
       AutofillSuggestionController::GetOrCreate(
           controller1_weak, manager1.external_delegate().GetWeakPtrForTest(),
           web_contents(),
-          PopupControllerCommon(manager1.driver().GetFrameToken(), {},
+          PopupControllerCommon(MakeFormGlobalId(manager1),
+                                MakeFieldGlobalId(manager1), {},
                                 base::i18n::UNKNOWN_DIRECTION),
           /*form_control_ax_id=*/0,
           AutofillSuggestionTriggerSource::kAtMemoryDoubleCtrl);
@@ -236,7 +252,8 @@
           suggestions[0],
           testing::Field(
               &AutofillSuggestionDelegate::SuggestionMetadata::multi_index,
-              std::vector<size_t>{0})));
+              std::vector<size_t>{0}),
+          _, _));
 
   client().suggestion_controller(manager()).AcceptSuggestion(
       /*index=*/0, AutofillMetrics::SuggestionAcceptedMethod::kTap);
@@ -248,7 +265,9 @@
   testing::NiceMock<MockAutofillSuggestionDelegate> mock_delegate;
   auto* controller = new AtMemorySuggestionController(
       mock_delegate.GetWeakPtr(), web_contents(),
-      PopupControllerCommon({}, gfx::RectF(), base::i18n::UNKNOWN_DIRECTION));
+      PopupControllerCommon(MakeFormGlobalId(manager()),
+                            MakeFieldGlobalId(manager()), gfx::RectF(),
+                            base::i18n::UNKNOWN_DIRECTION));
 
   auto mock_bridge =
       std::make_unique<MockAtMemoryBottomSheetBridge>(controller);
@@ -281,7 +300,8 @@
           parent,
           testing::Field(
               &AutofillSuggestionDelegate::SuggestionMetadata::multi_index,
-              std::vector<size_t>{0})));
+              std::vector<size_t>{0}),
+          _, _));
   controller->OnSuggestionAccepted(0);
 
   // OnChildSuggestionsShown routes to OnSuggestionsShown with parent metadata.
@@ -296,7 +316,8 @@
           child,
           testing::Field(
               &AutofillSuggestionDelegate::SuggestionMetadata::multi_index,
-              std::vector<size_t>{0, 0})));
+              std::vector<size_t>{0, 0}),
+          _, _));
   controller->OnChildSuggestionSelected(0, 0);
 
   // IsSearching routes to IsSearching on delegate.
diff --git a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc
index 6905193..bd6a32e 100644
--- a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc
@@ -1186,8 +1186,11 @@
                         1)));
 
   client().suggestion_controller(manager()).Recycle(
-      PopupControllerCommon(manager().driver().GetFrameToken(), {},
-                            base::i18n::UNKNOWN_DIRECTION),
+      PopupControllerCommon(FormGlobalId(manager().driver().GetFrameToken(),
+                                         test::MakeFormRendererId()),
+                            FieldGlobalId(manager().driver().GetFrameToken(),
+                                          test::MakeFieldRendererId()),
+                            {}, base::i18n::UNKNOWN_DIRECTION),
       /*form_control_ax_id=*/0);
 
   ShowSuggestions(manager(), {test::CreateAutofillSuggestion(
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac_unittest.mm b/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac_unittest.mm
index 4221a0f..64ff954 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac_unittest.mm
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac_unittest.mm
@@ -11,6 +11,7 @@
 #import "chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.h"
 #import "components/autofill/core/browser/suggestions/suggestion.h"
 #import "components/autofill/core/browser/suggestions/suggestion_type.h"
+#import "components/autofill/core/common/autofill_test_util.h"
 #import "testing/gmock/include/gmock/gmock.h"
 #import "testing/gtest/include/gtest/gtest.h"
 #import "ui/gfx/geometry/rect_f.h"
@@ -41,9 +42,11 @@
       : AutofillPopupControllerImplMac(
             external_delegate,
             web_contents,
-            PopupControllerCommon(frame_token,
-                                  element_bounds,
-                                  base::i18n::UNKNOWN_DIRECTION)) {}
+            PopupControllerCommon(
+                FormGlobalId(frame_token, test::MakeFormRendererId()),
+                FieldGlobalId(frame_token, test::MakeFieldRendererId()),
+                element_bounds,
+                base::i18n::UNKNOWN_DIRECTION)) {}
 
   ~AutofillPopupControllerImplMacForTesting() override = default;
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl_unittest.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl_unittest.cc
index 5ec88031..d2208e5 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl_unittest.cc
@@ -522,7 +522,7 @@
 
   EXPECT_CALL(manager().external_delegate(),
               DidAcceptSuggestion(
-                  _, EqualsSuggestionMetadata({.multi_index = {0, 0}})));
+                  _, EqualsSuggestionMetadata({.multi_index = {0, 0}}), _, _));
 
   task_environment()->FastForwardBy(base::Milliseconds(1000));
   sub_controller->AcceptSuggestion(
@@ -1179,10 +1179,12 @@
   ShowSuggestions(manager(),
                   {Suggestion(u"main_text", SuggestionType::kAddressEntry)});
 
-  EXPECT_CALL(manager().external_delegate(),
-              DidAcceptSuggestion(
-                  _, EqualsSuggestionMetadata(
-                         {.multi_index = {0}, .from_search_result = true})));
+  EXPECT_CALL(
+      manager().external_delegate(),
+      DidAcceptSuggestion(_,
+                          EqualsSuggestionMetadata(
+                              {.multi_index = {0}, .from_search_result = true}),
+                          _, _));
 
   controller.SetFilter(AutofillPopupController::StringFilter(u"main_text"),
                        AutofillPopupController::FilterSource::kInputChanged);
diff --git a/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc
index 8412e26..24f2d9ab 100644
--- a/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc
@@ -40,6 +40,7 @@
 #include "components/autofill/core/browser/ui/autofill_external_delegate.h"
 #include "components/autofill/core/browser/ui/autofill_suggestion_delegate.h"
 #include "components/autofill/core/common/aliases.h"
+#include "components/autofill/core/common/autofill_test_util.h"
 #include "components/autofill/core/common/unique_ids.h"
 #include "components/input/native_web_keyboard_event.h"
 #include "components/prefs/pref_service.h"
@@ -264,11 +265,14 @@
 
 TEST_F(AutofillSuggestionControllerTest, GetOrCreate) {
   auto create_controller = [&](gfx::RectF bounds) {
+    LocalFrameToken frame_token(*main_frame()->GetFrameToken());
     return AutofillSuggestionController::GetOrCreate(
         client().suggestion_controller(manager()).GetWeakPtr(),
         manager().external_delegate().GetWeakPtrForTest(), web_contents(),
-        PopupControllerCommon(LocalFrameToken(*main_frame()->GetFrameToken()),
-                              std::move(bounds), base::i18n::UNKNOWN_DIRECTION),
+        PopupControllerCommon(
+            FormGlobalId(frame_token, test::MakeFormRendererId()),
+            FieldGlobalId(frame_token, test::MakeFieldRendererId()),
+            std::move(bounds), base::i18n::UNKNOWN_DIRECTION),
         /*form_control_ax_id=*/0,
         AutofillSuggestionTriggerSource::kUnspecified);
   };
@@ -333,12 +337,15 @@
                               SuggestionType::kAutocompleteEntry});
 
   // Now show a new popup with the same controller, but with fewer items.
+  LocalFrameToken frame_token(*main_frame()->GetFrameToken());
   base::WeakPtr<AutofillSuggestionController> controller =
       AutofillSuggestionController::GetOrCreate(
           client().suggestion_controller(manager()).GetWeakPtr(),
           manager().external_delegate().GetWeakPtrForTest(), web_contents(),
-          PopupControllerCommon(LocalFrameToken(*main_frame()->GetFrameToken()),
-                                gfx::RectF(), base::i18n::UNKNOWN_DIRECTION),
+          PopupControllerCommon(
+              FormGlobalId(frame_token, test::MakeFormRendererId()),
+              FieldGlobalId(frame_token, test::MakeFieldRendererId()),
+              gfx::RectF(), base::i18n::UNKNOWN_DIRECTION),
           /*form_control_ax_id=*/0,
           AutofillSuggestionTriggerSource::kUnspecified);
   EXPECT_EQ(0, controller->GetLineCount());
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client_interactive_uitest.cc b/chrome/browser/ui/autofill/chrome_autofill_client_interactive_uitest.cc
index f2de98e..bbeb5f9 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client_interactive_uitest.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client_interactive_uitest.cc
@@ -25,6 +25,7 @@
 #include "components/autofill/core/browser/metrics/autofill_settings_metrics.h"
 #include "components/autofill/core/browser/suggestions/suggestion.h"
 #include "components/autofill/core/browser/suggestions/suggestion_type.h"
+#include "components/autofill/core/browser/test_utils/autofill_form_test_util.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_util.h"
 #include "components/autofill/core/browser/ui/autofill_external_delegate.h"
 #include "components/autofill/core/browser/ui/popup_open_enums.h"
@@ -133,12 +134,12 @@
   }
 
   AutofillClient::SuggestionUiSessionId ShowSuggestions(
+      const FormGlobalId& form_id,
       const FieldGlobalId& field_id,
       const gfx::RectF& bounds) {
     return client()->ShowAutofillSuggestions(
         ChromeAutofillClient::PopupOpenArgs(
-            field_id.frame_token, bounds,
-            base::i18n::TextDirection::LEFT_TO_RIGHT,
+            form_id, field_id, bounds, base::i18n::TextDirection::LEFT_TO_RIGHT,
             {Suggestion(u"test", SuggestionType::kAutocompleteEntry)},
             AutofillSuggestionTriggerSource::kFormControlElementClicked,
             /*form_control_ax_id=*/0, PopupAnchorType::kField),
@@ -179,16 +180,19 @@
 // that the IPH is hidden and the Autofill Popup is successfully shown.
... (truncated)
Loading diff…

Original Bug Report

reported by sc...@google.com

Cross-origin Popup hijacking

A compromised, unfocused cross-origin frame can silently overwrite AED::last_query_ while an Autofill popup is open and thus redirect autofills to itself.

The attack works as follows:

  1. A legitimate popup is open.
  2. The attacking renderer now starts an infinite loop of AutofillDriver::AskForValuesToFill() calls, where the Nth call
    • sets AED::last_query_ synchronously;
    • starts an asynchronous database lookup for Autocomplete during the suggestion generation, which buys the attacker time;
    • cancels the database query from the N-1th call and thus avoids a HideSuggestions() call that’d hide the legitimate popup.
  3. The user selects or accepts a suggestion, which attempts to fill the form and field stored in AED::last_query_, which is the attackers form and field. The attacker has, already before step 2, set up a form such as
    <input autocomplete="section-foo transaction-currency>
    <input autocomplete="section-foo cc-number>
    <input autocomplete="section-foo cc-exp>
    <input autocomplete="section-foo cc-csc>
    
    where the transaction-currency field is the trigger field for the malicious AskForValuesToFill() events and the cc-* fields are the harvester fields that’ll receive the credit card data, and the section-foo convinces FormFiller to fill all these fields.

While I haven’t tested it, the attack should also work for addresses as well as other filling products, provided we can get the fields into the same section (that shouldn’t be a problem though because credit card fields are the only ones with special treatment in that regard).

More material:

View on issue tracker