CVE-2026-12033
Overview
Files Changed
third_party/blink/renderer/platform/video_capture/video_capture_impl.cc
Patch
From e441cd425ca137110ce591ab693db1e6db65f71c Mon Sep 17 00:00:00 2001 From: Ilya Nikolaevskiy <[email protected]> Date: Wed, 03 Jun 2026 05:05:46 -0700 Subject: [PATCH] Ensure strides are valid in VideoCaptureImpl Stride can't be less than a row width. Fixed: 519248779 Change-Id: I6ef96efb1aaa1e199cccb153a0968c54ef62c7c0 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7895488 Commit-Queue: Ilya Nikolaevskiy <[email protected]> Reviewed-by: Guido Urdaneta <[email protected]> Cr-Commit-Position: refs/heads/main@{#1640847} --- 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 b8dca09c..98ad94d 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 @@ -289,6 +289,21 @@ (media::VideoFrame::NumPlanes( video_frame_init_data.ready_buffer->info->pixel_format) == 3)) << "Currently, only YUV formats support custom strides."; + const auto pixel_format = + video_frame_init_data.ready_buffer->info->pixel_format; + const auto coded_width = + video_frame_init_data.ready_buffer->info->coded_size.width(); + const auto& strides = + video_frame_init_data.ready_buffer->info->strides->stride_by_plane; + CHECK_GE(static_cast<size_t>(strides[0]), + media::VideoFrame::RowBytes(media::VideoFrame::Plane::kY, + pixel_format, coded_width)); + CHECK_GE(static_cast<size_t>(strides[1]), + media::VideoFrame::RowBytes(media::VideoFrame::Plane::kU, + pixel_format, coded_width)); + CHECK_GE(static_cast<size_t>(strides[2]), + media::VideoFrame::RowBytes(media::VideoFrame::Plane::kV, + pixel_format, coded_width)); const size_t y_size = (media::VideoFrame::Rows( media::VideoFrame::Plane::kY,
Original Bug Report
Out-of-bounds read in VideoCaptureImpl via custom strides less than minimum row bytes
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 out-of-bounds read can occur in the renderer process during video capture when custom strides are used. A compromised GPU process could send a video frame with custom strides smaller than the minimum row bytes for the specified width, bypassing the checks in VideoCaptureImpl. This can lead to an out-of-bounds read inside libyuv::I420Copy.
Affected files:
third_party/blink/renderer/platform/video_capture/video_capture_impl.ccmedia/base/video_frame.cc
Estimated timestamp from git blame: 2018-09-18
Description
A potential out-of-bounds read vulnerability has been identified in VideoCaptureImpl in the renderer process.
In VideoCaptureImpl::ProcessBuffer (third_party/blink/renderer/platform/video_capture/video_capture_impl.cc:285-331), when a video frame is processed with custom strides via the kUnsafeShmemRegion handle path, the code verifies that the backing shared memory span is large enough to contain rows * stride for each plane. However, it does not validate that the custom stride is at least as large as the minimum required bytes per row for the given width (i.e., stride >= VideoFrame::RowBytes(...)).
Since VideoFrame::WrapExternalYuvData does not perform further validation of strides against width, a VideoFrame can be created with an extremely small stride but a large width. Subsequent consumers of this frame, such as WebMediaPlayerMSCompositor::CopyFrame (which utilizes libyuv::I420Copy), copy data based on the frame’s logical width. This causes memcpy operations to read past the boundary of the shared memory mapping.
Suggested Attack Flow (Potential Steps)
Note: The following are potential steps based on source code analysis; our analysis tooling does not have the ability to execute code to verify this.
- A compromised GPU process calls
OnFrameCapturedvia theviz::mojom::FrameSinkVideoConsumerremote. - The IPC payload specifies a tiny shared memory region (e.g., 8 bytes), a large frame size (e.g.,
coded_size = {8192, 4}), and invalid strides (e.g.,strides = {1, 1, 1, 0}). - The browser process relays this frame to the renderer process without validation.
- The renderer process maps the 8-byte shared memory buffer in
BufferContext. - In
VideoCaptureImpl::ProcessBuffer, the validationRows * stridechecks pass because4 * 1 = 4(Y plane) and2 * 1 = 2(U/V planes) fit within the 8-byte buffer. - The
VideoFrameis created with a logical width of 8192 and strides of 1. - When the video is paused or copied in
WebMediaPlayerMSCompositor::ReplaceCurrentFrameWithACopy,libyuv::I420Copyattempts to copy 8192 bytes per row. - This results in an out-of-bounds read of up to 32 KiB from the renderer’s heap.
Code References
-
Vulnerable Code Location:
third_party/blink/renderer/platform/video_capture/video_capture_impl.cc:285-331 -
Missing Check Reference: In contrast, the sibling
FitsInContiguousBufferOfSizecheck (used by the non-strides path) correctly enforces this check:media/base/video_frame_layout.cc:254size_t row_bytes = VideoFrame::RowBytes(plane_idx, format_, coded_size_.width()); if (plane.stride < row_bytes) { return false; }
Suggested Fix
To mitigate this issue, validate that custom strides are not less than the minimum required row bytes for the specified width.
For example, in third_party/blink/renderer/platform/video_capture/video_capture_impl.cc, add the following validation inside the strides branch:
for (size_t i = 0; i < 3; ++i) {
size_t row_bytes = media::VideoFrame::RowBytes(
i, video_frame_init_data.ready_buffer->info->pixel_format,
video_frame_init_data.ready_buffer->info->coded_size.width());
if (video_frame_init_data.ready_buffer->info->strides->stride_by_plane[i] < row_bytes) {
return false;
}
}
Evaluated with Chrome root at commit: 87214e6721f6c34afd9181b80769a24c0c601c50
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.