CVE-2026-8575
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
RenderWidgetHostNSViewBridgecontent/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
Patch
From 8b4415fa123afb8de47420ce4b7caa32b91e83b5 Mon Sep 17 00:00:00 2001
From: mikt <mikt@google.com>
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 <arthursonzogni@chromium.org>
Commit-Queue: Arthur Sonzogni <arthursonzogni@chromium.org>
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,
Original Bug Report
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.mmcontent/browser/renderer_host/render_widget_host_view_mac.mmcontent/browser/renderer_host/text_input_client_mac.mmcontent/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.
- Call Chain:
RenderWidgetHostNSViewBridge::SetTextSelectioncalls[cocoa_view_ setTextSelectionText:text offset:offset range:range]. Under specific conditions (when_shouldRequestTextSubstitutionsis true), this invokes[self requestTextSubstitutions]. - Nested RunLoop: The substitution logic calls
[self firstRectForCharacterRange:...], which then invokesTextInputClientMac::GetFirstRectForRange. If thekTextInputClientUseNestedLoopfeature is enabled, this method enters a nested RunLoop viabase::RunLoop::Run()withkNestableTasksAllowed. - 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(). - 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. - UAF Trigger: The attacker uses heap spraying to reallocate the freed
RenderWidgetHostNSViewBridgememory with controlled data. When the nested loop eventually finishes and the stack unwinds, execution returns toSetTextSelection(line 243). The code evaluatesif (![cocoa_view_ hasMarkedText]), reading thecocoa_view_member from the freedthispointer. - Type Confusion: The attacker-controlled pointer is used in an
objc_msgSendcall, 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.
- The attacker compromises a macOS renderer process.
- The attacker waits for or induces a single user keystroke to set
_shouldRequestTextSubstitutions = YESin the browser’sRenderWidgetHostViewCocoaobject. - The renderer sends a crafted
TextSelectionChangedIPC with an empty range and text that triggers a macOS spellchecker substitution. - This triggers the nested RunLoop in the browser process.
- The renderer immediately drops its IPC connection, queueing the destruction of
RenderWidgetHostViewMac. - The attacker utilizes heap spraying via standard web APIs (or a second compromised renderer) to reallocate the
RenderWidgetHostNSViewBridgeobject with a fake Objective-Cisapointer. - When the RunLoop exits, the browser process calls
objc_msgSendon 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.