Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in WebCodecs
DescriptionUse after free in WebCodecs
ComponentWebCodecs
Bug ClassUAF
Tracker491515787
Fix commit46bc46aeb674 (chromium/src) +56/-1
CISA KEVNot listed
Creditedheapracer (@heapracer)
Disclosed2026-03-31

Changed Functions

FunctionChangeNotes
H264VideoEncoderTest
media/video/software_video_encoder_test.cc
modified
Vpx10BitVideoEncoderTest
media/video/software_video_encoder_test.cc
modified
SVCVideoEncoderTest
media/video/software_video_encoder_test.cc
modified
ManualSVCVideoEncoderTest
media/video/software_video_encoder_test.cc
modified
LargeTimestampOverflowTest
media/video/software_video_encoder_test.cc
modified
TEST_P
media/video/software_video_encoder_test.cc
modified
if
media/video/vpx_video_encoder.cc
modified

Files Changed

  • media/video/software_video_encoder_test.cc
  • media/video/vpx_video_encoder.cc
  • media/video/vpx_video_encoder.h
From 46bc46aeb6740ddee2c122ad1f5045b12b37a83a Mon Sep 17 00:00:00 2001
From: Eugene Zemtsov <[email protected]>
Date: Tue, 17 Mar 2026 18:48:13 -0700
Subject: [PATCH] media: Fix VpxVideoEncoder memory ownership bug

When encoding VP9 Profile 2 or 3, the encoder reuses its internal
image wrapper (`vpx_image_t`) between frames to save allocations.
However, it only checked if the format and dimensions changed.

If one frame provided external memory and the next frame required
the encoder to allocate its own buffer, the encoder would incorrectly
try to reuse the old external memory pointers.

This CL adds a check to ensure the image wrapper is recreated when
the memory ownership requirement changes between frames.

Bug: 491515787
Change-Id: If08d974edb10348a7fae18754788c0102e03c292
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7670921
Reviewed-by: Dale Curtis <[email protected]>
Commit-Queue: Eugene Zemtsov <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1600950}
---

diff --git a/media/video/software_video_encoder_test.cc b/media/video/software_video_encoder_test.cc
index 4ec61cd..3e879b8 100644
--- a/media/video/software_video_encoder_test.cc
+++ b/media/video/software_video_encoder_test.cc
@@ -316,6 +316,7 @@
 };
 
 class H264VideoEncoderTest : public SoftwareVideoEncoderTest {};
+class Vpx10BitVideoEncoderTest : public SoftwareVideoEncoderTest {};
 class SVCVideoEncoderTest : public SoftwareVideoEncoderTest {};
 class ManualSVCVideoEncoderTest : public SoftwareVideoEncoderTest {};
 class LargeTimestampOverflowTest : public SoftwareVideoEncoderTest {};
@@ -590,6 +591,46 @@
   EXPECT_EQ(total_decoded_frames, total_frames_count);
 }
 
