CVE-2026-11642
Overview
Files Changed
chrome/browser/ui/web_applications/tabbed_web_app_navigation_throttle.cc
Patch
From c303de53a69279cd5b7b7a79aa46303a4cce0356 Mon Sep 17 00:00:00 2001 From: Dibyajyoti Pal <[email protected]> Date: Fri, 29 May 2026 16:08:53 -0700 Subject: [PATCH] [PWA] Fix UAF in TabbedWebAppNavigationThrottle In TabbedWebAppNavigationThrottle::WillStartRequest, target=_blank links to the home tab cause a blank tab to be opened. The throttle closes this blank tab by calling web_contents->ClosePage() synchronously. However, calling ClosePage() synchronously during throttle execution destroys the WebContents (and consequently the active NavigationRequest and NavigationThrottle), which leads to a User-After-Free (UAF) when the navigation pipeline resumes execution. This CL fixes the UAF by deferring the ClosePage() call asynchronously. We use PostTask with a WeakPtr to the WebContents to ensure that the current navigation throttle can finish its lifecycle safely before the WebContents is closed. Fixed: 517678820 Change-Id: I06dfdc109f77ee31381948d1a2705187bc42d608 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7886484 Commit-Queue: Dibyajyoti Pal <[email protected]> Auto-Submit: Dibyajyoti Pal <[email protected]> Reviewed-by: Nate Chapin <[email protected]> Commit-Queue: Nate Chapin <[email protected]> Cr-Commit-Position: refs/heads/main@{#1638795} --- diff --git a/chrome/browser/ui/web_applications/tabbed_web_app_navigation_throttle.cc b/chrome/browser/ui/web_applications/tabbed_web_app_navigation_throttle.cc index 5d6ba6d7..2b20ead 100644 --- a/chrome/browser/ui/web_applications/tabbed_web_app_navigation_throttle.cc +++ b/chrome/browser/ui/web_applications/tabbed_web_app_navigation_throttle.cc @@ -4,6 +4,8 @@ #include "chrome/browser/ui/web_applications/tabbed_web_app_navigation_throttle.h" +#include "base/functional/bind.h" +#include "base/task/sequenced_task_runner.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" @@ -137,7 +139,9 @@ if (browser_window->GetTabStripModel()->count() > 1 && !web_contents->GetLastCommittedURL().is_valid()) { DVLOG(1) << "TabbedWebAppNavigationThrottle: Closing blank tab"; - web_contents->ClosePage(); + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, base::BindOnce(&content::WebContents::ClosePage, + web_contents->GetWeakPtr())); } DVLOG(1) << "TabbedWebAppNavigationThrottle: Redirecting to FocusHomeTab"; return FocusHomeTab(*app_controller, *browser_window->GetTabStripModel());
Original Bug Report
Potential Browser Process UAF in TabbedWebAppNavigationThrottle via Synchronous ClosePage
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A potential Use-After-Free (UAF) vulnerability exists in TabbedWebAppNavigationThrottle when a blank tab is navigated to a home-scope URL while a modal JavaScript dialog is active. Under this condition, calling ClosePage() synchronously destroys the WebContents, the NavigationRequest, and the throttle itself. Execution then continues into FocusHomeTab(), leading to virtual method calls on the freed throttle.
Affected files:
chrome/browser/ui/web_applications/tabbed_web_app_navigation_throttle.ccchrome/browser/ui/web_applications/tabbed_web_app_navigation_throttle.h
Estimated timestamp from git blame: 2023-07-14
Summary
A potential Use-After-Free (UAF) vulnerability has been identified in TabbedWebAppNavigationThrottle when a blank tab is navigated to a home-scope URL within a tabbed Progressive Web App (PWA). If a modal JavaScript dialog is active, calling web_contents->ClosePage() synchronously destroys the WebContents, the active NavigationRequest, and the TabbedWebAppNavigationThrottle itself. Execution then continues into FocusHomeTab(), performing virtual method calls on the freed this pointer and the deallocated NavigationRequest.
Vulnerability Analysis & Lifetime Flow
Under normal circumstances, calling web_contents->ClosePage() initiates an asynchronous teardown of the page. However, if a modal JavaScript dialog is showing on the page, the page is deemed “ready to be closed” immediately, causing RenderFrameHostImpl::ClosePage to bypass unload events and take a fully synchronous destruction path:
RenderFrameHostImpl::ClosePage()callsIsPageReadyToBeClosed(), which returnstruebecausedelegate_->IsJavaScriptDialogShowing()evaluates totrue.- This invokes
ClosePageIgnoringUnloadEvents(), which synchronously callsdelegate_->Close(this). - The delegate calls
Browser::CloseContents(), leading tochrome::CloseWebContents()andTabStripModel::CloseWebContentsAt(). - The
TabStripModelsynchronously detaches and resets the tab’sWebContentsImplobject. - Destroying the
WebContentsImplcancels the active navigation, synchronously deleting theNavigationRequestand the ownedNavigationThrottleRegistryImpl. - This registry destruction deletes all registered throttles, including the executing
TabbedWebAppNavigationThrottleinstance.
Upon returning from ClosePage() in TabbedWebAppNavigationThrottle::WillStartRequest() (line 140), the execution immediately calls:
return FocusHomeTab(*app_controller, *browser_window->GetTabStripModel());
Since this has been deallocated, FocusHomeTab is called on a dangling pointer. Inside FocusHomeTab, the code attempts to call navigation_handle(), which translates to ®istry_->GetNavigationHandle(), executing a virtual method call on a deallocated registry reference.
MiraclePtr (BackupRefPtr) Bypass
MiraclePtr does not protect against this Use-After-Free because:
- The dangling reference is the implicit stack pointer (
this) during member function execution, which is not wrapped. - The
raw_refreference toregistry_has already been destructed whenthiswas deleted, releasing its refcount and allowing the backing memory to be immediately reclaimed.
Potential Reproduction Steps
Note: These are potential/suggested steps; our tooling does not currently have the capability to execute code or run a live proof of concept.
- Launch a tabbed PWA window with a pinned home tab.
- Open a new blank tab (Tab B) in the same application window.
- On Tab B, trigger a modal JavaScript dialog state (e.g., via
RunModalAlertDialogMojo IPC) so thatis_showing_javascript_dialog_is set totrueon theWebContents. - Initiate a navigation on Tab B to a URL that is within the home tab’s scope.
- When
TabbedWebAppNavigationThrottle::WillStartRequest()is invoked, because the tab count is greater than 1 and Tab B’s last committed URL is invalid, it executesweb_contents->ClosePage(). - Due to the active JavaScript dialog, the synchronous destruction path is taken, deallocating the throttle.
- Execution resumes in
WillStartRequest(), which immediately entersFocusHomeTab()and performs virtual calls on the freed memory, leading to a potential browser process crash or control flow hijack.
Suggested Fix
To fix this issue, avoid closing the page synchronously within the execution frame of the navigation throttle. Instead of calling web_contents->ClosePage() synchronously during WillStartRequest, the throttle should return content::NavigationThrottle::CANCEL_AND_IGNORE and post a task to close the page asynchronously on the UI thread task runner via a WeakPtr to the WebContents:
// In chrome/browser/ui/web_applications/tabbed_web_app_navigation_throttle.cc:
if (browser_window->GetTabStripModel()->count() > 1 &&
!web_contents->GetLastCommittedURL().is_valid()) {
DVLOG(1) << "TabbedWebAppNavigationThrottle: Closing blank tab asynchronously";
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&content::WebContents::ClosePage,
web_contents->GetWeakPtr()));
}
This ensures the throttle can return safely and allows the navigation runner to terminate processing before the WebContents (and the throttle) are destroyed.
Evaluated with Chrome root at commit: 5133b93d189b383c37805b1cf3a9d2dbfe8d7379
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.