CVE-2026-14011
Overview
Files Changed
third_party/blink/renderer/platform/video_capture/video_capture_impl.cc
Patch
From 526b422fff767028725eb80aa9ee2f1aca937034 Mon Sep 17 00:00:00 2001
From: Ilya Nikolaevskiy <ilnik@chromium.org>
Date: Mon, 01 Jun 2026 07:29:05 -0700
Subject: [PATCH] Ensure custom strides fit inside the provided buffer
Fixed: 516944556
Change-Id: I964141af3d97fe1323e69846aee9cf7417e7846b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7890379
Reviewed-by: Guido Urdaneta <guidou@chromium.org>
Commit-Queue: Ilya Nikolaevskiy <ilnik@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1639364}
---
diff --git a/third_party/blink/renderer/platform/video_capture/video_capture_impl.cc b/third_party/blink/renderer/platform/video_capture/video_capture_impl.cc
index 51871b3..b8dca09c 100644
--- a/third_party/blink/renderer/platform/video_capture/video_capture_impl.cc
+++ b/third_party/blink/renderer/platform/video_capture/video_capture_impl.cc
@@ -308,6 +308,14 @@
base::span<const uint8_t> data = buffer_context->data();
auto [y_data, uv_data] = data.split_at(y_size);
auto [u_data, v_data] = uv_data.split_at(u_size);
+ CHECK_GE(v_data.size(),
+ (media::VideoFrame::Rows(
+ media::VideoFrame::Plane::kU,
+ video_frame_init_data.ready_buffer->info->pixel_format,
+ video_frame_init_data.ready_buffer->info->coded_size
+ .height()) *
+ video_frame_init_data.ready_buffer->info->strides
+ ->stride_by_plane[2]));
video_frame_init_data.frame = media::VideoFrame::WrapExternalYuvData(
video_frame_init_data.ready_buffer->info->pixel_format,
gfx::Size(video_frame_init_data.ready_buffer->info->coded_size),
Original Bug Report
Heap OOB Read in Renderer via Unvalidated VideoFrame Strides from GPU Process
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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A potential vulnerability in Chromium’s video capture pipeline allows a compromised GPU/viz process to relay unvalidated video frame stride parameters to the renderer. Due to a lack of bounds verification in the renderer’s frame-wrapping and copying logic, this can lead to an out-of-bounds heap read. An attacker could potentially exploit this to leak adjacent renderer memory back to the GPU process or the capturing web page.
Affected files:
content/browser/media/capture/frame_sink_video_capture_device.ccthird_party/blink/renderer/platform/video_capture/video_capture_impl.ccthird_party/blink/renderer/modules/mediastream/web_media_player_ms_compositor.cc
Estimated timestamp from git blame: 2021-02-01
Potential Heap Out-Of-Bounds Read in Renderer via Video Capture Relay
Summary
A potential security vulnerability in the capture pipeline allows a compromised GPU/viz process to send unvalidated video frame stride parameters to a renderer. Because the renderer fails to verify that the allocated shared memory region is large enough to satisfy the custom strides of all planes, subsequent frame-copying operations can trigger an out-of-bounds (OOB) heap read, potentially exposing adjacent renderer memory.
Root Cause Analysis
-
Unvalidated Strides in
ProcessBuffer: Inthird_party/blink/renderer/platform/video_capture/video_capture_impl.cc, theVideoCaptureImpl::ProcessBufferfunction processeskUnsafeShmemRegionbuffers with custom strides. While it usessplit_at()to split the main shared memory span intoy_dataanduv_data, it only validates that the sum of the Y and U plane sizes does not exceed the buffer. It does not validate whether the remainingv_dataspan is large enough to cover the expected size of the V-plane (i.e.,Rows(Plane::kV, format, height) * stride_by_plane[2]):const size_t y_size = Rows(kY, fmt, coded_h) * stride[0]; const size_t u_size = Rows(kU, fmt, coded_h) * stride[1]; auto [y_data, uv_data] = data.split_at(y_size); auto [u_data, v_data] = uv_data.split_at(u_size);If the attacker specifies an arbitrarily large V-stride,
v_datais wrapped into aVideoFrameviamedia::VideoFrame::WrapExternalYuvDatadespite being truncated. -
Bypassing Span Guards during Copying: In
third_party/blink/renderer/modules/mediastream/web_media_player_ms_compositor.cc(insideReplaceCurrentFrameWithACopy), the raw pointers to the planes are retrieved usingframe->data(plane):libyuv::I420Copy(frame->data(media::VideoFrame::Plane::kY), frame->stride(media::VideoFrame::Plane::kY), frame->data(media::VideoFrame::Plane::kU), frame->stride(media::VideoFrame::Plane::kU), frame->data(media::VideoFrame::Plane::kV), frame->stride(media::VideoFrame::Plane::kV), ...);Because
frame->data(plane)returns raw pointers directly, it bypasses the bounds checking and subspan restrictions that would normally be enforced by callingframe->visible_data(plane)or checkingdata_span(plane). Whenlibyuv::I420Copyattempts to copy the V-plane using the malicious, massive stride, it reads heavily out-of-bounds on the renderer’s heap.
Suggested Potential Reproduction Steps
(Note: These are theoretical/potential steps, as our current tooling does not have the ability to execute or verify proof-of-concept code.)
- From a compromised GPU process, intercept or initiate an active screen/tab/window capture session.
- Invoke
OnFrameCapturedvia theviz::mojom::FrameSinkVideoConsumerMojo interface, passing a customVideoFrameInfowherestride_by_plane[2](V-plane stride) is set to an exceptionally large value (e.g.,1,000,000), while allocating a shared memory handle only large enough to cover the Y and U planes. - In the renderer, trigger a frame copy event (such as pausing the video capture self-preview or backgrounding the tab).
- Observe
libyuv::I420Copyperforming an out-of-bounds heap read based on the custom stride, copying adjacent heap data into the destination video frame.
Proposed Remediation
To fix this issue, ensure that the renderer validates all custom strides against the actual sizes of the respective spans before wrapping them into a VideoFrame.
Inside VideoCaptureImpl::ProcessBuffer:
- Verify that the final plane’s remaining buffer (
v_data) size is at least as large as the computed V-plane size (Rows(Plane::kV, format, height) * stride_by_plane[2]). If not, discard the buffer and drop the frame. - Additionally, update
WebMediaPlayerMSCompositorto retrieve spans or check dimensions prior to callinglibyuvcopy functions.
Evaluated with Chrome root at commit: b1520ef4a76878853a31f0943b565e42060edec8
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.