+TEST_P(Vpx10BitVideoEncoderTest, EncodeDifferentMemoryTypes) {
+  VideoEncoder::Options options = CreateDefaultOptions();
+  options.frame_size = gfx::Size(2000, 2000);
+
+  encoder_->Initialize(profile_, options, /*info_cb=*/base::DoNothing(),
+                       /*output_cb=*/base::DoNothing(),
+                       ValidateStatusThenQuitCB());
+  RunUntilQuit();
+
+  VideoPixelFormat format1 = (profile_ == VP9PROFILE_PROFILE2)
+                                 ? PIXEL_FORMAT_YUV420P10
+                                 : PIXEL_FORMAT_YUV444P10;
+  VideoPixelFormat format2 =
+      (profile_ == VP9PROFILE_PROFILE2) ? PIXEL_FORMAT_I420 : PIXEL_FORMAT_I444;
+
+  // Encode a frame that doesn't need its own memory wrapper allocation.
+  auto frame1 = media::VideoFrame::CreateZeroInitializedFrame(
+      format1, options.frame_size, gfx::Rect(options.frame_size),
+      options.frame_size, base::Seconds(1));
+  encoder_->Encode(std::move(frame1), VideoEncoder::EncodeOptions(true),
+                   ValidateStatusThenQuitCB());
+  ASSERT_NO_FATAL_FAILURE(RunUntilQuit());
+
+  // Encode a frame that DOES need its own memory wrapper allocation.
+  auto frame2 = media::VideoFrame::CreateZeroInitializedFrame(
+      format2, options.frame_size, gfx::Rect(options.frame_size),
+      options.frame_size, base::Seconds(2));
+  encoder_->Encode(std::move(frame2), VideoEncoder::EncodeOptions(false),
+                   ValidateStatusThenQuitCB());
+  ASSERT_NO_FATAL_FAILURE(RunUntilQuit());
+
+  // Encode the first format again.
+  auto frame3 = media::VideoFrame::CreateZeroInitializedFrame(
+      format1, options.frame_size, gfx::Rect(options.frame_size),
+      options.frame_size, base::Seconds(3));
+  encoder_->Encode(std::move(frame3), VideoEncoder::EncodeOptions(false),
+                   ValidateStatusThenQuitCB());
+  ASSERT_NO_FATAL_FAILURE(RunUntilQuit());
+}
+
 TEST_P(SoftwareVideoEncoderTest, EncodeAndDecodeWithEnablingDrop) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(kWebCodecsVideoEncoderFrameDrop);
@@ -1527,6 +1568,15 @@
                          ::testing::ValuesIn(kVpxParams),
                          PrintTestParams);
 
+SwVideoTestParams kVpx10BitParams[] = {
+    {VideoCodec::kVP9, VP9PROFILE_PROFILE2, PIXEL_FORMAT_I420},
+    {VideoCodec::kVP9, VP9PROFILE_PROFILE3, PIXEL_FORMAT_I420}};
+
+INSTANTIATE_TEST_SUITE_P(Vpx10BitSpecific,
+                         Vpx10BitVideoEncoderTest,
+                         ::testing::ValuesIn(kVpx10BitParams),
+                         PrintTestParams);
+
 SwVideoTestParams kVpxSVCParams[] = {
     {VideoCodec::kVP9, VP9PROFILE_PROFILE0, PIXEL_FORMAT_I420, std::nullopt},
     {VideoCodec::kVP9, VP9PROFILE_PROFILE0, PIXEL_FORMAT_I420,
@@ -1603,6 +1653,7 @@
 #endif  // ENABLE_LIBAOM
 
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(H264VideoEncoderTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Vpx10BitVideoEncoderTest);
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SVCVideoEncoderTest);
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SoftwareVideoEncoderTest);
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ManualSVCVideoEncoderTest);
diff --git a/media/video/vpx_video_encoder.cc b/media/video/vpx_video_encoder.cc
index a661729e..6b0df913 100644
--- a/media/video/vpx_video_encoder.cc
+++ b/media/video/vpx_video_encoder.cc
@@ -919,13 +919,15 @@
                                                bool needs_memory) {
   const bool has_changed = vpx_image_.fmt != fmt ||
                            vpx_image_.d_w != codec_config_.g_w ||
-                           vpx_image_.d_h != codec_config_.g_h;
+                           vpx_image_.d_h != codec_config_.g_h ||
+                           vpx_image_owns_memory_ != needs_memory;
 
   if (!has_changed) {
     return;
   }
 
   vpx_img_free(&vpx_image_);
+  vpx_image_owns_memory_ = needs_memory;
   if (needs_memory) {
     CHECK(vpx_img_alloc(&vpx_image_, fmt, codec_config_.g_w, codec_config_.g_h,
                         /*align=*/1));
diff --git a/media/video/vpx_video_encoder.h b/media/video/vpx_video_encoder.h
index 099a3a2..de7ecc4 100644
--- a/media/video/vpx_video_encoder.h
+++ b/media/video/vpx_video_encoder.h
@@ -53,6 +53,8 @@
   vpx_codec_unique_ptr codec_;
   vpx_codec_enc_cfg_t codec_config_ = {};
   vpx_image_t vpx_image_ = {};
+  // True if `vpx_image_` was allocated with vpx_img_alloc().
+  bool vpx_image_owns_memory_ = false;
   gfx::Size originally_configured_size_;
   base::TimeDelta last_frame_timestamp_;
   gfx::ColorSpace last_frame_color_space_;
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/media/video/software_video_encoder_test.cc b/media/video/software_video_encoder_test.cc
index 4ec61cd..3e879b8 100644
--- a/media/video/software_video_encoder_test.cc
+++ b/media/video/software_video_encoder_test.cc
@@ -316,6 +316,7 @@
 };
 
 class H264VideoEncoderTest : public SoftwareVideoEncoderTest {};
+class Vpx10BitVideoEncoderTest : public SoftwareVideoEncoderTest {};
 class SVCVideoEncoderTest : public SoftwareVideoEncoderTest {};
 class ManualSVCVideoEncoderTest : public SoftwareVideoEncoderTest {};
 class LargeTimestampOverflowTest : public SoftwareVideoEncoderTest {};
@@ -590,6 +591,46 @@
   EXPECT_EQ(total_decoded_frames, total_frames_count);
 }
 
