Moderate firefox Logic Error 🔧 Commit mapped

Overview

Moderate
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionRace condition, use-after-free in the DOM: File component
ComponentDOM
Bug ClassLogic Error
Tracker2025386
Fix commita4f6917449ad (firefox) +145/-32
CISA KEVNot listed
CreditedMozilla
Disclosed2026-10-27

Changed Functions

FunctionChangeNotes
nsIHandleReportCallback
dom/file/BlobImpl.h
modified
nsIInputStream
dom/file/BlobImpl.h
modified
if
dom/file/BlobMemoryReporter.cpp
modified
for
dom/file/BlobMemoryReporter.cpp
modified
BlobImpl
dom/file/BlobMemoryReporter.h
modified
BlobMemoryReporter
dom/file/BlobMemoryReporter.h
modified
if
dom/file/StreamBlobImpl.cpp
modified
StreamBlobImpl
dom/file/StreamBlobImpl.h
modified
StringBlobImpl
dom/file/StringBlobImpl.h
modified

Files Changed

  • dom/file/BlobImpl.h
  • dom/file/BlobMemoryReporter.cpp
  • dom/file/BlobMemoryReporter.h
  • dom/file/StreamBlobImpl.cpp
  • dom/file/StreamBlobImpl.h
  • dom/file/StringBlobImpl.cpp
  • dom/file/StringBlobImpl.h
  • dom/file/moz.build
diff --git a/dom/file/BlobImpl.h b/dom/file/BlobImpl.h
index fea43f77b98..4b93e80e5c9 100644
--- a/dom/file/BlobImpl.h
+++ b/dom/file/BlobImpl.h
@@ -12,6 +12,7 @@
 #define BLOBIMPL_IID \
   {0xbccb3275, 0x6778, 0x4ac5, {0xaf, 0x03, 0x90, 0xed, 0x37, 0xad, 0xdf, 0x5d}}
 
