Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactHeap buffer overflow in Video
DescriptionHeap buffer overflow in Video
ComponentVideo
Bug ClassOOB
Tracker444755026
Fix commit8f2cf429ca30 (chromium/src) +2/-1
CISA KEVNot listed
CreditedElias Hohl
Disclosed2025-09-30

Files Changed

  • media/capture/video/video_capture_device_client.cc
From 8f2cf429ca3090d84c767aa3eddb1e9c3aa57671 Mon Sep 17 00:00:00 2001
From: Henrik Boström <hbos@chromium.org>
Date: Fri, 19 Sep 2025 01:34:03 -0700
Subject: [PATCH] Fix copy operation inside OnIncomingCapturedY16Data.

See issue description for details.

Bug: chromium:444755026
Change-Id: If6388a472e837240f6c3eae5c2b8960af93037bf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6965575
Auto-Submit: Henrik Boström <hbos@chromium.org>
Reviewed-by: Guido Urdaneta <guidou@chromium.org>
Commit-Queue: Henrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1517794}
---

diff --git a/media/capture/video/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc
index 3ffc6eb..dfa8738 100644
--- a/media/capture/video/video_capture_device_client.cc
+++ b/media/capture/video/video_capture_device_client.cc
@@ -1105,7 +1105,8 @@
     return;
   }
   auto buffer_access = buffer.handle_provider->GetHandleForInProcessAccess();
