Low chrome OOB 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactHeap buffer overflow in WebRTC
DescriptionHeap buffer overflow in WebRTC
ComponentWebRTC
Bug ClassOOB
Tracker517180511
Fix commit730e67fe7bd4 (chromium/src) +121/-62
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Files Changed

  • third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.cc
From 730e67fe7bd4418e36cf5224193e6941b983c989 Mon Sep 17 00:00:00 2001
From: Ilya Nikolaevskiy <[email protected]>
Date: Tue, 02 Jun 2026 01:11:43 -0700
Subject: [PATCH] Fix potential OOB in ConvertFromMappedWebRtcVideoFrameBuffer

Currently the planes are spans which may include extra padding after the last pixel due to stride being more than the width, since the size is simply stride*rows. But there may be no extra padding there, it may be OOB. Technically it's fine using such spans, as accessing wrong address would require accessing pixel after the last pixel in the last row, but if some user manipulated whole spans, it would be OOB.

Correct span sizes should be (stride-1)*rows+width.

Fixed: 517180511
Change-Id: I2bf9a795f7aeac68cd92905dd6fee490121670e7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7889536
Commit-Queue: Ilya Nikolaevskiy <[email protected]>
Reviewed-by: Guido Urdaneta <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1639990}
---

diff --git a/third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.cc b/third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.cc
index 5f984f26..eb71def 100644
--- a/third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.cc
+++ b/third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.cc
@@ -344,25 +344,34 @@
       const webrtc::I420ABufferInterface* yuva_buffer = buffer->GetI420A();
       const media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_I420A;
       const size_t luma_rows = size.height();
