Low firefox Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactlow
DescriptionDenial-of-service due to invalid pointer in the Audio/Video: Web Codecs component
ComponentDOM
Bug ClassLogic Error
Tracker2030467
Fix commitae1b6eb6cfbc (firefox) +28/-41
CISA KEVNot listed
CreditedTristan Madani
Disclosed2026-05-19

Changed Functions

FunctionChangeNotes
if
dom/media/webcodecs/ImageDecoder.cpp
modified

Files Changed

  • dom/media/webcodecs/ImageDecoder.cpp
  • dom/media/webcodecs/ImageDecoder.h
  • dom/media/webcodecs/ImageTrackList.cpp
diff --git a/dom/media/webcodecs/ImageDecoder.cpp b/dom/media/webcodecs/ImageDecoder.cpp
index 78a93f22e6a..ae93624cbd1 100644
--- a/dom/media/webcodecs/ImageDecoder.cpp
+++ b/dom/media/webcodecs/ImageDecoder.cpp
@@ -79,7 +79,8 @@ class ImageDecoder::SelectTrackMessage final
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ImageDecoder)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ImageDecoder)
-  tmp->Destroy();
+  tmp->CloseWithoutRef(
+      MediaResult(NS_ERROR_DOM_ABORT_ERR, "Cycle-collected decoder"_ns));
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadRequest)
@@ -118,7 +119,7 @@ ImageDecoder::ImageDecoder(nsCOMPtr<nsIGlobalObject>&& aParent,
 ImageDecoder::~ImageDecoder() {
   MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
           ("ImageDecoder %p ~ImageDecoder", this));
-  Destroy();
+  CloseWithoutRef(MediaResult(NS_ERROR_DOM_ABORT_ERR, "Destroyed decoder"_ns));
 }
 
 JSObject* ImageDecoder::WrapObject(JSContext* aCx,
@@ -127,33 +128,6 @@ JSObject* ImageDecoder::WrapObject(JSContext* aCx,
   return ImageDecoder_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-void ImageDecoder::Destroy() {
-  MOZ_LOG(gWebCodecsLog, LogLevel::Debug, ("ImageDecoder %p Destroy", this));
-  MOZ_ASSERT(mOutstandingDecodes.IsEmpty());
-
-  if (mReadRequest) {
-    mReadRequest->Destroy(/* aCancel */ false);
-    mReadRequest = nullptr;
-  }
-
-  if (mDecoder) {
-    mDecoder->Destroy();
-  }
-
-  if (mTracks) {
-    mTracks->Destroy();
-  }
-
-  if (mShutdownWatcher) {
-    mShutdownWatcher->Destroy();
-    mShutdownWatcher = nullptr;
-  }
-
-  mSourceBuffer = nullptr;
-  mDecoder = nullptr;
-  mParent = nullptr;
-}
-
 void ImageDecoder::QueueConfigureMessage(
     const Maybe<gfx::IntSize>& aOutputSize,
     ColorSpaceConversion aColorSpaceConversion) {
@@ -180,7 +154,7 @@ void ImageDecoder::ResumeControlMessageQueue() {
 }
 
 void ImageDecoder::ProcessControlMessageQueue() {
-  while (!mMessageQueueBlocked && !mControlMessageQueue.empty()) {
+  while (!mClosed && !mMessageQueueBlocked && !mControlMessageQueue.empty()) {
     auto& msg = mControlMessageQueue.front();
     auto result = MessageProcessedResult::Processed;
     if (auto* submsg = msg->AsConfigureMessage()) {
@@ -990,6 +964,7 @@ already_AddRefed<Promise> ImageDecoder::Decode(
   QueueDecodeFrameMessage();
 
   // 7. Process the control message queue.
+  RefPtr<ImageDecoder> kungFuDeathGrip(this);
   ProcessControlMessageQueue();
 
   // 8. Return promise.
@@ -1036,8 +1011,9 @@ void ImageDecoder::OnDecodeFramesFailed(const nsresult& aErr) {
   }
 }
 
-void ImageDecoder::Reset(const MediaResult& aResult) {
-  MOZ_LOG(gWebCodecsLog, LogLevel::Debug, ("ImageDecoder %p Reset", this));
+void ImageDecoder::ResetWithoutRef(const MediaResult& aResult) {
+  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
+          ("ImageDecoder %p Reset '%s'", this, aResult.Message().get()));
   // 10.2.5. Reset ImageDecoder (with exception)
 
   // 1. Signal [[codec implementation]] to abort any active decoding operation.
@@ -1058,18 +1034,23 @@ void ImageDecoder::Reset(const MediaResult& aResult) {
 
 void ImageDecoder::Close(const MediaResult& aResult) {
   RefPtr<ImageDecoder> kungFuDeathGrip(this);
-  MOZ_LOG(gWebCodecsLog, LogLevel::Debug, ("ImageDecoder %p Close", this));
+  CloseWithoutRef(aResult);
+}
 
+void ImageDecoder::CloseWithoutRef(const MediaResult& aResult) {
   if (mClosed) {
     return;
   }
 
+  MOZ_LOG(gWebCodecsLog, LogLevel::Debug,
+          ("ImageDecoder %p Close '%s'", this, aResult.Message().get()));
+
   // 10.2.5. Algorithms - Close ImageDecoder (with exception)
   mClosed = true;
   mTypeNotSupported = aResult.Code() == NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 
   // 1. Run the Reset ImageDecoder algorithm with exception.
-  Reset(aResult);
+  ResetWithoutRef(aResult);
 
   // 3. Clear [[codec implementation]] and release associated system resources.
   if (mDecoder) {
@@ -1093,7 +1074,9 @@ void ImageDecoder::Close(const MediaResult& aResult) {
   }
 
   if (!mComplete) {
-    aResult.RejectTo(mCompletePromise);
+    if (mCompletePromise) {
+      aResult.RejectTo(mCompletePromise);
+    }
     mComplete = true;
   }
 
@@ -1104,7 +1087,8 @@ void ImageDecoder::Close(const MediaResult& aResult) {
 }
 
 void ImageDecoder::Reset() {
-  Reset(MediaResult(NS_ERROR_DOM_ABORT_ERR, "Reset decoder"_ns));
+  RefPtr<ImageDecoder> kungFuDeathGrip(this);
+  ResetWithoutRef(MediaResult(NS_ERROR_DOM_ABORT_ERR, "Reset decoder"_ns));
 }
 
 void ImageDecoder::Close() {
diff --git a/dom/media/webcodecs/ImageDecoder.h b/dom/media/webcodecs/ImageDecoder.h
index ed87b4b047a..855712a4c59 100644
--- a/dom/media/webcodecs/ImageDecoder.h
+++ b/dom/media/webcodecs/ImageDecoder.h
@@ -80,6 +80,8 @@ class ImageDecoder final : public nsISupports,
   void QueueSelectTrackMessage(uint32_t aSelectedIndex);
   void ProcessControlMessageQueue();
 
+  void ResetWithoutRef(const MediaResult& aResult);
+
  private:
   ~ImageDecoder();
 
@@ -104,9 +106,8 @@ class ImageDecoder final : public nsISupports,
 
   void Initialize(const GlobalObject& aGLobal, const ImageDecoderInit& aInit,
                   ErrorResult& aRv);
-  void Destroy();
-  void Reset(const MediaResult& aResult);
   void Close(const MediaResult& aResult);
+  void CloseWithoutRef(const MediaResult& aResult);
 
   void QueueConfigureMessage(const Maybe<gfx::IntSize>& aOutputSize,
                              ColorSpaceConversion aColorSpaceConversion);
diff --git a/dom/media/webcodecs/ImageTrackList.cpp b/dom/media/webcodecs/ImageTrackList.cpp
index aa4314ec06d..455e70a6a6b 100644
--- a/dom/media/webcodecs/ImageTrackList.cpp
+++ b/dom/media/webcodecs/ImageTrackList.cpp
@@ -201,14 +201,16 @@ void ImageTrackList::SetSelectedIndex(int32_t aIndex, bool aSelected) {
   }
 
   // 10. Run the Reset ImageDecoder algorithm on [[ImageDecoder]].
-  mDecoder->Reset();
+  RefPtr<ImageDecoder> decoder = mDecoder;
+  decoder->ResetWithoutRef(
+      MediaResult(NS_ERROR_DOM_ABORT_ERR, "Reset decoder (select index)"_ns));
 
   // 11. Queue a control message to [[ImageDecoder]]'s control message queue to
   //     update the internal selected track index with selectedIndex.
-  mDecoder->QueueSelectTrackMessage(mSelectedIndex);
+  decoder->QueueSelectTrackMessage(mSelectedIndex);
 
   // 12. Process the control message queue belonging to [[ImageDecoder]].
-  mDecoder->ProcessControlMessageQueue();
+  decoder->ProcessControlMessageQueue();
 }
 
 }  // namespace mozilla::dom
Loading diff…