Overview

High
Severity
9.6
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Views
DescriptionUse after free in Views
ComponentViews
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.
Tracker553136141
Fix commit6080f65e48f9 (chromium/src) +8/-0
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-08-26 (per vendor advisory)
Fix landed2026-09-14 (public git)
Disclosed2026-09-22
Patch gap8 days — fix public in source before the advisory

Background

`Widget`
the Views-layer object that owns a top-level window’s non-client frame and delegates platform operations to a NativeWidget; it lives in the browser process.
`native_widget_`
the Widget’s pointer to its platform-specific backend (NativeWidget/aura window) that actually maps, shows, and hides the OS window.
Synchronous re-entrancy
visibility changes sent to native_widget_ can fire window-observer callbacks, focus changes, or capture-loss handlers inline, which may close the window and free the owning Widget before the delegating call returns.
`GetWeakPtr()` / `WeakPtr`
returns a weak reference to this that automatically becomes null when the object is destroyed, letting a method detect that this was freed out from under it.

Root Cause Analysis

Widget::Show(), Widget::Hide(), and Widget::ShowInactive() delegate visibility changes to native_widget_ (via native_widget_->Show(...), native_widget_->Hide(), and SetBounds(...)) and then continue to touch this — writing saved_show_state_, calling HandleShowRequested(), or passing this to OnAnyWidgetHidden(this). The unstated invariant is that this survives those delegated calls, but the delegate can synchronously run observer callbacks, focus changes, or capture loss that destroy the aura window and free the Widget instance. When that happens the post-delegation statements read and write freed memory, producing a use-after-free.

The fix captures auto weak_this = GetWeakPtr() before each delegating call and inserts CHECK(weak_this) immediately after it and before any further use of this, so a destruction turns into a deterministic abort instead of continued access to freed memory. Because each CHECK is placed ahead of the subsequent this-write or this-pass, the guard runs before the sink is reached.

Key insight
The single mistake is assuming this remains valid across a call that delegates to native_widget_, when that call can synchronously destroy the Widget; the fix detects the destruction with a WeakPtr and CHECKs before any post-call use of this, converting a use-after-free into a safe crash.

Attack Path

  1. Attach a destructive observer Arrange for a window observer, focus-change handler, or capture-loss handler that closes/destroys the widget’s window (and thus frees the Widget) when visibility changes.
  2. Trigger a visibility change Cause Widget::Show(), Widget::Hide(), or Widget::ShowInactive() to run, which delegates to native_widget_->Show()/Hide() (or SetBounds()).
  3. Free the Widget mid-call The delegated call synchronously invokes the destructive callback, destroying the aura window and freeing the Widget while the method’s stack frame is still executing.
  4. Use the freed instance Control returns to the method, which writes saved_show_state_, calls HandleShowRequested(), or passes this to OnAnyWidgetHidden(this), all operating on freed memory (the use-after-free the pre-patch code allowed).
  5. Escalate from the UAF Grooming the freed Widget allocation to obtain a controlled read/write primitive and code execution in the browser process (inferred).

Impact Assessment

An attacker who can drive a widget to show/hide while a callback destroys it gains a use-after-free on a Widget object in the browser process, potentially leading to memory corruption and code execution (inferred). Preconditions are the ability to trigger the visibility change and to arrange the synchronous destruction (e.g., via an observer, focus, or capture-loss path). The vendor advisory rates this high severity and does not report in-the-wild exploitation; NVD scores it CVSS 9.6.

Changed Functions

FunctionChangeNotes
if
ui/views/widget/widget.cc
modified

Files Changed

  • ui/views/widget/widget.cc

Audit Directions

  • Delegation-then-self-use
    Audit other Widget/NativeWidget methods that call into native_widget_ (or other platform backends) and then read or write member state or pass this onward, since those calls can re-enter and free the object.
  • Synchronous observer/focus/capture callbacks
    Look for code paths where visibility, focus, activation, or capture changes fire observer notifications inline, and check whether any caller assumes the notifying object outlives the notification.
  • Missing WeakPtr guards around reentrant sinks
    Search for methods lacking a GetWeakPtr()/CHECK (or early-return) pattern around calls that are known to be capable of destroying the current object mid-method.
From 6080f65e48f96ee710060f982eb0a4a006919ac5 Mon Sep 17 00:00:00 2001
From: David Yeung <[email protected]>
Date: Sun, 13 Sep 2026 21:10:56 -0700
Subject: [PATCH] Guard against widget destruction during show and hide

Delegating window visibility changes to the native widget in
Widget::Show(), Widget::ShowInactive(), and Widget::Hide() can
synchronously trigger window observer callbacks, focus changes, or
capture loss that destroy the underlying aura window and free the Widget
instance before the call returns.

Fix is to acquire a WeakPtr and check if the widget was destroyed to
prevent any UAFs.

Fixed: 553136141
Change-Id: Ie27e7d9b46a23df033045ac6f6603362f16b5734
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8391999
Commit-Queue: Keren Zhu <[email protected]>
Reviewed-by: Keren Zhu <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1697039}
---
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 41f7e82c..8bae166 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -1120,6 +1120,7 @@
   ui::mojom::WindowShowState preferred_show_state =
       CanActivate() ? ui::mojom::WindowShowState::kNormal
                     : ui::mojom::WindowShowState::kInactive;
+  auto weak_this = GetWeakPtr();
   if (non_client_view_) {
     // While initializing, the kiosk mode will go to full screen before the
     // widget gets shown. In that case we stay in full screen mode, regardless
@@ -1131,12 +1132,14 @@
     } else {
       native_widget_->Show(saved_show_state_, gfx::Rect());
     }
+    CHECK(weak_this);
     // |saved_show_state_| only applies the first time the window is shown.
     // If we don't reset the value the window may be shown maximized every time
     // it is subsequently shown after being hidden.
     saved_show_state_ = preferred_show_state;
   } else {
     native_widget_->Show(preferred_show_state, gfx::Rect());
+    CHECK(weak_this);
   }
 
   HandleShowRequested();
@@ -1146,7 +1149,9 @@
   if (!native_widget_) {
     return;
   }
+  auto weak_this = GetWeakPtr();
   native_widget_->Hide();
+  CHECK(weak_this);
   internal::AnyWidgetObserverSingleton::GetInstance()->OnAnyWidgetHidden(this);
 }
 
@@ -1154,6 +1159,7 @@
   if (!native_widget_) {
     return;
   }
+  auto weak_this = GetWeakPtr();
   // If this gets called with saved_show_state_ ==
   // ui::mojom::WindowShowState::kMaximized, call SetBounds()with the restored
   // bounds to set the correct size. This normally should not happen, but if it
@@ -1161,9 +1167,11 @@
   if (saved_show_state_ == ui::mojom::WindowShowState::kMaximized &&
       !initial_restored_bounds_.IsEmpty()) {
     SetBounds(initial_restored_bounds_);
+    CHECK(weak_this);
     saved_show_state_ = ui::mojom::WindowShowState::kNormal;
   }
   native_widget_->Show(ui::mojom::WindowShowState::kInactive, gfx::Rect());
+  CHECK(weak_this);
 
   HandleShowRequested();
 }
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.