+TEST_P(Vpx10BitVideoEncoderTest, EncodeDifferentMemoryTypes) {
+  VideoEncoder::Options options = CreateDefaultOptions();
+  options.frame_size = gfx::Size(2000, 2000);
+
+  encoder_->Initialize(profile_, options, /*info_cb=*/base::DoNothing(),
+                       /*output_cb=*/base::DoNothing(),
+                       ValidateStatusThenQuitCB());
+  RunUntilQuit();
+
+  VideoPixelFormat format1 = (profile_ == VP9PROFILE_PROFILE2)
+                                 ? PIXEL_FORMAT_YUV420P10
+                                 : PIXEL_FORMAT_YUV444P10;
+  VideoPixelFormat format2 =
+      (profile_ == VP9PROFILE_PROFILE2) ? PIXEL_FORMAT_I420 : PIXEL_FORMAT_I444;
+
+  // Encode a frame that doesn't need its own memory wrapper allocation.
+  auto frame1 = media::VideoFrame::CreateZeroInitializedFrame(
+      format1, options.frame_size, gfx::Rect(options.frame_size),
+      options.frame_size, base::Seconds(1));
+  encoder_->Encode(std::move(frame1), VideoEncoder::EncodeOptions(true),
+                   ValidateStatusThenQuitCB());
+  ASSERT_NO_FATAL_FAILURE(RunUntilQuit());
+
+  // Encode a frame that DOES need its own memory wrapper allocation.
+  auto frame2 = media::VideoFrame::CreateZeroInitializedFrame(
+      format2, options.frame_size, gfx::Rect(options.frame_size),
+      options.frame_size, base::Seconds(2));
+  encoder_->Encode(std::move(frame2), VideoEncoder::EncodeOptions(false),
+                   ValidateStatusThenQuitCB());
+  ASSERT_NO_FATAL_FAILURE(RunUntilQuit());
+
+  // Encode the first format again.
+  auto frame3 = media::VideoFrame::CreateZeroInitializedFrame(
+      format1, options.frame_size, gfx::Rect(options.frame_size),
+      options.frame_size, base::Seconds(3));
+  encoder_->Encode(std::move(frame3), VideoEncoder::EncodeOptions(false),
+                   ValidateStatusThenQuitCB());
+  ASSERT_NO_FATAL_FAILURE(RunUntilQuit());
+}
+
 TEST_P(SoftwareVideoEncoderTest, EncodeAndDecodeWithEnablingDrop) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(kWebCodecsVideoEncoderFrameDrop);
@@ -1527,6 +1568,15 @@
                          ::testing::ValuesIn(kVpxParams),
                          PrintTestParams);
 
