Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds read in WebRTC
DescriptionOut of bounds read in WebRTC
ComponentWebRTC
Bug ClassOOB
Tracker513268100
Fix commitb343cd4e2809 (src) +3/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-27

Changed Functions

FunctionChangeNotes
if
modules/desktop_capture/mouse_cursor_monitor_mac.mm
modified

Files Changed

  • modules/desktop_capture/mouse_cursor_monitor_mac.mm
From b343cd4e280980f26b4feb80684f57d5b4324e12 Mon Sep 17 00:00:00 2001
From: Johannes Kron <kron@webrtc.org>
Date: Fri, 15 May 2026 10:21:29 +0000
Subject: [PATCH] Validate CGImage dimensions in MouseCursorMonitorMac

This adds missing height checks alongside the existing width checks
when processing and scaling the cursor image.

Fixed: chromium:513268100
Change-Id: Ida1e58334a9d3bda429819e4af51b33afbd0a95d
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/472700
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Auto-Submit: Johannes Kron <kron@webrtc.org>
Commit-Queue: Alexander Cooper <alcooper@chromium.org>
Cr-Commit-Position: refs/heads/main@{#47715}
---

diff --git a/modules/desktop_capture/mouse_cursor_monitor_mac.mm b/modules/desktop_capture/mouse_cursor_monitor_mac.mm
index 248a782..3e6a6cb 100644
--- a/modules/desktop_capture/mouse_cursor_monitor_mac.mm
+++ b/modules/desktop_capture/mouse_cursor_monitor_mac.mm
@@ -154,7 +154,8 @@
   // crbug.com/632995.) After 10.12, OSX may report 2X cursor on non-Retina
   // screen. (See crbug.com/671436.) So scaling the cursor if needed.
   CGImageRef scaled_cg_image = nil;
-  if (CGImageGetWidth(cg_image) != static_cast<size_t>(size.width())) {
+  if (CGImageGetWidth(cg_image) != static_cast<size_t>(size.width()) ||
+      CGImageGetHeight(cg_image) != static_cast<size_t>(size.height())) {
     scaled_cg_image =
         CreateScaledCGImage(cg_image, size.width(), size.height());
     if (scaled_cg_image != nil) {
@@ -163,6 +164,7 @@
   }
   if (CGImageGetBitsPerPixel(cg_image) != DesktopFrame::kBytesPerPixel * 8 ||
       CGImageGetWidth(cg_image) != static_cast<size_t>(size.width()) ||
+      CGImageGetHeight(cg_image) != static_cast<size_t>(size.height()) ||
       CGImageGetBitsPerComponent(cg_image) != 8) {
     if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image);
     return;
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential browser-process heap OOB read in MouseCursorMonitorMac::CaptureImage on macOS

Project Fortify, 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 heap out-of-bounds read vulnerability in the WebRTC desktop capture module allows an attacker to leak browser-process memory into a video capture stream on macOS. The flaw is caused by insufficient validation of a cursor’s image height before performing a pixel copy operation in the unsandboxed browser process. This can be potentially triggered by crafting a custom CSS cursor with specific dimensions and scale factors.

Affected files:

  • third_party/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm
  • third_party/webrtc/modules/desktop_capture/desktop_frame.cc

Estimated timestamp from git blame: 2017-06-02

Summary

A potential heap out-of-bounds (OOB) read exists in the Chromium browser process on macOS within the WebRTC desktop capture module. The function MouseCursorMonitorMac::CaptureImage fails to validate that the height of a retrieved CGImageRef matches the expected capture dimensions before copying its pixel data. This can lead to the exfiltration of sensitive browser-process heap memory into a video capture stream (e.g., during a screen sharing session).

Root Cause Analysis

In third_party/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm, the function CaptureImage calculates the target pixel size for the cursor in the capture frame based on its logical (DIP) size and the display scale:

// lines 140-141
DesktopSize size(round(nssize.width * scale),
                 round(nssize.height * scale));  // Expected pixel size

The code then obtains a CGImageRef representing the cursor. It contains logic to rescale the image if its width does not match the expected width, but it neglects to check the height:

// lines 157-163
if (CGImageGetWidth(cg_image) != static_cast<size_t>(size.width())) {
  scaled_cg_image = CreateScaledCGImage(cg_image, size.width(), size.height());
  if (scaled_cg_image != nil) {
    cg_image = scaled_cg_image;
  }
}

Furthermore, a subsequent validation block (lines 164-169) checks for bits per pixel, width, and bits per component, but completely omits a check for the image height.

Finally, the pixel data is copied into a DesktopFrame using image->CopyPixelsFrom:

// line 187
image->CopyPixelsFrom(src_data, src_stride, DesktopRect::MakeSize(size));

CopyPixelsFrom (defined in third_party/webrtc/modules/desktop_capture/desktop_frame.cc) uses libyuv::CopyPlane to copy size.height() rows from the source buffer. If the actual CGImage height is smaller than size.height(), libyuv will read past the end of the heap-allocated buffer containing the cursor pixels.

Potential Attack Vector

An attacker could potentially trigger this by providing a custom CSS cursor with specifically crafted dimensions and scale factors. For example, if a custom cursor image is 121 pixels high and assigned a scale factor of 0.95 by the renderer, the browser process will calculate a logical (DIP) height of floor(121 / 0.95) = 127. When capturing on a 1.0x display, size.height() becomes 127, while the actual bitmap in the CGImage remains 121 pixels high, leading to a 6-row OOB read.

This vulnerability occurs in the browser process where the desktop capture logic resides. The leaked memory from the browser’s heap is composited into the outgoing video stream, which can be observed by a remote peer or a compromised renderer process.

Suggested Fix

Ensure that the image height is validated and, if necessary, rescaled to match the target dimensions. The rescaling check should be updated:

if (CGImageGetWidth(cg_image) != static_cast<size_t>(size.width()) || 
    CGImageGetHeight(cg_image) != static_cast<size_t>(size.height())) {
  // Rescale logic...
}

Additionally, include a height check in the validation block at line 164:

if (CGImageGetHeight(cg_image) != static_cast<size_t>(size.height())) {
  return;
}

Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

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