High firefox Memory Corruption 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionMemory safety bugs present in Firefox 152.0.3. Some of these bugs showed evidence of memory corruption and we presume that with enough effort some of these could have been exploited to run arbitrary code.
ComponentDOM
Bug ClassMemory Corruption
Tracker2043241
Fix commita52c881ebb3b (firefox) +42/-12
CISA KEVNot listed
CreditedChristian Holler, Gabriele Svelto, Greg Stoll and the Mozilla Fuzzing Team
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
if
dom/webserial/SerialPort.cpp
modified

Files Changed

  • dom/webserial/SerialPort.cpp
  • dom/webserial/SerialPort.h
diff --git a/dom/webserial/SerialPort.cpp b/dom/webserial/SerialPort.cpp
index 818fb9acea0..d5dcee6705a 100644
--- a/dom/webserial/SerialPort.cpp
+++ b/dom/webserial/SerialPort.cpp
@@ -137,7 +137,7 @@ void SerialPort::Shutdown() {
   if (mState == State::Opened || mState == State::Closing) {
     mState = State::Closed;
     // Don't have to wait for this
-    RefPtr<Promise> ignoredPromise = CloseStreams();
+    RefPtr<Promise> ignoredPromise = CloseStreams(StreamCloseMode::Forced);
   }
 
   if (mOpenPromise) {
@@ -529,7 +529,7 @@ already_AddRefed<Promise> SerialPort::Close(ErrorResult& aRv) {
   // promise to wait for all (steps 5-8). Our pendingClosePromise is
   // effectively resolved once both streams are nulled (step 6), so the
   // combined promise just waits for the cancel and abort to finish.
-  RefPtr<Promise> combinedPromise = CloseStreams();
+  RefPtr<Promise> combinedPromise = CloseStreams(StreamCloseMode::Graceful);
   if (!combinedPromise) {
     combinedPromise = Promise::CreateResolvedWithUndefined(global, aRv);
     if (NS_WARN_IF(aRv.Failed())) {
@@ -598,7 +598,7 @@ already_AddRefed<Promise> SerialPort::Forget(ErrorResult& aRv) {
 
   if (wasActive) {
     // Don't have to wait for this
-    RefPtr<Promise> ignoredPromise = CloseStreams();
+    RefPtr<Promise> ignoredPromise = CloseStreams(StreamCloseMode::Graceful);
   }
 
   UpdateWorkerRef();
@@ -652,7 +652,7 @@ void SerialPort::MarkForgotten() {
 
   if (wasActive) {
     // Don't have to wait for this
-    RefPtr<Promise> ignoredPromise = CloseStreams();
+    RefPtr<Promise> ignoredPromise = CloseStreams(StreamCloseMode::Graceful);
   }
 
   UpdateWorkerRef();
@@ -767,7 +767,7 @@ void SerialPort::NotifyDisconnected() {
   }
   mPhysicallyPresent = false;
   // Don't have to wait for this
-  RefPtr<Promise> ignoredPromise = CloseStreams();
+  RefPtr<Promise> ignoredPromise = CloseStreams(StreamCloseMode::Graceful);
   UpdateWorkerRef();
   NotifySharingStateChanged(false);
 
@@ -951,7 +951,7 @@ void SerialPort::SettleClosePromise(nsresult aResult) {
   }
 }
 
-already_AddRefed<Promise> SerialPort::CloseStreams() {
+already_AddRefed<Promise> SerialPort::CloseStreams(StreamCloseMode aMode) {
   nsIGlobalObject* global = GetRelevantGlobal();
   if (!global) {
     return nullptr;
@@ -963,8 +963,9 @@ already_AddRefed<Promise> SerialPort::CloseStreams() {
 
   MOZ_LOG(gWebSerialLog, LogLevel::Info,
           ("SerialPort[%p]::CloseStreams closing streams "
-           "(readable=%p, writable=%p)",
-           this, mReadable.get(), mWritable.get()));
+           "(readable=%p, writable=%p, mode=%s)",
+           this, mReadable.get(), mWritable.get(),
+           aMode == StreamCloseMode::Forced ? "forced" : "graceful"));
 
   AutoJSAPI jsapi;
   if (!jsapi.Init(global)) {
@@ -973,14 +974,33 @@ already_AddRefed<Promise> SerialPort::CloseStreams() {
 
   JSContext* cx = jsapi.cx();
 
-  // Cancel the readable and abort the writable, collecting their promises.
-  nsTArray<RefPtr<Promise>> streamPromises;
-
   RefPtr<DOMException> exception =
       DOMException::Create(NS_ERROR_DOM_NETWORK_ERR, "Port has been closed"_ns);
   JS::Rooted<JS::Value> errorVal(cx);
   bool hasError = ToJSValue(cx, exception, &errorVal);
 
+  // Error the streams instead of aborting, so we don't dispatchevents
+  // or runauthor-visible algorithms. The OS resources are released by
+  // mChild->Shutdown() in Shutdown().
+  if (aMode == StreamCloseMode::Forced) {
+    if (mReadable && hasError) {
+      IgnoredErrorResult rv;
+      RefPtr readable = mReadable;
+      readable->ErrorNative(cx, errorVal, rv);
+    }
+    if (mWritable && hasError) {
+      IgnoredErrorResult rv;
+      RefPtr writable = mWritable;
+      writable->ErrorNative(cx, errorVal, rv);
+    }
+    mReadable = nullptr;
+    mWritable = nullptr;
+    return nullptr;
+  }
+
+  // Cancel the readable and abort the writable, collecting their promises.
+  nsTArray<RefPtr<Promise>> streamPromises;
+
   if (mReadable && hasError) {
     IgnoredErrorResult rv;
     RefPtr readable = mReadable;
diff --git a/dom/webserial/SerialPort.h b/dom/webserial/SerialPort.h
index fb319115eaa..3950affdafe 100644
--- a/dom/webserial/SerialPort.h
+++ b/dom/webserial/SerialPort.h
@@ -116,11 +116,21 @@ class SerialPort final : public DOMEventTargetHelper {
  private:
   ~SerialPort() override;
 
+  enum class StreamCloseMode : uint8_t {
+    // Cancel the readable and abort the writable, running their underlying
+    // source/sink algorithms.
+    Graceful,
+    // Error both streams without running author-observable algorithms or
+    // dispatching events.
+    Forced,
+  };
+
   MOZ_CAN_RUN_SCRIPT ReadableStream* CreateReadableStream();
   MOZ_CAN_RUN_SCRIPT WritableStream* CreateWritableStream();
   void CloseAfterStreamsClosed();
   void SettleClosePromise(nsresult aResult);
-  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CloseStreams();
+  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CloseStreams(
+      StreamCloseMode aMode);
   void NotifySharingStateChanged(bool aConnected);
   void UpdateWorkerRef();
 
Loading diff…