+SwVideoTestParams kVpx10BitParams[] = {
+    {VideoCodec::kVP9, VP9PROFILE_PROFILE2, PIXEL_FORMAT_I420},
+    {VideoCodec::kVP9, VP9PROFILE_PROFILE3, PIXEL_FORMAT_I420}};
+
+INSTANTIATE_TEST_SUITE_P(Vpx10BitSpecific,
+                         Vpx10BitVideoEncoderTest,
+                         ::testing::ValuesIn(kVpx10BitParams),
+                         PrintTestParams);
+
 SwVideoTestParams kVpxSVCParams[] = {
     {VideoCodec::kVP9, VP9PROFILE_PROFILE0, PIXEL_FORMAT_I420, std::nullopt},
     {VideoCodec::kVP9, VP9PROFILE_PROFILE0, PIXEL_FORMAT_I420,
@@ -1603,6 +1653,7 @@
 #endif  // ENABLE_LIBAOM
 
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(H264VideoEncoderTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Vpx10BitVideoEncoderTest);
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SVCVideoEncoderTest);
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SoftwareVideoEncoderTest);
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ManualSVCVideoEncoderTest);
Loading diff…

Original Bug Report

reported by [email protected]

Heap UAF write in libyuv Convert8To16Row_Any_AVX2 via crafted raw VideoFrame input

Note

This is the same issues as https://issues.chromium.org/issues/488516881, which got incorrectly closed. Its reproduced in https://clusterfuzz.com/testcase?key=5670022819020800, which you can see ==1==ERROR: AddressSanitizer: heap-use-after-free on address 0x773c022c6440 at pc 0x5789c2ff30ae bp 0x746bf8394430 sp 0x746bf8393bf0 in the log but somehow Clusterfuzz didn’t manage to recognize it.

VULNERABILITY DETAILS

A WebCodecs VideoEncoder can be driven into a heap-use-after-free with a write primitive in the renderer process. The bug appears to be a lifetime/ownership error in the raw-frame conversion pipeline used by VideoEncoder. Attacker-controlled raw VideoFrame inputs are converted through libyuv (8-bit -> 10-bit path), and stale/freed memory is subsequently written during conversion. The failing write occurs in Convert8To16Row_Any_AVX2 while processing frame buffers derived from JS-supplied frame metadata and payloads (format/layout/stride/coded size/transfer behavior).

Crash log:

==217383==ERROR: AddressSanitizer: heap-use-after-free on address 0x7ea546140980 at pc 0x56334a039cfe bp 0x7b613739cf70 sp 0x7b613739c730
WRITE of size 44 at 0x7ea546140980 thread T4 (ThreadPoolForeg)
[217383:217383:0301/014040.337243:WARNING:media/base/video_frame_converter.cc:81] EncoderStatus::10 Data: {"dst":"format:PIXEL_FORMAT_I420 storage_type:OWNED_MEMORY coded_size:214x176 visible_rect:0,0 214x175 natural_size:214x175 timestamp:2600274 color_space: {primaries:INVALID, transfer:INVALID, matrix:INVALID, range:INVALID} hdr_metadata: {}","src":"format:PIXEL_FORMAT_YUV444P10 storage_type:OWNED_MEMORY coded_size:216x175 visible_rect:0,0 216x175 natural_size:216x175 timestamp:2600274 color_space: {primaries:BT709, transfer:BT709, matrix:BT709, range:LIMITED} hdr_metadata: {}"}
    #0 0x56334a039cfd in __asan_memcpy (/mnt/lvm_data/chromium/src/out/asan_dbg_symbols/chrome+0xf35acfd) (BuildId: 3afd3fb7d064a1b6)
    #1 0x7f667bb54faa in Convert8To16Row_Any_AVX2 third_party/libyuv/source/row_any.cc:1636:1
    #2 0x7f667bb23860 in Convert8To16Plane third_party/libyuv/source/planar_functions.cc:244:5
    #3 0x7f667bb0d7b3 in I420ToI010 third_party/libyuv/source/convert_from.cc:106:3
    #4 0x7f667aec2181 in media::VpxVideoEncoder::Encode(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>) media/video/vpx_video_encoder.cc:662:7
    #5 0x7f667ae2003b in void base::internal::DecayedFunctorTraits<void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), media::VideoEncoder*, scoped_refptr<media::VideoFrame>&&, media::VideoEncoder::EncodeOptions&&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>&&>::Invoke<void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), media::VideoEncoder*, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>(void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), media::VideoEncoder*&&, scoped_refptr<media::VideoFrame>&&, media::VideoEncoder::EncodeOptions&&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>&&) base/functional/bind_internal.h:740:12
    #6 0x7f667ae1fd74 in void base::internal::InvokeHelper<false, base::internal::FunctorTraits<void (media::VideoEncoder::*&&)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), media::VideoEncoder*, scoped_refptr<media::VideoFrame>&&, media::VideoEncoder::EncodeOptions&&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>&&>, void, 0ul, 1ul, 2ul, 3ul>::MakeItSo<void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), std::__Cr::tuple<base::internal::UnretainedWrapper<media::VideoEncoder, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>>(void (media::VideoEncoder::*&&)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), std::__Cr::tuple<base::internal::UnretainedWrapper<media::VideoEncoder, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>&&) base/functional/bind_internal.h:932:12
    #7 0x7f667ae1fa47 in void base::internal::Invoker<base::internal::FunctorTraits<void (media::VideoEncoder::*&&)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), media::VideoEncoder*, scoped_refptr<media::VideoFrame>&&, media::VideoEncoder::EncodeOptions&&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>&&>, base::internal::BindState<true, true, false, void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), base::internal::UnretainedWrapper<media::VideoEncoder, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>, void ()>::RunImpl<void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), std::__Cr::tuple<base::internal::UnretainedWrapper<media::VideoEncoder, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>, 0ul, 1ul, 2ul, 3ul>(void (media::VideoEncoder::*&&)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), std::__Cr::tuple<base::internal::UnretainedWrapper<media::VideoEncoder, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>&&, std::__Cr::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul>) base/functional/bind_internal.h:1069:14
    #8 0x7f667ae1f878 in base::internal::Invoker<base::internal::FunctorTraits<void (media::VideoEncoder::*&&)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), media::VideoEncoder*, scoped_refptr<media::VideoFrame>&&, media::VideoEncoder::EncodeOptions&&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>&&>, base::internal::BindState<true, true, false, void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), base::internal::UnretainedWrapper<media::VideoEncoder, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>, void ()>::RunOnce(base::internal::BindStateBase*) base/functional/bind_internal.h:982:12
    #9 0x7f66af5d3112 in base::OnceCallback<void ()>::Run() && base/functional/callback.h:155:12
    #10 0x7f66afaff12e in base::TaskAnnotator::RunTaskImpl(base::PendingTask&) base/task/common/task_annotator.cc:229:34
    #11 0x7f66afcec7d7 in void base::TaskAnnotator::RunTask<base::internal::TaskTracker::RunTaskImpl(base::internal::Task&, base::TaskTraits const&, base::internal::TaskSource*, base::internal::SequenceToken const&)::$_0>(perfetto::StaticString, base::PendingTask&, base::internal::TaskTracker::RunTaskImpl(base::internal::Task&, base::TaskTraits const&, base::internal::TaskSource*, base::internal::SequenceToken const&)::$_0&&) base/task/common/task_annotator.h:112:5
    #12 0x7f66afcec274 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:691:19
    #13 0x7f66afcec428 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:676:3
    #14 0x7f66afcea93d in base::internal::TaskTracker::RunTaskWithShutdownBehavior(base::internal::Task&, base::TaskTraits const&, base::internal::TaskSource*, base::internal::SequenceToken const&) base/task/thread_pool/task_tracker.cc:706:7
    #15 0x7f66afce9a49 in base::internal::TaskTracker::RunTask(base::internal::Task, base::internal::TaskSource*, base::TaskTraits const&, base::ThreadType) base/task/thread_pool/task_tracker.cc:506:5
    #16 0x7f66afce8593 in base::internal::TaskTracker::RunAndPopNextTask(base::internal::RegisteredTaskSource) base/task/thread_pool/task_tracker.cc:394:5
    #17 0x7f66afd3b243 in base::internal::WorkerThread::RunWorker() base/task/thread_pool/worker_thread.cc:473:36
    #18 0x7f66afd3a5ab in base::internal::WorkerThread::RunPooledWorker() base/task/thread_pool/worker_thread.cc:359:3
    #19 0x7f66afd39e25 in base::internal::WorkerThread::ThreadMain() base/task/thread_pool/worker_thread.cc:339:7
    #20 0x7f66afeac06c in base::(anonymous namespace)::ThreadFunc(void*) base/threading/platform_thread_posix.cc:102:13
    #21 0x56334a039896 in asan_thread_start(void*) asan_interceptors.cpp

