Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in UI
DescriptionUse after free in UI
ComponentUI
Bug ClassUAF
Tracker496217775
Fix commit8b4415fa123a (chromium/src) +4/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-12

Changed Functions

FunctionChangeNotes
RenderWidgetHostNSViewBridge
content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h
modified

Files Changed

  • content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h
From 8b4415fa123afb8de47420ce4b7caa32b91e83b5 Mon Sep 17 00:00:00 2001
From: mikt <[email protected]>
Date: Thu, 26 Mar 2026 06:46:37 -0700
Subject: [PATCH] Add AMSC macro to RenderWidgetHostNSViewBridge

Bug: 496217775
Change-Id: Id6404baf2740c27227a7cc97468f9320a1b0c691
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7703959
Reviewed-by: Arthur Sonzogni <[email protected]>
Commit-Queue: Arthur Sonzogni <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1605456}
---

diff --git a/content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h b/content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h
index 0990f44d..6e4df689 100644
--- a/content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h
+++ b/content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h
@@ -10,6 +10,7 @@
 
 #import <Cocoa/Cocoa.h>
 
+#include "base/memory/advanced_memory_safety_checks.h"
 #include "base/memory/weak_ptr.h"
 #include "components/remote_cocoa/app_shim/ns_view_ids.h"
 #import "content/app_shim_remote_cocoa/popup_window_mac.h"
@@ -31,6 +32,9 @@
 // be in a different process.
 class RenderWidgetHostNSViewBridge : public mojom::RenderWidgetHostNSView,
                                      public display::DisplayObserver {
+  // TODO(https://crbug.com/496217775): Remove this macro.
+  ADVANCED_MEMORY_SAFETY_CHECKS();
+
  public:
   RenderWidgetHostNSViewBridge(mojom::RenderWidgetHostNSViewHost* client,
                                RenderWidgetHostNSViewHostHelper* client_helper,
Loading diff…

Original Bug Report

reported by [email protected]

Potential UAF in RenderWidgetHostNSViewBridge::SetTextSelection via nested-loop reentrancy

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: A Use-After-Free vulnerability exists in the macOS browser process due to nested-loop reentrancy during text selection updates. If the RenderWidgetHostViewMac is destroyed while the nested loop is active, the associated C++ bridge object is freed, leading to a UAF when execution returns to the bridge. An attacker could potentially leverage this type-confusion primitive via Objective-C message passing to achieve Remote Code Execution.

Affected files:

  • content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.mm
  • content/browser/renderer_host/render_widget_host_view_mac.mm
  • content/browser/renderer_host/text_input_client_mac.mm
  • content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm

Estimated timestamp from git blame: 2023-07-06

Vulnerability Description

A potential Use-After-Free (UAF) vulnerability has been identified in the macOS browser process, specifically within the RenderWidgetHostNSViewBridge class. This issue is triggered by nested-loop reentrancy during text selection processing. A compromised renderer could potentially leverage this flaw to achieve Remote Code Execution (RCE) and Sandbox Escape.

Technical Details

The vulnerability is located in content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.mm. When a text selection change occurs, RenderWidgetHostViewMac::OnTextSelectionChanged makes a direct virtual call to RenderWidgetHostNSViewBridge::SetTextSelection. This method then synchronously calls into the Objective-C Cocoa view.

  1. Call Chain: RenderWidgetHostNSViewBridge::SetTextSelection calls [cocoa_view_ setTextSelectionText:text offset:offset range:range]. Under specific conditions (when _shouldRequestTextSubstitutions is true), this invokes [self requestTextSubstitutions].
  2. Nested RunLoop: The substitution logic calls [self firstRectForCharacterRange:...], which then invokes TextInputClientMac::GetFirstRectForRange. If the kTextInputClientUseNestedLoop feature is enabled, this method enters a nested RunLoop via base::RunLoop::Run() with kNestableTasksAllowed.
  3. Object Destruction: While the browser UI thread is spinning in this nested loop, it can process other queued tasks and IPCs. A compromised renderer can drop its IPC channel or crash, queueing a disconnection task that triggers RenderWidgetHostViewMac::Destroy().
  4. BRP Bypass: Inside Destroy(), the code clears its raw pointer to the bridge (ns_view_ = nullptr;) before destroying the bridge object (in_process_ns_view_bridge_.reset();). This drops the BackupRefPtr (BRP) refcount to 0, immediately returning the memory to the partition allocator and bypassing MiraclePtr protection.
  5. UAF Trigger: The attacker uses heap spraying to reallocate the freed RenderWidgetHostNSViewBridge memory with controlled data. When the nested loop eventually finishes and the stack unwinds, execution returns to SetTextSelection (line 243). The code evaluates if (![cocoa_view_ hasMarkedText]), reading the cocoa_view_ member from the freed this pointer.
  6. Type Confusion: The attacker-controlled pointer is used in an objc_msgSend call, providing a powerful type-confusion primitive that could lead to arbitrary RCE.

Proposed Steps to Trigger (Theoretical)

Note: As an LLM agent, I have not verified this exploit with a working Proof of Concept.

  1. The attacker compromises a macOS renderer process.
  2. The attacker waits for or induces a single user keystroke to set _shouldRequestTextSubstitutions = YES in the browser’s RenderWidgetHostViewCocoa object.
  3. The renderer sends a crafted TextSelectionChanged IPC with an empty range and text that triggers a macOS spellchecker substitution.
  4. This triggers the nested RunLoop in the browser process.
  5. The renderer immediately drops its IPC connection, queueing the destruction of RenderWidgetHostViewMac.
  6. The attacker utilizes heap spraying via standard web APIs (or a second compromised renderer) to reallocate the RenderWidgetHostNSViewBridge object with a fake Objective-C isa pointer.
  7. When the RunLoop exits, the browser process calls objc_msgSend on the attacker-controlled pointer, yielding RCE.

Proposed Fix

To prevent this UAF, the C++ stack frame must ensure the this pointer remains valid after the synchronous Objective-C call returns.

One approach is to use a base::WeakPtr within RenderWidgetHostNSViewBridge::SetTextSelection to check if the object has been destroyed during the nested loop:

void RenderWidgetHostNSViewBridge::SetTextSelection(const std::u16string& text,
                                                    uint64_t offset,
                                                    const gfx::Range& range) {
  base::WeakPtr<RenderWidgetHostNSViewBridge> weak_this = weak_factory_.GetWeakPtr();
  [cocoa_view_ setTextSelectionText:text offset:offset range:range];

  if (!weak_this) {
    return;
  }

  // Updates markedRange when there is no marked text so that retrieving
  // markedRange immediately after calling setMarkedText: returns the current
  // caret position.
  if (![cocoa_view_ hasMarkedText]) {
    [cocoa_view_ setMarkedRange:range.ToNSRange()];
  }
}

Evaluated with Chrome root at commit: 0eb4855bda702feaaa8b899336664f97e3df88b8


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. Please feel free to reach out to me if you have concerns or feedback.

View on issue tracker