-  memcpy(buffer_access->data(), data, length);
+  memcpy(buffer_access->data(), data,
+         std::min(static_cast<size_t>(length), buffer_access->mapped_size()));
   const VideoCaptureFormat output_format = VideoCaptureFormat(
       format.frame_size, format.frame_rate, PIXEL_FORMAT_Y16);
   OnIncomingCapturedBuffer(std::move(buffer), output_format, reference_time,
Loading diff…

Original Bug Report

reported by el...@cryptosearch.tools

Buffer Overflow in Y16 Video Capture


Report description

Buffer Overflow in Y16 Video Capture


Bug location

Where do you want to report your vulnerability?

Chrome VRP – Report security issues affecting the Chrome browser. See program rules


The problem

Please describe the technical details of the vulnerability

There is a buffer overflow in the Y16 video capture implementation. The problem is that the buffer is allocated based on the width of the images, but copied size includes padding. If padding is present, this will result in a buffer overflow in the destination shared memory region. Interestingly, there is a comment in the code that acknowledges the fact that padding may be present. Vulnerable code: https://source.chromium.org/chromium/chromium/src/+/refs/tags/140.0.7339.128:media/capture/video/video_capture_device_client.cc;drc=fe5d35fd601e4b1461bd447f334b7e0494e64c61;l=1084

void VideoCaptureDeviceClient::OnIncomingCapturedY16Data(
    const uint8_t* data,
    int length,
    const VideoCaptureFormat& format,
    base::TimeTicks reference_time,
    base::TimeDelta timestamp,
    std::optional<base::TimeTicks> capture_begin_timestamp,
    const std::optional<VideoFrameMetadata>& metadata,
    int frame_feedback_id) {
  Buffer buffer;
  const auto reservation_result_code = ReserveOutputBuffer(
      format.frame_size, PIXEL_FORMAT_Y16, frame_feedback_id, &buffer,
      /*require_new_buffer_id=*/nullptr, /*retire_old_buffer_id=*/nullptr);
  // The input |length| can be greater than the required buffer size because of
  // paddings and/or alignments, but it cannot be smaller.
  CHECK_GE(static_cast<size_t>(length),
           media::VideoFrame::AllocationSize(format.pixel_format,
                                             format.frame_size));
  // Failed to reserve output buffer, so drop the frame.
  if (reservation_result_code != ReserveResult::kSucceeded) {
    receiver_->OnFrameDropped(
        ConvertReservationFailureToFrameDropReason(reservation_result_code));
    return;
  }
  auto buffer_access = buffer.handle_provider->GetHandleForInProcessAccess();
  memcpy(buffer_access->data(), data, length);
  const VideoCaptureFormat output_format = VideoCaptureFormat(
      format.frame_size, format.frame_rate, PIXEL_FORMAT_Y16);
  OnIncomingCapturedBuffer(std::move(buffer), output_format, reference_time,
                           timestamp, capture_begin_timestamp, metadata);
}

Proof of Concept

I do not have a Y16 camera, so I implemented virtual cameras for Windows and Linux as a proof of concept.

Windows

Chrome version used: 140.0.7339.128 (Official Build) (64-bit) (cohort: Stable)

Install VCamSample.zip according to the README (I modified the code to make it use the Y16 pixel format, but the installation still works the same way). Launch Chrome with the command line

"C:\Program Files\Google\Chrome\Application\chrome.exe" --enable-logging=stderr --v=1 --utility-startup-dialog=video_capture.mojom.VideoCaptureService --no-sandbox

Attach the Visual Studio Debugger to the VideoCaptureService process. Then, open y16_auto_final.html in Chrome and observe the crash with the error “access violation” (like in the png files appended to this report). Call stack:

chrome.dll!memcpy_repmovs() Line 50

chrome.dll!media::VideoCaptureDeviceClient::OnIncomingCapturedY16Data(const unsigned char * data, int length, const media::VideoCaptureFormat & format, base::TimeTicks reference_time, base::TimeDelta timestamp, std::__Cr::optional<base::TimeTicks> capture_begin_timestamp, const std::__Cr::optional<media::VideoFrameMetadata> & metadata, int frame_feedback_id) Line 1110

chrome.dll!media::VideoCaptureDeviceClient::OnIncomingCapturedData(const unsigned char * data, int length, const media::VideoCaptureFormat & format, const gfx::ColorSpace & data_color_space, int rotation, bool flip_y, base::TimeTicks reference_time, base::TimeDelta timestamp, std::__Cr::optional<base::TimeTicks> capture_begin_timestamp, const std::__Cr::optional<media::VideoFrameMetadata> & metadata, int frame_feedback_id) Line 533

chrome.dll!media::VideoCaptureDevice::Client::OnIncomingCapturedData(const unsigned char * data, int length, const media::VideoCaptureFormat & frame_format, const gfx::ColorSpace & color_space, int clockwise_rotation, bool flip_y, base::TimeTicks reference_time, base::TimeDelta timestamp, std::__Cr::optional<base::TimeTicks> capture_begin_timestamp, const std::__Cr::optional<media::VideoFrameMetadata> & metadata) Line 96

chrome.dll!media::VideoCaptureDeviceMFWin::OnIncomingCapturedDataInternal() Line 2507

[Inline Frame] chrome.dll!base::OnceCallback<void ()>::Run() Line 156

[Inline Frame] chrome.dll!base::TaskAnnotator::RunTaskImpl(base::PendingTask & pending_task) Line 207

[Inline Frame] chrome.dll!base::TaskAnnotator::RunTask(perfetto::StaticString event_name, base::PendingTask & pending_task, base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl::<lambda_4> && args) Line 104

[Inline Frame] chrome.dll!base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow * continuation_lazy_now) Line 456

chrome.dll!base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork() Line 330

chrome.dll!base::MessagePumpDefault::Run(base::MessagePump::Delegate * delegate) Line 43

chrome.dll!base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::Run(bool application_tasks_allowed, base::TimeDelta timeout) Line 632

chrome.dll!base::RunLoop::Run(const base::Location & location) Line 136

chrome.dll!content::UtilityMain(content::MainFunctionParams parameters) Line 506

chrome.dll!content::RunOtherNamedProcessTypeMain(const std::__Cr::basic_string<char,std::__Cr::char_traits<char>,std::__Cr::allocator<char>> & process_type, content::MainFunctionParams main_function_params, content::ContentMainDelegate * delegate) Line 763

chrome.dll!content::ContentMainRunnerImpl::Run() Line 1131

[Inline Frame] chrome.dll!content::RunContentProcess(content::ContentMainParams params, content::ContentMainRunner * content_main_runner) Line 361

chrome.dll!content::ContentMain(content::ContentMainParams params) Line 374

chrome.dll!ChromeMain(HINSTANCE__ * instance, sandbox::SandboxInterfaceInfo * sandbox_info, __int64 exe_entry_point_ticks, __int64 preread_begin_ticks, __int64 preread_end_ticks) Line 224