-      const size_t chroma_rows =
-          media::VideoFrame::PlaneSizeInSamples(
-              pixel_format, media::VideoFrame::Plane::kU, size)
-              .height();
+      const size_t luma_width = size.width();
+      const gfx::Size chroma_size = media::VideoFrame::PlaneSizeInSamples(
+          pixel_format, media::VideoFrame::Plane::kU, size);
+      const size_t chroma_width = chroma_size.width();
+      const size_t chroma_rows = chroma_size.height();
       // TODO(issues.webrtc.org/issues/42225170):
       // webrtc::I420ABufferInterface should expose ArrayView instead of raw
       // pointers.
       auto y_plane = UNSAFE_TODO(base::span<const uint8_t>(
           yuva_buffer->DataY(),
-          base::saturated_cast<size_t>(yuva_buffer->StrideY()) * luma_rows));
+          base::saturated_cast<size_t>(yuva_buffer->StrideY()) *
+                  (luma_rows - 1) +
+              luma_width));
       auto u_plane = UNSAFE_TODO(base::span<const uint8_t>(
           yuva_buffer->DataU(),
-          base::saturated_cast<size_t>(yuva_buffer->StrideU()) * chroma_rows));
+          base::saturated_cast<size_t>(yuva_buffer->StrideU()) *
+                  (chroma_rows - 1) +
+              chroma_width));
       auto v_plane = UNSAFE_TODO(base::span<const uint8_t>(
           yuva_buffer->DataV(),
-          base::saturated_cast<size_t>(yuva_buffer->StrideV()) * chroma_rows));
+          base::saturated_cast<size_t>(yuva_buffer->StrideV()) *
+                  (chroma_rows - 1) +
+              chroma_width));
       auto a_plane = UNSAFE_TODO(base::span<const uint8_t>(
           yuva_buffer->DataA(),
-          base::saturated_cast<size_t>(yuva_buffer->StrideA()) * luma_rows));
+          base::saturated_cast<size_t>(yuva_buffer->StrideA()) *
+                  (luma_rows - 1) +
+              luma_width));
       video_frame = media::VideoFrame::WrapExternalYuvaData(
           pixel_format, size, gfx::Rect(size), size, yuva_buffer->StrideY(),
           yuva_buffer->StrideU(), yuva_buffer->StrideV(),
@@ -374,22 +383,29 @@
       const webrtc::I420BufferInterface* yuv_buffer = buffer->GetI420();
       const media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_I420;
       const size_t luma_rows = size.height();
-      const size_t chroma_rows =
-          media::VideoFrame::PlaneSizeInSamples(
-              pixel_format, media::VideoFrame::Plane::kU, size)
-              .height();
+      const size_t luma_width = size.width();
+      const gfx::Size chroma_size = media::VideoFrame::PlaneSizeInSamples(
+          pixel_format, media::VideoFrame::Plane::kU, size);
+      const size_t chroma_width = chroma_size.width();
+      const size_t chroma_rows = chroma_size.height();
       // TODO(issues.webrtc.org/issues/42225170):
       // webrtc::I420BufferInterface should expose ArrayView instead of raw
       // pointers.
       auto y_plane = UNSAFE_TODO(base::span<const uint8_t>(
           yuv_buffer->DataY(),
-          base::saturated_cast<size_t>(yuv_buffer->StrideY()) * luma_rows));
+          base::saturated_cast<size_t>(yuv_buffer->StrideY()) *
+                  (luma_rows - 1) +
+              luma_width));
       auto u_plane = UNSAFE_TODO(base::span<const uint8_t>(
           yuv_buffer->DataU(),
-          base::saturated_cast<size_t>(yuv_buffer->StrideU()) * chroma_rows));
+          base::saturated_cast<size_t>(yuv_buffer->StrideU()) *
+                  (chroma_rows - 1) +
+              chroma_width));
       auto v_plane = UNSAFE_TODO(base::span<const uint8_t>(
           yuv_buffer->DataV(),
-          base::saturated_cast<size_t>(yuv_buffer->StrideV()) * chroma_rows));
+          base::saturated_cast<size_t>(yuv_buffer->StrideV()) *
+                  (chroma_rows - 1) +
+              chroma_width));
 
       video_frame = media::VideoFrame::WrapExternalYuvData(
           pixel_format, size, gfx::Rect(size), size, yuv_buffer->StrideY(),
@@ -401,21 +417,28 @@
       const webrtc::I444BufferInterface* yuv_buffer = buffer->GetI444();
       const media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_I444;
       const size_t luma_rows = size.height();
-      const size_t chroma_rows =
-          media::VideoFrame::PlaneSizeInSamples(
-              pixel_format, media::VideoFrame::Plane::kU, size)
-              .height();
+      const size_t luma_width = size.width();
+      const gfx::Size chroma_size = media::VideoFrame::PlaneSizeInSamples(
+          pixel_format, media::VideoFrame::Plane::kU, size);
+      const size_t chroma_width = chroma_size.width();
+      const size_t chroma_rows = chroma_size.height();
       // TODO(crbug.com/338570700): webrtc::I444BufferInterface should expose
       // ArrayView instead of raw pointers.
       auto y_plane = UNSAFE_TODO(base::span<const uint8_t>(
           yuv_buffer->DataY(),
-          base::saturated_cast<size_t>(yuv_buffer->StrideY()) * luma_rows));
+          base::saturated_cast<size_t>(yuv_buffer->StrideY()) *
+                  (luma_rows - 1) +
+              luma_width));
       auto u_plane = UNSAFE_TODO(base::span<const uint8_t>(
           yuv_buffer->DataU(),
-          base::saturated_cast<size_t>(yuv_buffer->StrideU()) * chroma_rows));
+          base::saturated_cast<size_t>(yuv_buffer->StrideU()) *
+                  (chroma_rows - 1) +
+              chroma_width));
       auto v_plane = UNSAFE_TODO(base::span<const uint8_t>(
           yuv_buffer->DataV(),
-          base::saturated_cast<size_t>(yuv_buffer->StrideV()) * chroma_rows));
+          base::saturated_cast<size_t>(yuv_buffer->StrideV()) *
+                  (chroma_rows - 1) +
+              chroma_width));
       video_frame = media::VideoFrame::WrapExternalYuvData(
           pixel_format, size, gfx::Rect(size), size, yuv_buffer->StrideY(),
           yuv_buffer->StrideU(), yuv_buffer->StrideV(), y_plane, u_plane,
@@ -426,21 +449,28 @@
       const webrtc::I422BufferInterface* yuv_buffer = buffer->GetI422();
       const media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_I422;
       const size_t luma_rows = size.height();
-      const size_t chroma_rows =
-          media::VideoFrame::PlaneSizeInSamples(
-              pixel_format, media::VideoFrame::Plane::kU, size)
-              .height();
+      const size_t luma_width = size.width();
+      const gfx::Size chroma_size = media::VideoFrame::PlaneSizeInSamples(
+          pixel_format, media::VideoFrame::Plane::kU, size);
+      const size_t chroma_width = chroma_size.width();
+      const size_t chroma_rows = chroma_size.height();
       // TODO(crbug.com/338570700): webrtc::I422BufferInterface should expose
       // ArrayView instead of raw pointers.
       auto y_plane = UNSAFE_TODO(base::span<const uint8_t>(
           yuv_buffer->DataY(),
-          base::saturated_cast<size_t>(yuv_buffer->StrideY()) * luma_rows));
+          base::saturated_cast<size_t>(yuv_buffer->StrideY()) *
+                  (luma_rows - 1) +
+              luma_width));
       auto u_plane = UNSAFE_TODO(base::span<const uint8_t>(
           yuv_buffer->DataU(),
-          base::saturated_cast<size_t>(yuv_buffer->StrideU()) * chroma_rows));
+          base::saturated_cast<size_t>(yuv_buffer->StrideU()) *
+                  (chroma_rows - 1) +
+              chroma_width));
       auto v_plane = UNSAFE_TODO(base::span<const uint8_t>(
           yuv_buffer->DataV(),
-          base::saturated_cast<size_t>(yuv_buffer->StrideV()) * chroma_rows));
+          base::saturated_cast<size_t>(yuv_buffer->StrideV()) *
+                  (chroma_rows - 1) +
+              chroma_width));
       video_frame = media::VideoFrame::WrapExternalYuvData(
           pixel_format, size, gfx::Rect(size), size, yuv_buffer->StrideY(),
           yuv_buffer->StrideU(), yuv_buffer->StrideV(), y_plane, u_plane,
@@ -452,25 +482,33 @@
       const media::VideoPixelFormat pixel_format =
           media::PIXEL_FORMAT_YUV420P10;
       const size_t luma_rows = size.height();
-      const size_t chroma_rows =
-          media::VideoFrame::PlaneSizeInSamples(
-              pixel_format, media::VideoFrame::Plane::kU, size)
-              .height();
+      const size_t luma_width = size.width();
+      const gfx::Size chroma_size = media::VideoFrame::PlaneSizeInSamples(
+          pixel_format, media::VideoFrame::Plane::kU, size);
+      const size_t chroma_width = chroma_size.width();
+      const size_t chroma_rows = chroma_size.height();
       // WebRTC defines I010 data as uint16 whereas Chromium uses uint8 for all
       // video formats, so conversion and cast is needed.
       // TODO(crbug.com/338570700): webrtc::I010BufferInterface should expose
       // ArrayView instead of raw pointers.
       auto y_plane = UNSAFE_TODO(base::span<const uint8_t>(
           reinterpret_cast<const uint8_t*>(yuv_buffer->DataY()),
-          base::saturated_cast<size_t>(yuv_buffer->StrideY()) * 2 * luma_rows));
+          (base::saturated_cast<size_t>(yuv_buffer->StrideY()) *
+               (luma_rows - 1) +
+           luma_width) *
+              2));
       auto u_plane = UNSAFE_TODO(base::span<const uint8_t>(
           reinterpret_cast<const uint8_t*>(yuv_buffer->DataU()),
-          base::saturated_cast<size_t>(yuv_buffer->StrideU()) * 2 *
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.