+class nsIHandleReportCallback;
 class nsIInputStream;
 
 namespace mozilla {
@@ -103,6 +104,11 @@ class BlobImpl : public SupportsThreadSafeWeakPtr<BlobImpl>,
   // file is a directory.
   virtual bool IsDirectory() const { return false; }
 
+  virtual nsresult CollectReports(nsIHandleReportCallback* aHandleReport,
+                                  nsISupports* aData, bool aAnonymize) {
+    return NS_OK;
+  }
+
  protected:
   friend SupportsThreadSafeWeakPtr<BlobImpl>;
 
diff --git a/dom/file/BlobMemoryReporter.cpp b/dom/file/BlobMemoryReporter.cpp
new file mode 100644
index 00000000000..0fdd8b2f085
--- /dev/null
+++ b/dom/file/BlobMemoryReporter.cpp
@@ -0,0 +1,73 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BlobMemoryReporter.h"
+
+#include "BlobImpl.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/ThreadSafeWeakPtr.h"
+#include "nsTHashSet.h"
+
+namespace mozilla::dom {
+
+static StaticMutex sMutex;
+static StaticAutoPtr<nsTHashSet<ThreadSafeWeakPtr<BlobImpl>>> sEntries
+    MOZ_GUARDED_BY(sMutex);
+
+NS_IMPL_ISUPPORTS(BlobMemoryReporter, nsIMemoryReporter)
+
+BlobMemoryReporter::~BlobMemoryReporter() {
+  StaticMutexAutoLock lock(sMutex);
+  sEntries = nullptr;
+}
+
+/* static */
+void BlobMemoryReporter::Register(BlobImpl* aImpl) {
+  StaticMutexAutoLock lock(sMutex);
+
+  if (!sEntries) {
+    sEntries = new nsTHashSet<ThreadSafeWeakPtr<BlobImpl>>();
+    RegisterStrongMemoryReporter(MakeAndAddRef<BlobMemoryReporter>());
+  }
+
+  sEntries->Insert(aImpl);
+}
+
+/* static */
+void BlobMemoryReporter::Unregister(BlobImpl* aImpl) {
+  StaticMutexAutoLock lock(sMutex);
+  if (sEntries) {
+    sEntries->Remove(aImpl);
+  }
+}
+
+NS_IMETHODIMP
+BlobMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
+                                   nsISupports* aData, bool aAnonymize) {
+  // Don't release the strong reference under the lock.
+  nsTArray<RefPtr<BlobImpl>> alive;
+
+  {
+    StaticMutexAutoLock lock(sMutex);
+    if (!sEntries) {
+      return NS_OK;
+    }
+
+    for (const auto& key : *sEntries) {
+      if (RefPtr<BlobImpl> ref(key); ref) {
+        alive.AppendElement(std::move(ref));
+      }
+    }
+  }
+
+  for (const auto& impl : alive) {
+    nsresult rv = impl->CollectReports(aHandleReport, aData, aAnonymize);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+}  // namespace mozilla::dom
diff --git a/dom/file/BlobMemoryReporter.h b/dom/file/BlobMemoryReporter.h
new file mode 100644
index 00000000000..af2de0a1044
--- /dev/null
+++ b/dom/file/BlobMemoryReporter.h
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_BlobMemoryReporter_h
+#define mozilla_dom_BlobMemoryReporter_h
+
+#include "nsIMemoryReporter.h"
+
+namespace mozilla::dom {
+
+class BlobImpl;
+
+// Shared memory reporter for all BlobImpl instances.
+// Call Register/Unregister from BlobImpl ctor/dtor.
+class BlobMemoryReporter final : public nsIMemoryReporter {
+ public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIMEMORYREPORTER
+
+  static void Register(BlobImpl* aImpl);
+  static void Unregister(BlobImpl* aImpl);
+
+ private:
+  ~BlobMemoryReporter();
+};
+
+}  // namespace mozilla::dom
+
+#endif  // mozilla_dom_BlobMemoryReporter_h
diff --git a/dom/file/StreamBlobImpl.cpp b/dom/file/StreamBlobImpl.cpp
index e8d934bcfe8..3aacfd25451 100644
--- a/dom/file/StreamBlobImpl.cpp
+++ b/dom/file/StreamBlobImpl.cpp
@@ -4,6 +4,7 @@
 
 #include "StreamBlobImpl.h"
 
+#include "BlobMemoryReporter.h"
 #include "EmptyBlobImpl.h"
 #include "js/GCAPI.h"
 #include "mozilla/InputStreamLengthWrapper.h"
@@ -19,7 +20,7 @@
 
 namespace mozilla::dom {
 
-NS_IMPL_ISUPPORTS_INHERITED(StreamBlobImpl, BlobImpl, nsIMemoryReporter)
+MOZ_DEFINE_MALLOC_SIZE_OF(StreamBlobImplMallocSizeOf)
 
 static already_AddRefed<nsICloneableInputStream> EnsureCloneableStream(
     nsIInputStream* aInputStream, uint64_t aLength) {
@@ -120,7 +121,7 @@ StreamBlobImpl::~StreamBlobImpl() {
     nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mInputStream);
     stream->Close();
   }
-  UnregisterWeakMemoryReporter(this);
+  BlobMemoryReporter::Unregister(this);
 }
 
 void StreamBlobImpl::CreateInputStream(nsIInputStream** aStream,
@@ -185,12 +186,11 @@ void StreamBlobImpl::MaybeRegisterMemoryReporter() {
     return;
   }
 
-  RegisterWeakMemoryReporter(this);
+  BlobMemoryReporter::Register(this);
 }
 
-NS_IMETHODIMP
-StreamBlobImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
-                               nsISupports* aData, bool aAnonymize) {
+nsresult StreamBlobImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
+                                        nsISupports* aData, bool aAnonymize) {
   nsCOMPtr<nsIStringInputStream> stringInputStream =
       do_QueryInterface(mInputStream);
   if (!stringInputStream) {
@@ -198,10 +198,11 @@ StreamBlobImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
   }
 
   MOZ_COLLECT_REPORT(
-      "explicit/dom/memory-file-data/stream", KIND_HEAP, UNITS_BYTES,
-      stringInputStream->SizeOfIncludingThisIfUnshared(MallocSizeOf),
+      "explicit/dom/memory-file-data/stream", nsIMemoryReporter::KIND_HEAP,
+      nsIMemoryReporter::UNITS_BYTES,
+      stringInputStream->SizeOfIncludingThisIfUnshared(
+          StreamBlobImplMallocSizeOf),
       "Memory used to back a File/Blob based on an input stream.");
-
   return NS_OK;
 }
 
@@ -217,7 +218,8 @@ size_t StreamBlobImpl::GetAllocationSize() const {
     return 0;
   }
 
-  return stringInputStream->SizeOfIncludingThisEvenIfShared(MallocSizeOf);
+  return stringInputStream->SizeOfIncludingThisEvenIfShared(
+      StreamBlobImplMallocSizeOf);
 }
 
 void StreamBlobImpl::GetBlobImplType(nsAString& aBlobImplType) const {
diff --git a/dom/file/StreamBlobImpl.h b/dom/file/StreamBlobImpl.h
index 03becc95d52..29781394e0f 100644
--- a/dom/file/StreamBlobImpl.h
+++ b/dom/file/StreamBlobImpl.h
@@ -8,16 +8,12 @@
 #include "BaseBlobImpl.h"
 #include "nsCOMPtr.h"
 #include "nsICloneableInputStream.h"
-#include "nsIMemoryReporter.h"
 
 namespace mozilla::dom {
 
-class StreamBlobImpl final : public BaseBlobImpl, public nsIMemoryReporter {
-  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
-
+class StreamBlobImpl final : public BaseBlobImpl {
  public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIMEMORYREPORTER
+  NS_INLINE_DECL_REFCOUNTING_INHERITED(StreamBlobImpl, BaseBlobImpl)
 
   // Blob constructor.
   static already_AddRefed<StreamBlobImpl> Create(
@@ -77,6 +73,9 @@ class StreamBlobImpl final : public BaseBlobImpl, public nsIMemoryReporter {
 
   ~StreamBlobImpl() override;
 
+  nsresult CollectReports(nsIHandleReportCallback* aHandleReport,
+                          nsISupports* aData, bool aAnonymize) override;
+
   void MaybeRegisterMemoryReporter();
 
   nsCOMPtr<nsICloneableInputStream> mInputStream;
diff --git a/dom/file/StringBlobImpl.cpp b/dom/file/StringBlobImpl.cpp
index f5817545e5d..faabdd43d4d 100644
--- a/dom/file/StringBlobImpl.cpp
+++ b/dom/file/StringBlobImpl.cpp
@@ -4,17 +4,18 @@
 
 #include "StringBlobImpl.h"
 
+#include "BlobMemoryReporter.h"
 #include "nsStringStream.h"
 
 namespace mozilla::dom {
 
-NS_IMPL_ISUPPORTS_INHERITED(StringBlobImpl, BlobImpl, nsIMemoryReporter)
+MOZ_DEFINE_MALLOC_SIZE_OF(StringBlobImplMallocSizeOf)
 
 /* static */
 already_AddRefed<StringBlobImpl> StringBlobImpl::Create(
     const nsACString& aData, const nsAString& aContentType) {
   RefPtr<StringBlobImpl> blobImpl = new StringBlobImpl(aData, aContentType);
-  RegisterWeakMemoryReporter(blobImpl);
+  BlobMemoryReporter::Register(blobImpl);
   return blobImpl.forget();
 }
 
@@ -22,13 +23,14 @@ StringBlobImpl::StringBlobImpl(const nsACString& aData,
                                const nsAString& aContentType)
     : BaseBlobImpl(aContentType, aData.Length()), mData(aData) {}
 
-StringBlobImpl::~StringBlobImpl() { UnregisterWeakMemoryReporter(this); }
+StringBlobImpl::~StringBlobImpl() { BlobMemoryReporter::Unregister(this); }
 
 already_AddRefed<BlobImpl> StringBlobImpl::CreateSlice(
     uint64_t aStart, uint64_t aLength, const nsAString& aContentType,
     ErrorResult& aRv) const {
   RefPtr<BlobImpl> impl =
       new StringBlobImpl(Substring(mData, aStart, aLength), aContentType);
+  BlobMemoryReporter::Register(impl);
   return impl.forget();
 }
 
@@ -37,13 +39,13 @@ void StringBlobImpl::CreateInputStream(nsIInputStream** aStream,
   aRv = NS_NewCStringInputStream(aStream, mData);
 }
 
-NS_IMETHODIMP
-StringBlobImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
-                               nsISupports* aData, bool aAnonymize) {
-  MOZ_COLLECT_REPORT("explicit/dom/memory-file-data/string", KIND_HEAP,
-                     UNITS_BYTES,
-                     mData.SizeOfExcludingThisIfUnshared(MallocSizeOf),
-                     "Memory used to back a File/Blob based on a string.");
+nsresult StringBlobImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
+                                        nsISupports* aData, bool aAnonymize) {
+  MOZ_COLLECT_REPORT(
+      "explicit/dom/memory-file-data/string", nsIMemoryReporter::KIND_HEAP,
+      nsIMemoryReporter::UNITS_BYTES,
+      mData.SizeOfExcludingThisIfUnshared(StringBlobImplMallocSizeOf),
+      "Memory used to back a File/Blob based on a string.");
   return NS_OK;
 }
 
diff --git a/dom/file/StringBlobImpl.h b/dom/file/StringBlobImpl.h
index 796311c33da..245ee85728a 100644
--- a/dom/file/StringBlobImpl.h
+++ b/dom/file/StringBlobImpl.h
@@ -6,16 +6,12 @@
 #define mozilla_dom_StringBlobImpl_h
 
 #include "BaseBlobImpl.h"
-#include "nsIMemoryReporter.h"
 
 namespace mozilla::dom {
 
-class StringBlobImpl final : public BaseBlobImpl, public nsIMemoryReporter {
-  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
-
+class StringBlobImpl final : public BaseBlobImpl {
  public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIMEMORYREPORTER
+  NS_INLINE_DECL_REFCOUNTING_INHERITED(StringBlobImpl, BaseBlobImpl)
 
   static already_AddRefed<StringBlobImpl> Create(const nsACString& aData,
                                                  const nsAString& aContentType);
@@ -43,6 +39,9 @@ class StringBlobImpl final : public BaseBlobImpl, public nsIMemoryReporter {
 
   ~StringBlobImpl() override;
 
+  nsresult CollectReports(nsIHandleReportCallback* aHandleReport,
+                          nsISupports* aData, bool aAnonymize) override;
+
   nsCString mData;
 };
 
diff --git a/dom/file/moz.build b/dom/file/moz.build
index b8467975cf6..efc522f038f 100644
--- a/dom/file/moz.build
+++ b/dom/file/moz.build
@@ -11,6 +11,7 @@ EXPORTS.mozilla.dom += [
     "BaseBlobImpl.h",
     "Blob.h",
     "BlobImpl.h",
+    "BlobMemoryReporter.h",
     "BlobSet.h",
     "EmptyBlobImpl.h",
     "File.h",
@@ -30,6 +31,7 @@ UNIFIED_SOURCES += [
     "BaseBlobImpl.cpp",
     "Blob.cpp",
     "BlobImpl.cpp",
+    "BlobMemoryReporter.cpp",
     "BlobSet.cpp",
     "EmptyBlobImpl.cpp",
     "File.cpp",
Loading diff…