[External Code]	

Linux

Chrome version used: 140.0.7339.127 official ASAN build for linux (stable channel)

Note: Linux does not allocate shared memory using 64KiB VirtualAlloc blocks with unmapped pages if the end of the last 4KiB page does not coincide with the 64KiB block boundary, like Windows does, which increases the probability that an adjacent shared memory block will swallow the overflow without a crash. ASAN also does not add redzones next to the shared memory regions.

Note: I tested this on Debian 11 5.10.0-35-amd64

First, get the kernel sources:

sudo apt-get install build-essential linux-headers-$(uname -r)
apt-get source linux   # get the exact matching source for your kernel build
cd linux-*/drivers/media/test-drivers/vivid

Replace the vivid folder with the content from the file vivid.zip appended to this report. Compile the vivid driver and load it (we first load the original version using modprobe to load dependencies, then remove it and load our own build which was modified to use Y16):

make -C /lib/modules/$(uname -r)/build M=$PWD modules
sudo modprobe vivid n_devs=1 vid_cap_nr=42
sudo rmmod vivid
sudo insmod ./vivid.ko n_devs=1 vid_cap_nr=42

Open ASAN Chrome with the following command line:

./chrome --disable-gpu-sandbox --no-sandbox

Open y16_auto_final.html and you will get the following ASAN stack trace:

=================================================================
==586193==ERROR: AddressSanitizer: memcpy-param-overlap: memory ranges [0x7b385d3dc000,0x7b385d4a6800) and [0x7b385d4a3000, 0x7b385d56d800) overlap
    #0 0x563d91527219 in __asan_memcpy (/home/elias/chromium-exploits/asanchrome-new/chrome+0xff44219) (BuildId: 751dffbb7e8a9303)
    #1 0x563dacc244fc in media::VideoCaptureDeviceClient::OnIncomingCapturedY16Data(unsigned char const*, int, media::VideoCaptureFormat const&, base::TimeTicks, base::TimeDelta, std::__Cr::optional<base::TimeTicks>, std::__Cr::optional<media::VideoFrameMetadata> const&, int) media/capture/video/video_capture_device_client.cc:1109:3
    #2 0x563dacc2342e in media::VideoCaptureDeviceClient::OnIncomingCapturedData(unsigned char const*, int, media::VideoCaptureFormat const&, gfx::ColorSpace const&, int, bool, base::TimeTicks, base::TimeDelta, std::__Cr::optional<base::TimeTicks>, std::__Cr::optional<media::VideoFrameMetadata> const&, int) media/capture/video/video_capture_device_client.cc:533:12
    #3 0x563d9510b093 in media::VideoCaptureDevice::Client::OnIncomingCapturedData(unsigned char const*, int, media::VideoCaptureFormat const&, gfx::ColorSpace const&, int, bool, base::TimeTicks, base::TimeDelta, std::__Cr::optional<base::TimeTicks>, std::__Cr::optional<media::VideoFrameMetadata> const&) media/capture/video/video_capture_device.cc:96:3
    #4 0x563dacc497f3 in media::V4L2CaptureDelegate::DoCapture() media/capture/video/linux/v4l2_capture_delegate.cc:1164:18
    #5 0x563dacc52144 in Invoke<void (V4L2CaptureDelegate::*)(), const base::WeakPtr<media::V4L2CaptureDelegate> &> base/functional/bind_internal.h:731:12
    #6 0x563dacc52144 in MakeItSo<void (V4L2CaptureDelegate::*)(), std::__Cr::tuple<base::WeakPtr<media::V4L2CaptureDelegate> > > base/functional/bind_internal.h:947:5
    #7 0x563dacc52144 in RunImpl<void (V4L2CaptureDelegate::*)(), std::__Cr::tuple<base::WeakPtr<media::V4L2CaptureDelegate> >, 0UL> base/functional/bind_internal.h:1060:14
    #8 0x563dacc52144 in base::internal::Invoker<base::internal::FunctorTraits<void (media::V4L2CaptureDelegate::*&&)(), base::WeakPtr<media::V4L2CaptureDelegate>&&>, base::internal::BindState<true, true, false, void (media::V4L2CaptureDelegate::*)(), base::WeakPtr<media::V4L2CaptureDelegate> >, void ()>::RunOnce(base::internal::BindStateBase*) base/functional/bind_internal.h:973:12
    #9 0x563da71eee06 in Run base/functional/callback.h:156:12
    #10 0x563da71eee06 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&) base/task/common/task_annotator.cc:207:34
    #11 0x563da72863ba in RunTask<(lambda at ../../base/task/thread_pool/task_tracker.cc:687:35)> base/task/common/task_annotator.h:104:5
    #12 0x563da72863ba in base::internal::TaskTracker::RunTaskImpl(base::internal::Task&, base::TaskTraits const&, base::internal::TaskSource*, base::internal::SequenceToken const&) base/task/thread_pool/task_tracker.cc:686:19
    #13 0x563da728660c in base::internal::TaskTracker::RunSkipOnShutdown(base::internal::Task&, base::TaskTraits const&, base::internal::TaskSource*, base::internal::SequenceToken const&) base/task/thread_pool/task_tracker.cc:671:3
    #14 0x563da7284c14 in RunTaskWithShutdownBehavior base/task/thread_pool/task_tracker.cc:701:7
    #15 0x563da7284c14 in base::internal::TaskTracker::RunTask(base::internal::Task, base::internal::TaskSource*, base::TaskTraits const&) base/task/thread_pool/task_tracker.cc:501:5
    #16 0x563da7283c74 in base::internal::TaskTracker::RunAndPopNextTask(base::internal::RegisteredTaskSource) base/task/thread_pool/task_tracker.cc:391:5
    #17 0x563da72c4dd3 in base::internal::WorkerThread::RunWorker() base/task/thread_pool/worker_thread.cc:473:36
    #18 0x563da72c4167 in base::internal::WorkerThread::RunDedicatedWorker() base/task/thread_pool/worker_thread.cc:379:3
    #19 0x563da72c38e9 in base::internal::WorkerThread::ThreadMain() base/task/thread_pool/worker_thread.cc:345:7
    #20 0x563da734062e in base::(anonymous namespace)::ThreadFunc(void*) base/threading/platform_thread_posix.cc:101:13
    #21 0x563d91526fd6 in asan_thread_start(void*) (/home/elias/chromium-exploits/asanchrome-new/chrome+0xff43fd6) (BuildId: 751dffbb7e8a9303)

