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 <[email protected]> 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 <[email protected]> Reviewed-by: Joe DeBlasio <[email protected]> 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>