CVE-2026-11096
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc |
modified |
Files Changed
third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
Patch
From 626adaa56e77945e51227c1189746d1b90076e79 Mon Sep 17 00:00:00 2001 From: Ilya Nikolaevskiy <[email protected]> Date: Fri, 10 Apr 2026 07:15:19 -0700 Subject: [PATCH] Use correct dimensions in CreateNV12SharedImageFrame in RtcVideoEncoder The existing code doesn't take into account that ToI420() may already crop and downscale the image. Fixed: 500296311 Change-Id: I64aa22f26c9021346c18edd92af6405149df4623 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7735747 Reviewed-by: Dale Curtis <[email protected]> Commit-Queue: Ilya Nikolaevskiy <[email protected]> Cr-Commit-Position: refs/heads/main@{#1612839} --- diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc index 68ea3218..5ec5b6a 100644 --- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc +++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc @@ -2147,13 +2147,16 @@ CHECK(!input_buffers_free_.empty()); TRACE_EVENT1("webrtc", "RTCVideoEncoder::Impl::CreateNV12SharedImageFrame", "visible_rect", frame->visible_rect().ToString()); + + // ToI420() below may rescale the image. It will always output the + // resolution reported by VideoFrameBuffer. + const gfx::Size frame_size(frame_buffer.width(), frame_buffer.height()); + const int index = input_buffers_free_.back(); scoped_refptr<gpu::ClientSharedImage>& nv12_shared_image = input_buffers_[index].nv12_shared_image; - if (!nv12_shared_image || - nv12_shared_image->size() != frame->visible_rect().size()) { - nv12_shared_image = - CreateClientSharedImage(gpu_factories_, frame->visible_rect().size()); + if (!nv12_shared_image || nv12_shared_image->size() != frame_size) { + nv12_shared_image = CreateClientSharedImage(gpu_factories_, frame_size); if (!nv12_shared_image) { NotifyErrorStatus({media::EncoderStatus::Codes::kSystemAPICallError, "Failed to allocate shared image"}); @@ -2180,8 +2183,8 @@ uint8_t* dst_uv = mapping->GetMemoryForPlane(1).data(); const size_t dst_y_stride = mapping->Stride(0); const size_t dst_uv_stride = mapping->Stride(1); - const size_t width = frame->visible_rect().width(); - const size_t height = frame->visible_rect().height(); + const size_t width = frame_size.width(); + const size_t height = frame_size.height(); if (libyuv::I420ToNV12(i420_buffer->DataY(), i420_buffer->StrideY(), i420_buffer->DataU(), i420_buffer->StrideU(), i420_buffer->DataV(), i420_buffer->StrideV(), dst_y, @@ -2201,7 +2204,7 @@ // The timestamp is set later in EncodeOneFrameWithNativeInput(). frame = media::VideoFrame::WrapMappableSharedImage( nv12_shared_image, sync_token, base::NullCallback(), - frame->visible_rect(), frame->visible_rect().size(), base::TimeDelta()); + gfx::Rect(frame_size), frame_size, base::TimeDelta()); if (!frame) { NotifyErrorStatus({media::EncoderStatus::Codes::kEncoderFailedEncode, "Failed to create video frame"});
Original Bug Report
Potential Heap OOB Read in RTCVideoEncoder::Impl::CreateNV12SharedImageFrame
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 security team.
Overview: A potential heap out-of-bounds read exists in RTCVideoEncoder when processing a crafted VideoFrame. If a frame has a visible_rect larger than its natural_size, the encoder scales the frame down but attempts to read from the scaled buffer using the original, larger dimensions, resulting in an OOB read. This leaked memory can be encoded and transmitted over WebRTC.
Affected files:
third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
Estimated timestamp from git blame: 2025-10-02
Description
A potential heap out-of-bounds (OOB) read vulnerability exists in the renderer process on platforms supporting hardware video encoding with native input (e.g., ChromeOS). The issue is located in RTCVideoEncoder::Impl::CreateNV12SharedImageFrame within third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc.
The vulnerability is triggered when a VideoFrame is created with a visible_rect that is significantly larger than its natural_size (the display size). While Blink and the media layer validate that visible_rect fits within coded_size, they do not enforce that visible_rect must be smaller than or equal to natural_size.
When such a frame is routed to the hardware encoder (e.g., via a MediaStreamTrackGenerator), RTCVideoEncoder::Impl::EncodeOneFrameWithNativeInput may decide to convert it to an NV12 SharedImage.
- It calls
CreateNV12SharedImageFrame. - The method allocates a new NV12 SharedImage using the dimensions of the original frame’s
visible_rect. - It then extracts an I420 buffer by calling
frame_buffer.ToI420(). Becausenatural_sizediffers fromvisible_rect, theConvertToWebRtcVideoFrameBufferutility downscales the frame to matchnatural_size. The returnedI420Bufferis only large enough to hold thenatural_sizedimensions. - However,
CreateNV12SharedImageFrameextracts the width and height for the copy operation from the original frame’svisible_rect:const size_t width = frame->visible_rect().width(); const size_t height = frame->visible_rect().height(); libyuv::I420ToNV12is then invoked with these large dimensions, causing it to read far past the bounds of the small, downscaledI420Buffer.
Impact
This is a renderer-process information leak. The massive OOB read copies adjacent renderer heap memory into the NV12 SharedImage. This image is then passed to the hardware encoder, compressed, and sent over the active RTCPeerConnection. A remote attacker can decode the stream to view or extract the leaked heap contents.
Suggested Reproduction Steps
Note: These are potential steps as our tooling cannot execute a full Proof-of-Concept.
- On a platform with a hardware WebRTC encoder (e.g., ChromeOS), host a page that creates a large 1920x1080 canvas and generates an
ImageBitmap. - Use WebCodecs to create a
VideoFramefrom the bitmap with mismatched dimensions:visibleRect: {x: 0, y: 0, width: 1920, height: 1080}displayWidth: 640displayHeight: 480
- Push this frame into a
MediaStreamTrackGeneratorconfigured for video. - Add the track to an
RTCPeerConnectionand connect to a remote peer. - Observe an ASAN heap-buffer-overflow READ crash in
libyuv::I420ToNV12(or underlying copy functions) within the renderer process. On a non-ASAN build, observe the leaked memory in the decoded video stream on the remote peer.
Suggested Fix
In RTCVideoEncoder::Impl::CreateNV12SharedImageFrame, do not use the original frame->visible_rect() to determine the copy dimensions. Instead, use the actual dimensions of the extracted i420_buffer:
const size_t width = i420_buffer->width();
const size_t height = i420_buffer->height();
Additionally, the nv12_shared_image allocation and the final WrapMappableSharedImage call should also use these dimensions to ensure consistency across the conversion process.
Evaluated with Chrome root at commit: f200f57a19490707ff8bc7aa5de3cbc443a3afad
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. And please feel free to reach out to me directly if you have concerns or feedback on the project.