Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect security UI in SplitView
DescriptionIncorrect security UI in SplitView
ComponentSplitView
Bug ClassLogic Error
Tracker507263861
Fix commitd0a7e6cba457 (chromium/src) +65/-13
CISA KEVNot listed
Creditedadisahilna35@gmail.com
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
if
chrome/browser/file_select_helper.cc
modified
if
chrome/browser/file_select_helper_unittest.cc
modified
TEST_F
chrome/browser/file_select_helper_unittest.cc
modified

Files Changed

  • chrome/browser/file_select_helper.cc
  • chrome/browser/file_select_helper.h
  • chrome/browser/file_select_helper_unittest.cc
From d0a7e6cba457479e58b1f5b5c7c34333372b81ec Mon Sep 17 00:00:00 2001
From: Alison Gale <agale@chromium.org>
Date: Fri, 08 May 2026 09:04:56 -0700
Subject: [PATCH] [SxS] Close directory enumeration on tab switch

This is similar to the change I just made for RunFileChooser and ensures
when tab visibility changes within split views we close out open
file/directory pickers

Bug: 507263861
Change-Id: I9ee5520fe0de942bae4d7a4ae68dcd3599f9fca0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7805653
Reviewed-by: Bryan Oltman <bryanoltman@google.com>
Commit-Queue: Alison Gale <agale@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1627691}
---

diff --git a/chrome/browser/file_select_helper.cc b/chrome/browser/file_select_helper.cc
index a269fd1..1525b49 100644
--- a/chrome/browser/file_select_helper.cc
+++ b/chrome/browser/file_select_helper.cc
@@ -556,15 +556,7 @@
   render_frame_host_ = render_frame_host;
   web_contents_ = WebContents::FromRenderFrameHost(render_frame_host);
   listener_ = std::move(listener);
-  content::WebContentsObserver::Observe(web_contents_);
-
-  tabs::TabInterface* tab_interface =
-      tabs::TabInterface::MaybeGetFromContents(web_contents_);
-  if (tab_interface) {
-    tab_deactivated_subscription_ =
-        tab_interface->RegisterWillDeactivate(base::BindRepeating(
-            &FileSelectHelper::OnTabDeactivated, base::Unretained(this)));
-  }
+  InitLifecycleObserver(web_contents_);
 
 #if !BUILDFLAG(IS_ANDROID)
   if (PictureInPictureWindowManager::GetInstance()
@@ -686,12 +678,14 @@
 #endif  // !BUILDFLAG(IS_ANDROID)
 
   tab_deactivated_subscription_ = {};
+  directory_enumeration_.reset();
 
   // If there are temporary files, then this instance needs to stick around
   // until web_contents_ is destroyed, so that this instance can delete the
   // temporary files.
-  if (!temporary_files_.empty())
+  if (!temporary_files_.empty()) {
     return;
+  }
 
   if (listener_) {
     listener_->FileSelectionCanceled();
@@ -717,6 +711,7 @@
   dialog_type_ = ui::SelectFileDialog::SELECT_NONE;
   web_contents_ = tab;
   listener_ = std::move(listener);
+  InitLifecycleObserver(web_contents_);
   // Because this class returns notifications to the RenderViewHost, it is
   // difficult for callers to know how long to keep a reference to this
   // instance. We keep a reference to ourself to keep the instance alive after
@@ -738,7 +733,7 @@
 // code. Perform any cleanup and release the reference we added in
 // EnumerateDirectoryImpl().
 void FileSelectHelper::EnumerateDirectoryEnd() {
-  self_ptr_.reset();
+  RunFileChooserEnd();
 }
 
 void FileSelectHelper::RenderFrameHostChanged(
@@ -768,6 +763,20 @@
   CleanUp();
 }
 
+void FileSelectHelper::InitLifecycleObserver(
+    content::WebContents* web_contents) {
+  DCHECK(web_contents);
+  content::WebContentsObserver::Observe(web_contents);
+
+  tabs::TabInterface* tab_interface =
+      tabs::TabInterface::MaybeGetFromContents(web_contents);
+  if (tab_interface) {
+    tab_deactivated_subscription_ =
+        tab_interface->RegisterWillDeactivate(base::BindRepeating(
+            &FileSelectHelper::OnTabDeactivated, base::Unretained(this)));
+  }
+}
+
 void FileSelectHelper::OnTabDeactivated(tabs::TabInterface* tab) {
   RunFileChooserEnd();
 }
diff --git a/chrome/browser/file_select_helper.h b/chrome/browser/file_select_helper.h
index d28286f..19f9d43 100644
--- a/chrome/browser/file_select_helper.h
+++ b/chrome/browser/file_select_helper.h
@@ -119,6 +119,8 @@
   FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest, ConfirmationDialog);
   FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest,
                            WebContentsDestroyedDuringAsyncFileProcessing);