Address 0x7b385d3dc000 is a wild pointer inside of access range of size 0x0000000ca800.
Address 0x7b385d4a3000 is a wild pointer inside of access range of size 0x0000000ca800.
SUMMARY: AddressSanitizer: memcpy-param-overlap (/home/elias/chromium-exploits/asanchrome-new/chrome+0xff44219) (BuildId: 751dffbb7e8a9303) in __asan_memcpy
Thread T7 (ThreadPoolSingl) created by T0 (chrome) here:
    #0 0x563d9150d8c1 in pthread_create (/home/elias/chromium-exploits/asanchrome-new/chrome+0xff2a8c1) (BuildId: 751dffbb7e8a9303)
    #1 0x563da733fc18 in base::(anonymous namespace)::CreateThread(unsigned long, bool, base::PlatformThreadBase::Delegate*, base::PlatformThreadHandle*, base::ThreadType, base::MessagePumpType) base/threading/platform_thread_posix.cc:149:13
    #2 0x563da72c26b8 in base::internal::WorkerThread::Start(scoped_refptr<base::SingleThreadTaskRunner>, base::WorkerThreadObserver*) base/task/thread_pool/worker_thread.cc:185:3
    #3 0x563da72a52ee in CreateTaskRunnerImpl<base::internal::(anonymous namespace)::WorkerThreadDelegate> base/task/thread_pool/pooled_single_thread_task_runner_manager.cc:749:13
    #4 0x563da72a52ee in base::internal::PooledSingleThreadTaskRunnerManager::CreateSingleThreadTaskRunner(base::TaskTraits const&, base::SingleThreadTaskRunnerThreadMode) base/task/thread_pool/pooled_single_thread_task_runner_manager.cc:685:10
    #5 0x563dacc3fcfb in media::VideoCaptureDeviceLinux::VideoCaptureDeviceLinux(scoped_refptr<media::V4L2CaptureDevice>, media::VideoCaptureDeviceDescriptor const&) media/capture/video/linux/video_capture_device_linux.cc:61:20
    #6 0x563dacc39180 in make_unique<media::VideoCaptureDeviceLinux, media::V4L2CaptureDevice *, const media::VideoCaptureDeviceDescriptor &, 0> third_party/libc++/src/include/__memory/unique_ptr.h:759:30
    #7 0x563dacc39180 in media::VideoCaptureDeviceFactoryV4L2::CreateDevice(media::VideoCaptureDeviceDescriptor const&) media/capture/video/linux/video_capture_device_factory_v4l2.cc:148:7
    #8 0x563dacc372b6 in media::VideoCaptureDeviceFactoryLinux::CreateDevice(media::VideoCaptureDeviceDescriptor const&) media/capture/video/linux/video_capture_device_factory_linux.cc
    #9 0x563dacc2d54c in media::VideoCaptureSystemImpl::CreateDevice(std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> > const&) media/capture/video/video_capture_system_impl.cc:112:20
    #10 0x563d9eece088 in video_capture::DeviceFactoryImpl::CreateAndAddNewDevice(std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> > const&, base::OnceCallback<void (video_capture::DeviceFactory::DeviceInfo)>) services/video_capture/device_factory_impl.cc:181:24
    #11 0x563d9eecf1e5 in Invoke<void (DeviceFactoryImpl::*)(const std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> > &, base::OnceCallback<void (video_capture::DeviceFactory::DeviceInfo)>), const base::WeakPtr<video_capture::DeviceFactoryImpl> &, std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> >, base::OnceCallback<void (video_capture::DeviceFactory::DeviceInfo)> > base/functional/bind_internal.h:731:12
    #12 0x563d9eecf1e5 in MakeItSo<void (DeviceFactoryImpl::*)(const std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> > &, base::OnceCallback<void (video_capture::DeviceFactory::DeviceInfo)>), std::__Cr::tuple<base::WeakPtr<video_capture::DeviceFactoryImpl>, std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> >, base::OnceCallback<void (video_capture::DeviceFactory::DeviceInfo)> > > base/functional/bind_internal.h:947:5
    #13 0x563d9eecf1e5 in RunImpl<void (DeviceFactoryImpl::*)(const std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> > &, base::OnceCallback<void (video_capture::DeviceFactory::DeviceInfo)>), std::__Cr::tuple<base::WeakPtr<video_capture::DeviceFactoryImpl>, std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> >, base::OnceCallback<void (video_capture::DeviceFactory::DeviceInfo)> >, 0UL, 1UL, 2UL> base/functional/bind_internal.h:1060:14
    #14 0x563d9eecf1e5 in base::internal::Invoker<base::internal::FunctorTraits<void (video_capture::DeviceFactoryImpl::*&&)(std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> > const&, base::OnceCallback<void (video_capture::DeviceFactory::DeviceInfo)>), base::WeakPtr<video_capture::DeviceFactoryImpl>&&, std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> >&&, base::OnceCallback<void (video_capture::DeviceFactory::DeviceInfo)>&&>, base::internal::BindState<true, true, false, void (video_capture::DeviceFactoryImpl::*)(std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> > const&, base::OnceCallback<void (video_capture::DeviceFactory::DeviceInfo)>), base::WeakPtr<video_capture::DeviceFactoryImpl>, std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> >, base::OnceCallback<void (video_capture::DeviceFactory::DeviceInfo)> >, void ()>::RunOnce(base::internal::BindStateBase*) base/functional/bind_internal.h:973:12
    #15 0x563d9eecdc5c in Run base/functional/callback.h:156:12
    #16 0x563d9eecdc5c in video_capture::DeviceFactoryImpl::CreateDevice(std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> > const&, base::OnceCallback<void (video_capture::DeviceFactory::DeviceInfo)>) services/video_capture/device_factory_impl.cc:135:45
    #17 0x563d9eefc28d in video_capture::VirtualDeviceEnabledDeviceFactory::CreateDevice(std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char> > const&, base::OnceCallback<void (video_capture::DeviceFactory::DeviceInfo)>) services/video_capture/virtual_device_enabled_device_factory.cc:153:27
    #18 0x563d9eef25dc in video_capture::VideoSourceImpl::StartDeviceWithSettings(media::VideoCaptureParams const&) services/video_capture/video_source_impl.cc:134:20
    #19 0x563d9eef189e in video_capture::VideoSourceImpl::CreatePushSubscription(mojo::PendingRemote<video_capture::mojom::VideoFrameHandler>, media::VideoCaptureParams const&, bool, mojo::PendingReceiver<video_capture::mojom::PushVideoStreamSubscription>, base::OnceCallback<void (mojo::InlinedStructPtr<video_capture::mojom::CreatePushSubscriptionResultCode>, media::VideoCaptureParams const&)>) services/video_capture/video_source_impl.cc:63:7
    #20 0x563d95159074 in video_capture::mojom::VideoSourceStubDispatch::AcceptWithResponder(video_capture::mojom::VideoSource*, mojo::Message*, std::__Cr::unique_ptr<mojo::MessageReceiverWithStatus, std::__Cr::default_delete<mojo::MessageReceiverWithStatus> >) gen/services/video_capture/public/mojom/video_source.mojom.cc:2020:13
    #21 0x563da6fc4330 in mojo::InterfaceEndpointClient::HandleValidatedMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:1013:56
    #22 0x563da6fe1c1d in mojo::MessageDispatcher::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/message_dispatcher.cc:48:24
    #23 0x563da6fc9f74 in mojo::InterfaceEndpointClient::HandleIncomingMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:731:20
    #24 0x563da6ff1b3a in mojo::internal::MultiplexRouter::ProcessIncomingMessage(mojo::internal::MultiplexRouter::MessageWrapper*, mojo::internal::MultiplexRouter::ClientCallBehavior, base::SequencedTaskRunner*) mojo/public/cpp/bindings/lib/multiplex_router.cc:1120:42
    #25 0x563da6ff0046 in mojo::internal::MultiplexRouter::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/multiplex_router.cc:733:7
    #26 0x563da6fe1d1a in mojo::MessageDispatcher::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/message_dispatcher.cc:43:19
    #27 0x563da6fbae22 in mojo::Connector::DispatchMessage(mojo::ScopedHandleBase<mojo::MessageHandle>) mojo/public/cpp/bindings/lib/connector.cc:561:49
    #28 0x563da6fbc5c0 in mojo::Connector::ReadAllAvailableMessages() mojo/public/cpp/bindings/lib/connector.cc:619:14
    #29 0x563da6fbbfe9 in OnHandleReadyInternal mojo/public/cpp/bindings/lib/connector.cc:450:3
    #30 0x563da6fbbfe9 in mojo::Connector::OnWatcherHandleReady(char const*, unsigned int) mojo/public/cpp/bindings/lib/connector.cc:416:3
    #31 0x563da6fbde6a in Invoke<void (Connector::*)(const char *, unsigned int), mojo::Connector *, const char *, unsigned int> base/functional/bind_internal.h:731:12
    #32 0x563da6fbde6a in MakeItSo<void (Connector::*const &)(const char *, unsigned int), const std::__Cr::tuple<base::internal::UnretainedWrapper<mojo::Connector, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, base::internal::UnretainedWrapper<const char, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0> > &, unsigned int> base/functional/bind_internal.h:923:12
    #33 0x563da6fbde6a in RunImpl<void (Connector::*const &)(const char *, unsigned int), const std::__Cr::tuple<base::internal::UnretainedWrapper<mojo::Connector, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, base::internal::UnretainedWrapper<const char, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0> > &, 0UL, 1UL> base/functional/bind_internal.h:1060:14
    #34 0x563da6fbde6a in base::internal::Invoker<base::internal::FunctorTraits<void (mojo::Connector::* const&)(char const*, unsigned int), mojo::Connector*, char const* const&>, base::internal::BindState<true, true, false, void (mojo::Connector::*)(char const*, unsigned int), base::internal::UnretainedWrapper<mojo::Connector, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, base::internal::UnretainedWrapper<char const, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0> >, void (unsigned int)>::Run(base::internal::BindStateBase*, unsigned int) base/functional/bind_internal.h:980:12
    #35 0x563d95efbace in base::RepeatingCallback<void (unsigned int)>::Run(unsigned int) const & base/functional/callback.h:344:12
    #36 0x563d95efb85f in Invoke<void (*const &)(const base::RepeatingCallback<void (unsigned int)> &, unsigned int, const mojo::HandleSignalsState &), const base::RepeatingCallback<void (unsigned int)> &, unsigned int, const mojo::HandleSignalsState &> base/functional/bind_internal.h:664:12
    #37 0x563d95efb85f in MakeItSo<void (*const &)(const base::RepeatingCallback<void (unsigned int)> &, unsigned int, const mojo::HandleSignalsState &), const std::__Cr::tuple<base::RepeatingCallback<void (unsigned int)> > &, unsigned int, const mojo::HandleSignalsState &> base/functional/bind_internal.h:923:12
    #38 0x563d95efb85f in RunImpl<void (*const &)(const base::RepeatingCallback<void (unsigned int)> &, unsigned int, const mojo::HandleSignalsState &), const std::__Cr::tuple<base::RepeatingCallback<void (unsigned int)> > &, 0UL> base/functional/bind_internal.h:1060:14
    #39 0x563d95efb85f in base::internal::Invoker<base::internal::FunctorTraits<void (* const&)(base::RepeatingCallback<void (unsigned int)> const&, unsigned int, mojo::HandleSignalsState const&), base::RepeatingCallback<void (unsigned int)> const&>, base::internal::BindState<false, true, false, void (*)(base::RepeatingCallback<void (unsigned int)> const&, unsigned int, mojo::HandleSignalsState const&), base::RepeatingCallback<void (unsigned int)> >, void (unsigned int, mojo::HandleSignalsState const&)>::Run(base::internal::BindStateBase*, unsigned int, mojo::HandleSignalsState const&) base/functional/bind_internal.h:980:12
    #40 0x563da7d41b40 in base::RepeatingCallback<void (unsigned int, mojo::HandleSignalsState const&)>::Run(unsigned int, mojo::HandleSignalsState const&) const & base/functional/callback.h:344:12
    #41 0x563da7d41478 in mojo::SimpleWatcher::OnHandleReady(int, unsigned int, mojo::HandleSignalsState const&) mojo/public/cpp/system/simple_watcher.cc:278:14
    #42 0x563da7d426ad in Invoke<void (SimpleWatcher::*)(int, unsigned int, const mojo::HandleSignalsState &), const base::WeakPtr<mojo::SimpleWatcher> &, int, unsigned int, mojo::HandleSignalsState> base/functional/bind_internal.h:731:12
    #43 0x563da7d426ad in MakeItSo<void (SimpleWatcher::*)(int, unsigned int, const mojo::HandleSignalsState &), std::__Cr::tuple<base::WeakPtr<mojo::SimpleWatcher>, int, unsigned int, mojo::HandleSignalsState> > base/functional/bind_internal.h:947:5
    #44 0x563da7d426ad in RunImpl<void (SimpleWatcher::*)(int, unsigned int, const mojo::HandleSignalsState &), std::__Cr::tuple<base::WeakPtr<mojo::SimpleWatcher>, int, unsigned int, mojo::HandleSignalsState>, 0UL, 1UL, 2UL, 3UL> base/functional/bind_internal.h:1060:14
    #45 0x563da7d426ad in base::internal::Invoker<base::internal::FunctorTraits<void (mojo::SimpleWatcher::*&&)(int, unsigned int, mojo::HandleSignalsState const&), base::WeakPtr<mojo::SimpleWatcher>&&, int&&, unsigned int&&, mojo::HandleSignalsState&&>, base::internal::BindState<true, true, false, void (mojo::SimpleWatcher::*)(int, unsigned int, mojo::HandleSignalsState const&), base::WeakPtr<mojo::SimpleWatcher>, int, unsigned int, mojo::HandleSignalsState>, void ()>::RunOnce(base::internal::BindStateBase*) base/functional/bind_internal.h:973:12
    #46 0x563da71eee06 in Run base/functional/callback.h:156:12
    #47 0x563da71eee06 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&) base/task/common/task_annotator.cc:207:34
    #48 0x563da7261537 in RunTask<(lambda at ../../base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:458:11)> base/task/common/task_annotator.h:104:5
    #49 0x563da7261537 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*) base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:456:23
    #50 0x563da726041c in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork() base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:330:40
    #51 0x563da726202a in non-virtual thunk to base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork() base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
    #52 0x563da73efde8 in base::MessagePumpGlib::HandleDispatch() base/message_loop/message_pump_glib.cc:656:46
    #53 0x563da73f31c8 in base::(anonymous namespace)::WorkSourceDispatch(_GSource*, int (*)(void*), void*) base/message_loop/message_pump_glib.cc:275:43
    #54 0x7f386e81ae0a in g_main_context_dispatch (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x51e0a) (BuildId: 51af3166f1370652c1f9302d6cb2509fd974c335)


