Low chrome UAF 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in WebAppInstalls
DescriptionUse after free in WebAppInstalls
ComponentWebAppInstalls
Sub-componentPWA
Bug ClassUAF
Tracker533135911
Fix commit977032a456e2 (chromium/src) +62/-3
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-20

Changed Functions

FunctionChangeNotes
IN_PROC_BROWSER_TEST_F
chrome/browser/extensions/api/tabs/tabs_test.cc
modified
if
chrome/browser/extensions/extension_browsertest.cc
modified

Files Changed

  • chrome/browser/extensions/api/tabs/tabs_test.cc
  • chrome/browser/ui/browser_browsertest.cc
  • chrome/browser/extensions/extension_browsertest.cc
  • chrome/browser/ui/browser_commands.cc
From 977032a456e29c6e18edb398332e5f793bf10bcd Mon Sep 17 00:00:00 2001
From: Adriana Ixba <[email protected]>
Date: Thu, 17 Sep 2026 20:42:48 -0700
Subject: [PATCH] [PWA] Disallow tab duplication on APP_POPUPs browser windows

Duplicating a tab in a TYPE_APP_POPUP browser window (such as via
chrome.tabs.duplicate) is not supported because app popups do not have a
tab strip and cannot be cloned into a standalone popup window with a
valid app controller. Previously, attempting to duplicate an app popup
bypassed the DCHECK in release builds and created a TYPE_APP_POPUP
window with an empty app_name, leading to a browser-process CHECK crash
on Linux and a chromeless window without an origin indicator on
Windows/macOS. This CL:

1. Updates chrome::CanDuplicateTabAt() to return false for
   BrowserWindowInterface::TYPE_APP_POPUP, mirroring TYPE_PICTURE_IN_PICTURE. And additionally guard with CanDuplicateTabAt in DuplicateTabAt
2. Hardens CreateAndShowNewWindowWithContents() by upgrading the DCHECK
   against TYPE_APP_POPUP to CHECK_NE.
3. Updates ExtensionBrowserTest::CreateBrowserWindowWithType to populate
   app_name for TYPE_APP_POPUP test windows.
4. Adds test coverage in BrowserTest.CanDuplicateTab and
   ExtensionTabsTest.CannotDuplicateAppPopupWindows.