+  FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest,
+                           EnumerateDirectory_TabDeactivated);
   FRIEND_TEST_ALL_PREFIXES(policy::DlpFilesControllerAshBrowserTest,
                            FilesUploadCallerPassed);
 
@@ -150,6 +152,7 @@
   void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
   void WebContentsDestroyed() override;
 
+  void InitLifecycleObserver(content::WebContents* web_contents);
   void OnTabDeactivated(tabs::TabInterface* tab);
 
   void EnumerateDirectoryImpl(
diff --git a/chrome/browser/file_select_helper_unittest.cc b/chrome/browser/file_select_helper_unittest.cc
index ea51163..5d18923 100644
--- a/chrome/browser/file_select_helper_unittest.cc
+++ b/chrome/browser/file_select_helper_unittest.cc
@@ -22,8 +22,12 @@
 #include "build/build_config.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/tabs/public/mock_tab_interface.h"
 #include "content/public/browser/file_select_listener.h"
+#include "content/public/browser/web_contents_user_data.h"
 #include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_web_contents_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/models/dialog_model.h"
 #include "ui/base/test/test_dialog_model_host.h"
@@ -42,17 +46,22 @@
       std::vector<blink::mojom::FileChooserFileInfoPtr>* files)
       : files_(files) {}
 
+  bool canceled() const { return canceled_; }
+
  private:
   ~TestFileSelectListener() override = default;
   // content::FileSelectListener overrides.
   void FileSelected(std::vector<blink::mojom::FileChooserFileInfoPtr> files,
                     const base::FilePath& base_dir,
                     blink::mojom::FileChooserParams::Mode mode) override {
-    *files_ = std::move(files);
+    if (files_) {
+      *files_ = std::move(files);
+    }
   }
-  void FileSelectionCanceled() override {}
+  void FileSelectionCanceled() override { canceled_ = true; }
 
   raw_ptr<std::vector<blink::mojom::FileChooserFileInfoPtr>> files_;
+  bool canceled_ = false;
 };
 
 // Fill in the arguments to be passed to the ContentAnalysisCompletionCallback()
@@ -586,6 +595,37 @@
   task_environment.RunUntilIdle();
   EXPECT_FALSE(weak_ptr);
 }
+
+TEST_F(FileSelectHelperTest, EnumerateDirectory_TabDeactivated) {
+  content::BrowserTaskEnvironment task_environment;
+  TestingProfile profile;
+  content::TestWebContentsFactory web_contents_factory;
+  content::WebContents* web_contents =
+      web_contents_factory.CreateWebContents(&profile);
+
+  tabs::MockTabInterface mock_tab;
+  EXPECT_CALL(mock_tab, GetContents())
+      .WillRepeatedly(testing::Return(web_contents));
+  tabs::TabLookupFromWebContents::CreateForWebContents(web_contents, &mock_tab);
+
+  base::RepeatingCallback<void(tabs::TabInterface*)> deactivation_callback;
+  EXPECT_CALL(mock_tab, RegisterWillDeactivate(testing::_))
+      .WillOnce([&](base::RepeatingCallback<void(tabs::TabInterface*)> cb) {
+        deactivation_callback = std::move(cb);
+        return base::CallbackListSubscription();
+      });
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr> files;
+  auto listener = base::MakeRefCounted<TestFileSelectListener>(&files);
+
+  FileSelectHelper::EnumerateDirectory(web_contents, listener,
+                                       base::FilePath(FILE_PATH_LITERAL("/")));
+
+  ASSERT_FALSE(deactivation_callback.is_null());
+  deactivation_callback.Run(&mock_tab);
+
+  EXPECT_TRUE(listener->canceled());
+}
 #endif  // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
 
 TEST_F(FileSelectHelperTest, GetFileTypesFromAcceptType) {
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/file_select_helper_unittest.cc b/chrome/browser/file_select_helper_unittest.cc
index ea51163..5d18923 100644
--- a/chrome/browser/file_select_helper_unittest.cc
+++ b/chrome/browser/file_select_helper_unittest.cc
@@ -22,8 +22,12 @@
 #include "build/build_config.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/tabs/public/mock_tab_interface.h"
 #include "content/public/browser/file_select_listener.h"
+#include "content/public/browser/web_contents_user_data.h"
 #include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_web_contents_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/models/dialog_model.h"
 #include "ui/base/test/test_dialog_model_host.h"
@@ -42,17 +46,22 @@
       std::vector<blink::mojom::FileChooserFileInfoPtr>* files)
       : files_(files) {}
 
+  bool canceled() const { return canceled_; }
+
  private:
   ~TestFileSelectListener() override = default;
   // content::FileSelectListener overrides.
   void FileSelected(std::vector<blink::mojom::FileChooserFileInfoPtr> files,
                     const base::FilePath& base_dir,
                     blink::mojom::FileChooserParams::Mode mode) override {
-    *files_ = std::move(files);
+    if (files_) {
+      *files_ = std::move(files);
+    }
   }
-  void FileSelectionCanceled() override {}
+  void FileSelectionCanceled() override { canceled_ = true; }
 
   raw_ptr<std::vector<blink::mojom::FileChooserFileInfoPtr>> files_;
+  bool canceled_ = false;
 };
 
 // Fill in the arguments to be passed to the ContentAnalysisCompletionCallback()
@@ -586,6 +595,37 @@
   task_environment.RunUntilIdle();
   EXPECT_FALSE(weak_ptr);
 }
+
+TEST_F(FileSelectHelperTest, EnumerateDirectory_TabDeactivated) {
+  content::BrowserTaskEnvironment task_environment;
+  TestingProfile profile;
+  content::TestWebContentsFactory web_contents_factory;
+  content::WebContents* web_contents =
+      web_contents_factory.CreateWebContents(&profile);
+
+  tabs::MockTabInterface mock_tab;
+  EXPECT_CALL(mock_tab, GetContents())
+      .WillRepeatedly(testing::Return(web_contents));
+  tabs::TabLookupFromWebContents::CreateForWebContents(web_contents, &mock_tab);
+
+  base::RepeatingCallback<void(tabs::TabInterface*)> deactivation_callback;
+  EXPECT_CALL(mock_tab, RegisterWillDeactivate(testing::_))
+      .WillOnce([&](base::RepeatingCallback<void(tabs::TabInterface*)> cb) {
+        deactivation_callback = std::move(cb);
+        return base::CallbackListSubscription();
+      });
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr> files;
+  auto listener = base::MakeRefCounted<TestFileSelectListener>(&files);
+
+  FileSelectHelper::EnumerateDirectory(web_contents, listener,
+                                       base::FilePath(FILE_PATH_LITERAL("/")));
+
+  ASSERT_FALSE(deactivation_callback.is_null());
+  deactivation_callback.Run(&mock_tab);
+
+  EXPECT_TRUE(listener->canceled());
+}
 #endif  // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
 
 TEST_F(FileSelectHelperTest, GetFileTypesFromAcceptType) {
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.