0x7ea546140980 is located 384 bytes inside of 123967-byte region [0x7ea546140800,0x7ea54615ec3f)
freed by thread T-1 here:
AddressSanitizer: CHECK failed: asan_descriptions.cpp:184 "((res.trace)) != (0)" (0x0, 0x0) (tid=217660)
    #0 0x56334a046d61 in __asan::CheckUnwind() asan_rtl.cpp
    #1 0x56334a061af2 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) sanitizer_termination.cpp
    #2 0x563349fa50a3 in __asan::HeapAddressDescription::Print() const asan_descriptions.cpp
    #3 0x563349fa8167 in __asan::ErrorGeneric::Print() asan_errors.cpp
    #4 0x56334a04211f in __asan::ScopedInErrorReport::~ScopedInErrorReport() asan_report.cpp
    #5 0x56334a0444ac in __asan::ReportGenericError(unsigned long, unsigned long, unsigned long, unsigned long, bool, unsigned long, unsigned int, bool) asan_report.cpp
    #6 0x56334a039d30 in __asan_memcpy (/mnt/lvm_data/chromium/src/out/asan_dbg_symbols/chrome+0xf35ad30) (BuildId: 3afd3fb7d064a1b6)
    #7 0x7f667bb54faa in Convert8To16Row_Any_AVX2 third_party/libyuv/source/row_any.cc:1636:1
    #8 0x7f667bb23860 in Convert8To16Plane third_party/libyuv/source/planar_functions.cc:244:5
    #9 0x7f667bb0d7b3 in I420ToI010 third_party/libyuv/source/convert_from.cc:106:3
    #10 0x7f667aec2181 in media::VpxVideoEncoder::Encode(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>) media/video/vpx_video_encoder.cc:662:7
    #11 0x7f667ae2003b in void base::internal::DecayedFunctorTraits<void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), media::VideoEncoder*, scoped_refptr<media::VideoFrame>&&, media::VideoEncoder::EncodeOptions&&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>&&>::Invoke<void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), media::VideoEncoder*, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>(void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), media::VideoEncoder*&&, scoped_refptr<media::VideoFrame>&&, media::VideoEncoder::EncodeOptions&&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>&&) base/functional/bind_internal.h:740:12
    #12 0x7f667ae1fd74 in void base::internal::InvokeHelper<false, base::internal::FunctorTraits<void (media::VideoEncoder::*&&)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), media::VideoEncoder*, scoped_refptr<media::VideoFrame>&&, media::VideoEncoder::EncodeOptions&&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>&&>, void, 0ul, 1ul, 2ul, 3ul>::MakeItSo<void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), std::__Cr::tuple<base::internal::UnretainedWrapper<media::VideoEncoder, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>>(void (media::VideoEncoder::*&&)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), std::__Cr::tuple<base::internal::UnretainedWrapper<media::VideoEncoder, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>&&) base/functional/bind_internal.h:932:12
    #13 0x7f667ae1fa47 in void base::internal::Invoker<base::internal::FunctorTraits<void (media::VideoEncoder::*&&)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), media::VideoEncoder*, scoped_refptr<media::VideoFrame>&&, media::VideoEncoder::EncodeOptions&&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>&&>, base::internal::BindState<true, true, false, void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), base::internal::UnretainedWrapper<media::VideoEncoder, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>, void ()>::RunImpl<void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), std::__Cr::tuple<base::internal::UnretainedWrapper<media::VideoEncoder, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>, 0ul, 1ul, 2ul, 3ul>(void (media::VideoEncoder::*&&)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), std::__Cr::tuple<base::internal::UnretainedWrapper<media::VideoEncoder, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>&&, std::__Cr::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul>) base/functional/bind_internal.h:1069:14
    #14 0x7f667ae1f878 in base::internal::Invoker<base::internal::FunctorTraits<void (media::VideoEncoder::*&&)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), media::VideoEncoder*, scoped_refptr<media::VideoFrame>&&, media::VideoEncoder::EncodeOptions&&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>&&>, base::internal::BindState<true, true, false, void (media::VideoEncoder::*)(scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions const&, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>), base::internal::UnretainedWrapper<media::VideoEncoder, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>, scoped_refptr<media::VideoFrame>, media::VideoEncoder::EncodeOptions, base::OnceCallback<void (media::TypedStatus<media::EncoderStatusTraits>)>>, void ()>::RunOnce(base::internal::BindStateBase*) base/functional/bind_internal.h:982:12
    #15 0x7f66af5d3112 in base::OnceCallback<void ()>::Run() && base/functional/callback.h:155:12
    #16 0x7f66afaff12e in base::TaskAnnotator::RunTaskImpl(base::PendingTask&) base/task/common/task_annotator.cc:229:34
    #17 0x7f66afcec7d7 in void base::TaskAnnotator::RunTask<base::internal::TaskTracker::RunTaskImpl(base::internal::Task&, base::TaskTraits const&, base::internal::TaskSource*, base::internal::SequenceToken const&)::$_0>(perfetto::StaticString, base::PendingTask&, base::internal::TaskTracker::RunTaskImpl(base::internal::Task&, base::TaskTraits const&, base::internal::TaskSource*, base::internal::SequenceToken const&)::$_0&&) base/task/common/task_annotator.h:112:5
    #18 0x7f66afcec274 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:691:19
    #19 0x7f66afcec428 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:676:3
    #20 0x7f66afcea93d in base::internal::TaskTracker::RunTaskWithShutdownBehavior(base::internal::Task&, base::TaskTraits const&, base::internal::TaskSource*, base::internal::SequenceToken const&) base/task/thread_pool/task_tracker.cc:706:7
    #21 0x7f66afce9a49 in base::internal::TaskTracker::RunTask(base::internal::Task, base::internal::TaskSource*, base::TaskTraits const&, base::ThreadType) base/task/thread_pool/task_tracker.cc:506:5
    #22 0x7f66afce8593 in base::internal::TaskTracker::RunAndPopNextTask(base::internal::RegisteredTaskSource) base/task/thread_pool/task_tracker.cc:394:5
    #23 0x7f66afd3b243 in base::internal::WorkerThread::RunWorker() base/task/thread_pool/worker_thread.cc:473:36
    #24 0x7f66afd3a5ab in base::internal::WorkerThread::RunPooledWorker() base/task/thread_pool/worker_thread.cc:359:3
    #25 0x7f66afd39e25 in base::internal::WorkerThread::ThreadMain() base/task/thread_pool/worker_thread.cc:339:7
    #26 0x7f66afeac06c in base::(anonymous namespace)::ThreadFunc(void*) base/threading/platform_thread_posix.cc:102:13
    #27 0x56334a039896 in asan_thread_start(void*) asan_interceptors.cpp
    #28 0x7f656e9d4ac2 in start_thread nptl/pthread_create.c:442:8
    #29 0x7f656ea668cf  misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81


MiraclePtr Status: NOT PROTECTED
No raw_ptr<T> access to this region was detected prior to this crash.
This crash is still exploitable with MiraclePtr.
Refer to https://chromium.googlesource.com/chromium/src/+/main/base/memory/raw_ptr.md for details.

==100280==END OF ADDITIONAL INFO

VERSION

Chrome Version: 147.0.7703.0 (dev, ASan-instrumented Chromium build)

Operating System: Ubuntu 22.04.3 LTS (Jammy), x86_64, kernel 5.15.0-151-generic

REPRODUCTION CASE

PoC file:

  • highbd-fuzzer-1.html

Opening it with –autoplay-policy=no-user-gesture-required flag directly can trigger the bug. Note that this flag is not required for this vulnerability, just for making PoC leading to crash more reliably.

ASAN_OPTIONS=detect_odr_violation=0 chrome --headless file:///highbd-fuzzer-1.html --autoplay-policy=no-user-gesture-required

Also attached heap-spray-poc.html, which is a poc for controlled write, but it is less reliable.

Type of crash: Renderer (tab) process memory corruption

CREDIT INFORMATION

Reporter credit: heapracer (@heapracer)

View on issue tracker