==586193==ADDITIONAL INFO

==586193==Note: Please include this section with the ASan report.
Task trace:
    #0 0x563dacc48b69 in media::V4L2CaptureDelegate::DoCapture() media/capture/video/linux/v4l2_capture_delegate.cc:1191:7
    #1 0x563dacc48b69 in media::V4L2CaptureDelegate::DoCapture() media/capture/video/linux/v4l2_capture_delegate.cc:1191:7
    #2 0x563dacc48b69 in media::V4L2CaptureDelegate::DoCapture() media/capture/video/linux/v4l2_capture_delegate.cc:1191:7
    #3 0x563dacc455e0 in media::V4L2CaptureDelegate::AllocateAndStart(int, int, float, std::__Cr::unique_ptr<media::VideoCaptureDevice::Client, std::__Cr::default_delete<media::VideoCaptureDevice::Client> >) media/capture/video/linux/v4l2_capture_delegate.cc:468:7


Command line: `/proc/self/exe --type=utility --utility-sub-type=video_capture.mojom.VideoCaptureService --lang=de --service-sandbox-type=none --no-sandbox --message-loop-type-ui --crashpad-handler-pid=585873 --enable-crash-reporter=, --subproc-heap-profiling --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --metrics-shmem-handle=4,i,6229673590439775338,1326251627037240520,524288 --field-trial-handle=3,i,2008510889948857896,2382443360607485135,262144 --enable-features=Vulkan --variations-seed-version`


