Overview

Critical
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Browser
DescriptionUse after free in Browser
ComponentBrowser
Bug ClassUAF
Tracker508289938
Fix commit7a43db7d3d42 (chromium/src) +9/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-27

Changed Functions

FunctionChangeNotes
if
chrome/browser/device_reauth/mac/device_authenticator_mac.mm
modified

Files Changed

  • chrome/browser/device_reauth/mac/device_authenticator_mac.mm
From 7a43db7d3d42a665df15826decddcd7109028d10 Mon Sep 17 00:00:00 2001
From: Avi Drissman <[email protected]>
Date: Mon, 04 May 2026 07:50:53 -0700
Subject: [PATCH] Guard against nested run loop issues in DeviceAuthenticatorMac

DeviceAuthenticatorMac can fall back to showing a dialog which uses a
nested run loop. For that case, use weak pointers appropriately to avoid
any UaF issues.

Fixed: 508289938
Link: https://chromium-review.googlesource.com/id/I1e8c2c85ea4d1d4ab245bccfcc910fa26a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7807746
Commit-Queue: Avi Drissman <[email protected]>
Reviewed-by: Ioana Treib <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1624662}
---

diff --git a/chrome/browser/device_reauth/mac/device_authenticator_mac.mm b/chrome/browser/device_reauth/mac/device_authenticator_mac.mm
index aa2414df..c9cb06d 100644
--- a/chrome/browser/device_reauth/mac/device_authenticator_mac.mm
+++ b/chrome/browser/device_reauth/mac/device_authenticator_mac.mm
@@ -85,9 +85,16 @@
   // API, and if it fails use password_manager_util_mac::AuthenticateUser()
   // instead, until crbug.com/40236979 is fixed.
   if (!CanAuthenticateWithBiometrics()) {
-    OnAuthenticationCompleted(authenticator_->AuthenticateUserWithNonBiometrics(
+    // AuthenticateUserWithNonBiometrics runs a dialog with a nested run loop,
+    // so protect against this page disappearing within that nested run loop.
+    // https://crbug.com/508289938
+    auto weak_this = weak_ptr_factory_.GetWeakPtr();
+    bool success = authenticator_->AuthenticateUserWithNonBiometrics(
         l10n_util::GetStringFUTF16(IDS_PASSWORDS_AUTHENTICATION_PROMPT_PREFIX,
-                                   message)));
+                                   message));
+    if (weak_this) {
+      weak_this->OnAuthenticationCompleted(success);
+    }
     return;
   }
 
Loading diff…

Original Bug Report

reported by [email protected]

Potential Use-After-Free in DeviceAuthenticatorMac due to nested run loop

Flapjack, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A potential Use-After-Free (UAF) vulnerability exists in DeviceAuthenticatorMac when falling back to synchronous system password authentication on macOS. The blocking OS call spins a nested event loop, allowing IPCs to destroy the authenticator instance while its this pointer is already cached on the stack. When the call returns, execution continues using the dangling pointer, potentially leading to arbitrary code execution in the browser process.

Affected files:

  • chrome/browser/device_reauth/mac/device_authenticator_mac.mm
  • chrome/browser/device_reauth/mac/authenticator_mac.mm
  • chrome/browser/password_manager/password_manager_util_mac.mm

Estimated timestamp from git blame: 2023-03-03

Summary

A potential Use-After-Free (UAF) vulnerability exists in DeviceAuthenticatorMac::AuthenticateWithMessage on macOS. When biometric authentication is unavailable, the code falls back to a synchronous OS password prompt. This system call spins a nested macOS event loop, allowing Chromium’s main thread to continue processing IPC messages. If a malicious page triggers frame destruction (e.g., via window.close()) while the prompt is active, the DeviceAuthenticatorMac instance is freed. Upon the prompt’s dismissal, the browser resumes execution using a cached, dangling this pointer, which can be leveraged for Arbitrary Code Execution (ACE) in the browser process.

Technical Details