Bug: 533135911
Change-Id: I52ed317eb95f386dcff4f891afca76543abd9630
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8415307
Reviewed-by: Daniel Murphy <[email protected]>
Reviewed-by: Eshwar Stalin <[email protected]>
Commit-Queue: Adriana Ixba <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1700446}
---
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 53a351ee..08c50fa 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -280,7 +280,8 @@
     std::unique_ptr<content::WebContents> contents,
     BrowserWindowInterface* original_browser) {
   BrowserWindowInterface* new_browser = nullptr;
-  DCHECK(original_browser->GetType() != BrowserWindowInterface::TYPE_APP_POPUP);
+  CHECK_NE(original_browser->GetType(), BrowserWindowInterface::TYPE_APP_POPUP);
+  CHECK_NE(original_browser->GetType(), BrowserWindowInterface::TYPE_DEVTOOLS);
   if (original_browser->GetType() == BrowserWindowInterface::TYPE_APP) {
     const bool is_trusted_source =
         WindowFeatureController::From(original_browser)->IsTrustedSource();
@@ -369,6 +370,9 @@
 content::WebContents* DuplicateTabAt(BrowserWindowInterface* browser,
                                      int index,
                                      int dst_index) {
+  if (!chrome::CanDuplicateTabAt(browser, index)) {
+    return nullptr;
+  }
   content::WebContents* contents =
       browser->GetTabStripModel()->GetWebContentsAt(index);
   CHECK(contents);
@@ -1766,7 +1770,9 @@
 }
 
 bool CanDuplicateTabAt(const BrowserWindowInterface* browser, int index) {
-  if (browser->GetType() == BrowserWindowInterface::TYPE_PICTURE_IN_PICTURE) {
+  if (browser->GetType() == BrowserWindowInterface::TYPE_PICTURE_IN_PICTURE ||
+      browser->GetType() == BrowserWindowInterface::TYPE_APP_POPUP ||
+      browser->GetType() == BrowserWindowInterface::TYPE_DEVTOOLS) {
     return false;
   }
   WebContents* contents = browser->GetTabStripModel()->GetWebContentsAt(index);
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc
index be48a02..fc6a164 100644
--- a/chrome/browser/extensions/api/tabs/tabs_test.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -5452,6 +5452,40 @@
             error);
 }
 
+IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, CannotDuplicateAppPopupWindows) {
+  // Create app popup browser.
+  BrowserWindowInterface* app_popup_browser =
+      CreateBrowserWindowWithType(BrowserWindowInterface::Type::TYPE_APP_POPUP);
+  TabListInterface* app_popup_tab_list =
+      TabListInterface::From(app_popup_browser);
+
+  // Ensure we have a tab.
+  if (app_popup_tab_list->GetTabCount() == 0) {
+    app_popup_tab_list->OpenTab(GURL(url::kAboutBlankURL), -1);
+  }
+  ASSERT_GE(app_popup_tab_list->GetTabCount(), 1);
+  content::WebContents* web_contents =
+      app_popup_tab_list->GetTab(0)->GetContents();
+  ASSERT_NE(web_contents, nullptr);
+  int app_popup_tab_id = ExtensionTabUtil::GetTabId(web_contents);
+  ASSERT_GT(app_popup_tab_id, 0);
+
+  // Attempt to duplicate the app popup tab. This should fail as app popup tabs
+  // are not allowed to be duplicated.
+  auto function = base::MakeRefCounted<TabsDuplicateFunction>();
+  scoped_refptr<const Extension> extension =
+      ExtensionBuilder("Test").AddAPIPermission("tabs").Build();
+  function->set_extension(extension);
+  std::string args = base::StringPrintf("[%d]", app_popup_tab_id);
+  std::string error = utils::RunFunctionAndReturnError(
+      function.get(), args, app_popup_browser->GetProfile(),
+      utils::FunctionMode::kNone);
+  EXPECT_EQ(
+      ErrorUtils::FormatErrorMessage(keys::kCannotDuplicateTab,
+                                     base::NumberToString(app_popup_tab_id)),
+      error);
+}
+
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index e27cee5..53b14ab 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -853,7 +853,8 @@
     BrowserWindowInterface::Type type) {
   BrowserWindowCreateParams create_params = BrowserWindowCreateParams(
       type, *GetProfile(), /*from_user_gesture=*/false);
-  if (type == BrowserWindowInterface::Type::TYPE_APP) {
+  if (type == BrowserWindowInterface::Type::TYPE_APP ||
+      type == BrowserWindowInterface::Type::TYPE_APP_POPUP) {
     // Apps must have an app name.
     create_params.app_name = "app_name";
   }
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index 0a91dc2..792e4f6 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -2805,6 +2805,24 @@
   EXPECT_TRUE(chrome::CanDuplicateTab(browser()));
   EXPECT_TRUE(chrome::CanDuplicateTabAt(browser(), 0));
   EXPECT_TRUE(chrome::CanDuplicateTabAt(browser(), 1));
+
+  BrowserWindowInterface* app_popup_browser =
+      CreateBrowserWindow(BrowserWindowCreateParams::CreateForAppPopup(
+          "app_name", /*trusted_source=*/true, gfx::Rect(),
+          browser()->GetProfile(), /*user_gesture=*/true));
+  AddBlankTabAndShow(app_popup_browser);
+  EXPECT_FALSE(chrome::CanDuplicateTab(app_popup_browser));
+  EXPECT_FALSE(chrome::CanDuplicateTabAt(app_popup_browser, 0));
+  EXPECT_FALSE(chrome::CanDuplicateKeyboardFocusedTab(app_popup_browser));
+  EXPECT_EQ(chrome::DuplicateTabAt(app_popup_browser, 0), nullptr);
+
+  BrowserWindowInterface* devtools_browser = CreateBrowserWindow(
+      BrowserWindowCreateParams::CreateForDevTools(browser()->GetProfile()));
+  AddBlankTabAndShow(devtools_browser);
+  EXPECT_FALSE(chrome::CanDuplicateTab(devtools_browser));
+  EXPECT_FALSE(chrome::CanDuplicateTabAt(devtools_browser, 0));
+  EXPECT_FALSE(chrome::CanDuplicateKeyboardFocusedTab(devtools_browser));
+  EXPECT_EQ(chrome::DuplicateTabAt(devtools_browser, 0), nullptr);
 }
 
 namespace {
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc
index be48a02..fc6a164 100644
--- a/chrome/browser/extensions/api/tabs/tabs_test.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -5452,6 +5452,40 @@
             error);
 }
 
+IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, CannotDuplicateAppPopupWindows) {
+  // Create app popup browser.
+  BrowserWindowInterface* app_popup_browser =
+      CreateBrowserWindowWithType(BrowserWindowInterface::Type::TYPE_APP_POPUP);
+  TabListInterface* app_popup_tab_list =
+      TabListInterface::From(app_popup_browser);
+
+  // Ensure we have a tab.
+  if (app_popup_tab_list->GetTabCount() == 0) {
+    app_popup_tab_list->OpenTab(GURL(url::kAboutBlankURL), -1);
+  }
+  ASSERT_GE(app_popup_tab_list->GetTabCount(), 1);
+  content::WebContents* web_contents =
+      app_popup_tab_list->GetTab(0)->GetContents();
+  ASSERT_NE(web_contents, nullptr);
+  int app_popup_tab_id = ExtensionTabUtil::GetTabId(web_contents);
+  ASSERT_GT(app_popup_tab_id, 0);
+
+  // Attempt to duplicate the app popup tab. This should fail as app popup tabs
+  // are not allowed to be duplicated.
+  auto function = base::MakeRefCounted<TabsDuplicateFunction>();
+  scoped_refptr<const Extension> extension =
+      ExtensionBuilder("Test").AddAPIPermission("tabs").Build();
+  function->set_extension(extension);
+  std::string args = base::StringPrintf("[%d]", app_popup_tab_id);
+  std::string error = utils::RunFunctionAndReturnError(
+      function.get(), args, app_popup_browser->GetProfile(),
+      utils::FunctionMode::kNone);
+  EXPECT_EQ(
+      ErrorUtils::FormatErrorMessage(keys::kCannotDuplicateTab,
+                                     base::NumberToString(app_popup_tab_id)),
+      error);
+}
+
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index e27cee5..53b14ab 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -853,7 +853,8 @@
     BrowserWindowInterface::Type type) {
   BrowserWindowCreateParams create_params = BrowserWindowCreateParams(
       type, *GetProfile(), /*from_user_gesture=*/false);
-  if (type == BrowserWindowInterface::Type::TYPE_APP) {
+  if (type == BrowserWindowInterface::Type::TYPE_APP ||
+      type == BrowserWindowInterface::Type::TYPE_APP_POPUP) {
     // Apps must have an app name.
     create_params.app_name = "app_name";
   }
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index 0a91dc2..792e4f6 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -2805,6 +2805,24 @@
   EXPECT_TRUE(chrome::CanDuplicateTab(browser()));
   EXPECT_TRUE(chrome::CanDuplicateTabAt(browser(), 0));
   EXPECT_TRUE(chrome::CanDuplicateTabAt(browser(), 1));
+
+  BrowserWindowInterface* app_popup_browser =
+      CreateBrowserWindow(BrowserWindowCreateParams::CreateForAppPopup(
+          "app_name", /*trusted_source=*/true, gfx::Rect(),
+          browser()->GetProfile(), /*user_gesture=*/true));
+  AddBlankTabAndShow(app_popup_browser);
+  EXPECT_FALSE(chrome::CanDuplicateTab(app_popup_browser));
+  EXPECT_FALSE(chrome::CanDuplicateTabAt(app_popup_browser, 0));
+  EXPECT_FALSE(chrome::CanDuplicateKeyboardFocusedTab(app_popup_browser));
+  EXPECT_EQ(chrome::DuplicateTabAt(app_popup_browser, 0), nullptr);
+
+  BrowserWindowInterface* devtools_browser = CreateBrowserWindow(
+      BrowserWindowCreateParams::CreateForDevTools(browser()->GetProfile()));
+  AddBlankTabAndShow(devtools_browser);
+  EXPECT_FALSE(chrome::CanDuplicateTab(devtools_browser));
+  EXPECT_FALSE(chrome::CanDuplicateTabAt(devtools_browser, 0));
+  EXPECT_FALSE(chrome::CanDuplicateKeyboardFocusedTab(devtools_browser));
+  EXPECT_EQ(chrome::DuplicateTabAt(devtools_browser, 0), nullptr);
 }
 
 namespace {
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.