==586193==END OF ADDITIONAL INFO
==586193==ABORTING

The memcpy-param-overlap error probably occurs because the smaller destination buffer was allocated directly next to the larger source buffer, which would also explain why the error is silent without ASAN Chrome.

Bisect

Apparently, this vulnerability has been present since the feature was introduced in 2016 (I did not test it with the 2016 version though). https://source.chromium.org/chromium/chromium/src/+/198b050b9fe647bf3799ab75df07b888c7adcf50:media/capture/video/video_capture_device_client.cc;dlc=a6257d62eb21b0e308dbccc8ac45c4f15160f5d8

Suggested Patch

Copy row-by-row using the width excluding padding as copy size, or resize the destination buffer to include padding (that might require changes in the logic that later processes this destination buffer though), or reject padding altogether (which would break the feature for all devices / drivers / configurations that use padding though).

Impact analysis – Please briefly explain who can exploit the vulnerability, and what they gain when doing so

This vulnerability can be triggered from the web by an attacker against victims that have a Y16 camera (which Chrome must have permission to access) available on their computer, if that camera and its driver have at least one configuration that uses padding. No further user interaction is required (it is not necessary that the user allows camera access for the attacker website, because the vulnerability gets already triggered in the preview when Chrome asks the user to allow access to the camera). The possible impact is a buffer overflow in the shared memory region in the video capture process, which is unsandboxed on Linux and Windows. A memory corruption in this process could theoretically result in a full sandbox escape RCE, if an attacker somehow manages to control the data returned by the camera, and other mitigations and problems typical for memory corruption exploitation can be bypassed / solved (ASLR, memory layout, heap metadata corruption protection).


The cause

What version of Chrome have you found the security issue in?

Windows: 140.0.7339.128 (Official Build) (64-bit) (cohort: Stable), Linux: 140.0.7339.127 (stable official ASAN build)

Yes, it is related to a crash.

Choose the type of vulnerability

Memory Corruption (in a non-sandboxed process)

How would you like to be publicly acknowledged for your report?

Elias Hohl

View on issue tracker