In chrome/browser/device_reauth/mac/device_authenticator_mac.mm, the AuthenticateWithMessage function handles device re-authentication. If Touch ID is unavailable, it performs a synchronous fallback:

  if (!CanAuthenticateWithBiometrics()) {
    OnAuthenticationCompleted(authenticator_->AuthenticateUserWithNonBiometrics(
        l10n_util::GetStringFUTF16(IDS_PASSWORDS_AUTHENTICATION_PROMPT_PREFIX,
                                   message)));
    return;
  }
  1. Evaluation Order: Under C++17 evaluation rules for member function calls (E1.E2(E3)), the postfix-expression (the this pointer for OnAuthenticationCompleted) is evaluated and cached in a register or on the stack before the argument expression (authenticator_->...) is evaluated.
  2. Blocking Call & Nested Loop: AuthenticateUserWithNonBiometrics eventually invokes AuthorizationCopyRights via password_manager_util_mac::AuthenticateUser. This is a blocking macOS system API that displays a modal password dialog. To keep the application responsive, it spins a nested CFRunLoop. Because Chromium’s MessagePumpCFRunLoop is registered to kCFRunLoopCommonModes, it continues to pump tasks and Mojo IPCs from renderers while the dialog is visible.
  3. Object Destruction: If the renderer sends an IPC that destroys the WebContents (e.g., a timer firing window.close()), the destruction is processed synchronously during the nested loop. This destroys the ContentPasswordManagerDriverFactory, the ContentPasswordManagerDriver, the PasswordAutofillManager, and ultimately the DeviceAuthenticatorMac instance. The backing memory is freed.
  4. Use-After-Free: When the user dismisses the system dialog, AuthorizationCopyRights returns. Execution resumes in AuthenticateWithMessage, which uses the previously cached (and now dangling) this pointer to call OnAuthenticationCompleted.

Inside OnAuthenticationCompleted, a write occurs (touch_id_auth_context_ = nullptr;), followed by a read and execution of a base::OnceCallback (std::move(callback_).Run(success);). Because the implicit this pointer is held on the stack during the call, it is not protected by MiraclePtr (BackupRefPtr). An attacker who reclaims the freed memory via heap spraying can forge the callback_ member, leading to Arbitrary Code Execution in the browser process.

Potential Reproduction Steps

Note: Our tooling agent cannot execute code, so these are suggested steps based on static analysis.

  1. On a macOS device where Touch ID is disabled or unavailable, an attacker hosts a malicious page with a password field.
  2. The attacker’s JavaScript sets a setTimeout to call window.close() after a brief delay.
  3. The user interacts with the password field and selects a saved credential, triggering an Autofill re-authentication request to the browser process.
  4. The browser invokes DeviceAuthenticatorMac::AuthenticateWithMessage, bringing up the blocking OS password dialog.
  5. While the dialog is visible, the setTimeout fires, sending the window.close() IPC to the browser.
  6. The browser processes the IPC in the nested CFRunLoop, destroying the frame and freeing the DeviceAuthenticatorMac object.
  7. The attacker’s JavaScript (e.g., from another open window or a Web Worker) performs heap spraying to reclaim the freed memory with a forged base::OnceCallback.
  8. The user clicks “Cancel” on the OS password prompt.
  9. The system call returns, and the browser executes the hijacked callback, achieving code execution.

Suggested Fix

Do not call OnAuthenticationCompleted directly using the implicit this pointer inline with the synchronous blocking call. Instead, evaluate the result first, and use a base::WeakPtr to safely check if the object survived the nested run loop before proceeding.

  if (!CanAuthenticateWithBiometrics()) {
    base::WeakPtr<DeviceAuthenticatorMac> weak_this = weak_ptr_factory_.GetWeakPtr();
    bool success = authenticator_->AuthenticateUserWithNonBiometrics(
        l10n_util::GetStringFUTF16(IDS_PASSWORDS_AUTHENTICATION_PROMPT_PREFIX,
                                   message));
    if (weak_this) {
      weak_this->OnAuthenticationCompleted(success);
    }
    return;
  }

Evaluated with Chrome root at commit: cc901875d53bf4e4fe0e01f02843871da4106e70


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker