CVE-2026-91716
Overview
Background
- `HttpAuthCoordinator::Flow`
- the per-request state object in
chrome/browser/ui/login/http_auth_coordinator.ccthat drives a single HTTP authentication challenge and owns the resultinglogin_handler_. - `CreateLoginDelegateFromLoginHandler`
- a coordinator call that synchronously creates and shows the HTTP auth login prompt for a subresource, blocking the associated
WebContents. - Nested message loop
- a re-entrant run of the browser UI event loop, here spun while exiting HTML fullscreen, during which unrelated callbacks (including cancellation of the auth challenge) can execute and destroy live objects.
- `base::WeakPtr`
- a smart pointer that automatically becomes null when the object it references is destroyed, letting callers test liveness after a re-entrant call.
Root Cause Analysis
Inside the subresource branch of the edited Flow method, the pre-patch code assigned the result of CreateLoginDelegateFromLoginHandler(...) directly into the member login_handler_. Showing the prompt synchronously blocks the WebContents and drops HTML fullscreen, and exiting fullscreen can spin a nested message loop (per the comment referencing WebContentsImpl::ExitFullscreenMode(), crbug.com/1506535, crbug.com/498752242). During that nested loop the auth challenge can be cancelled, which synchronously destroys the Flow (this), so the subsequent write login_handler_ = ... dereferences and stores into freed memory — a use-after-free. The invariant violated is that this must remain valid across any call that can re-enter the message loop before its members are touched.
The fix captures base::WeakPtr<Flow> weak_this before the call, holds the new delegate in a local std::unique_ptr, and checks if (!weak_this) afterward, returning early (destroying the local unique_ptr, which closes the just-created prompt) instead of writing to the freed member.
login_handler_) after a synchronous call that can re-enter the message loop and destroy this; the fix adds a WeakPtr liveness check between the re-entrant call and the member write, so the assignment only happens when this is still alive.Attack Path
- Trigger a subresource auth challenge in fullscreen
Cause a page that is in HTML fullscreen to issue a subresource request that returns an HTTP authentication challenge, routing execution into the
Flowmethod that callsCreateLoginDelegateFromLoginHandler. - Force a nested message loop
Showing the prompt blocks the
WebContentsand drops fullscreen; exiting fullscreen spins a nested message loop, as noted in the added comment. - Cancel the challenge during re-entry
Within the nested loop, cause the auth challenge to be cancelled so the owning
Flow(this) is synchronously destroyed whileCreateLoginDelegateFromLoginHandleris still on the stack. - Use-after-free write
When the call returns, the pre-patch code executes
login_handler_ = ..., writing through the freedFlowobject, corrupting freed heap memory. - Escalation
Controlling the contents/reuse of the freed
Flowallocation could turn the write into a more powerful primitive (inferred); the patch itself demonstrates only the UAF write.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/ui/login/http_auth_coordinator.cc |
modified |
Files Changed
chrome/browser/ui/login/http_auth_coordinator.cc
Audit Directions
- Member write after a re-entrant callLook for code that stores into a member (or otherwise dereferences
this) immediately after a synchronous call that can show UI, block aWebContents, or spin a nested message loop, without aWeakPtrliveness re-check. - Fullscreen-exit re-entrancyAudit call sites that can trigger
WebContentsImpl::ExitFullscreenMode()(directly or via blocking theWebContents), since it may spin a nested loop that destroys objects mid-call. - Cancellable request/challenge lifetimesReview other per-request coordinator/flow objects (auth, permissions, dialogs) whose owning object can be cancelled and destroyed while a create/show call is still on the stack.
Patch
From 3926ac636fb2362c3169fff9368abfd72f6fc20b Mon Sep 17 00:00:00 2001
From: Carlos IL <carlosil@chromium.org>
Date: Fri, 04 Sep 2026 11:15:02 -0700
Subject: [PATCH] Check validity of 'this' in HttpAuthCoordinator when showing http auth dialog.
Fixed: 554558368
Change-Id: I82fb4b38bedf4c08aac492947c0e2212bd991ef4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8349451
Commit-Queue: Carlos IL <carlosil@chromium.org>
Reviewed-by: Joe DeBlasio <jdeblasio@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1692604}
---
diff --git a/chrome/browser/ui/login/http_auth_coordinator.cc b/chrome/browser/ui/login/http_auth_coordinator.cc
index f98e019..2e7a2dd 100644
--- a/chrome/browser/ui/login/http_auth_coordinator.cc
+++ b/chrome/browser/ui/login/http_auth_coordinator.cc
@@ -221,9 +221,23 @@
// For subresources, create a LoginHandler which will show a login prompt.
auto wrapped_callback = base::BindOnce(&Flow::OnCredentials, GetWeakPtr());
- login_handler_ = coordinator_->CreateLoginDelegateFromLoginHandler(
- web_contents_.get(), auth_info_, request_id_, url_, response_headers_,
- std::move(wrapped_callback));
+ // Showing the login prompt synchronously blocks the WebContents, which
+ // drops HTML fullscreen. Exiting fullscreen can spin a nested message loop
+ // (see the comment in WebContentsImpl::ExitFullscreenMode(),
+ // crbug.com/1506535, crbug.com/498752242) in which the auth challenge can
+ // be cancelled, synchronously destroying `this`. Check liveness before
+ // touching any member.
+ base::WeakPtr<Flow> weak_this = GetWeakPtr();
+ std::unique_ptr<content::LoginDelegate> login_handler =
+ coordinator_->CreateLoginDelegateFromLoginHandler(
+ web_contents_.get(), auth_info_, request_id_, url_, response_headers_,
+ std::move(wrapped_callback));
+ if (!weak_this) {
+ // `this` was destroyed while the prompt was being shown. Dropping
+ // `login_handler` closes the just-created prompt.
+ return;
+ }
+ login_handler_ = std::move(login_handler);
}
base::WeakPtr<HttpAuthCoordinator::Flow>