High firefox Logic Error 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionUse-after-free in the Widget component
ComponentWidget
Bug ClassLogic Error
Tracker2049352
Fix commit9218e63542cc (firefox) +54/-24
CISA KEVNot listed
CreditedMohamed Mbarek
Disclosed2026-09-29

Changed Functions

FunctionChangeNotes
if
widget/cocoa/nsColorPicker.mm
modified
if
widget/gtk/nsApplicationChooser.cpp
modified
switch
widget/gtk/nsApplicationChooser.cpp
modified
if
widget/gtk/nsColorPicker.cpp
modified
switch
widget/gtk/nsColorPicker.cpp
modified
if
widget/gtk/nsFilePicker.cpp
modified
if
widget/nsFilePickerProxy.cpp
modified

Files Changed

  • widget/cocoa/nsColorPicker.mm
  • widget/gtk/nsApplicationChooser.cpp
  • widget/gtk/nsColorPicker.cpp
  • widget/gtk/nsFilePicker.cpp
  • widget/gtk/nsFilePicker.h
  • widget/nsFilePickerProxy.cpp
diff --git a/widget/cocoa/nsColorPicker.mm b/widget/cocoa/nsColorPicker.mm
index 92ff6c7da8f..cb54c1cc143 100644
--- a/widget/cocoa/nsColorPicker.mm
+++ b/widget/cocoa/nsColorPicker.mm
@@ -4,6 +4,8 @@
 
 #import <Cocoa/Cocoa.h>
 
+#include <utility>
+
 #include "nsCocoaUtils.h"
 #include "nsColorPicker.h"
 #include "nsThreadUtils.h"
@@ -158,14 +160,22 @@ nsresult nsColorPicker::OpenNative() {
 
 void nsColorPicker::Update(NSColor* aColor) {
   GetHexStringFromNSColor(aColor, mInitialColor);
-  mCallback->Update(mInitialColor);
+  if (mCallback) {
+    mCallback->Update(mInitialColor);
+  }
 }
 
 void nsColorPicker::Done() {
+  // A null callback means a re-entrant call; the outer one owns the teardown.
+  nsCOMPtr<nsIColorPickerShownCallback> callback = std::move(mCallback);
+  if (!callback) {
+    return;
+  }
+
   [mColorPanelWrapper close];
   [mColorPanelWrapper release];
   mColorPanelWrapper = nullptr;
-  mCallback->Done(u""_ns);
-  mCallback = nullptr;
+
+  callback->Done(u""_ns);
   NS_RELEASE_THIS();
 }
diff --git a/widget/gtk/nsApplicationChooser.cpp b/widget/gtk/nsApplicationChooser.cpp
index 67ccd8448e3..4976f4f3d61 100644
--- a/widget/gtk/nsApplicationChooser.cpp
+++ b/widget/gtk/nsApplicationChooser.cpp
@@ -7,6 +7,8 @@
 #include <gio/gdesktopappinfo.h>
 #include <gtk/gtk.h>
 
+#include <utility>
+
 #include "WidgetUtils.h"
 #include "mozilla/GRefPtr.h"
 #include "nsCExternalHandlerService.h"
@@ -75,6 +77,13 @@ void nsApplicationChooser::OnDestroy(GtkWidget* chooser, gpointer user_data) {
 }
 
 void nsApplicationChooser::Done(GtkWidget* chooser, gint response) {
+  // A null callback means a re-entrant call; the outer one owns the teardown.
+  nsCOMPtr<nsIApplicationChooserFinishedCallback> callback =
+      std::move(mCallback);
+  if (!callback) {
+    return;
+  }
+
   nsCOMPtr<nsIGIOHandlerApp> gioHandler;
   switch (response) {
     case GTK_RESPONSE_OK:
@@ -105,9 +114,6 @@ void nsApplicationChooser::Done(GtkWidget* chooser, gint response) {
                                        this);
   gtk_widget_destroy(chooser);
 
-  if (mCallback) {
-    mCallback->Done(gioHandler);
-    mCallback = nullptr;
-  }
+  callback->Done(gioHandler);
   NS_RELEASE_THIS();
 }
diff --git a/widget/gtk/nsColorPicker.cpp b/widget/gtk/nsColorPicker.cpp
index 9e957e78779..40acb970f80 100644
--- a/widget/gtk/nsColorPicker.cpp
+++ b/widget/gtk/nsColorPicker.cpp
@@ -4,6 +4,8 @@
 
 #include <gtk/gtk.h>
 
+#include <utility>
+
 #ifdef MOZ_X11
 #  include "X11UndefineNone.h"
 #endif
@@ -206,6 +208,12 @@ void nsColorPicker::OnDestroy(GtkWidget* color_chooser, gpointer user_data) {
 }
 
 void nsColorPicker::Done(GtkWidget* color_chooser, gint response) {
+  // A null callback means a re-entrant call; the outer one owns the teardown.
+  nsCOMPtr<nsIColorPickerShownCallback> callback = std::move(mCallback);
+  if (!callback) {
+    return;
+  }
+
   switch (response) {
     case GTK_RESPONSE_OK:
     case GTK_RESPONSE_ACCEPT:
@@ -232,10 +240,8 @@ void nsColorPicker::Done(GtkWidget* color_chooser, gint response) {
                                        this);
 
   gtk_widget_destroy(color_chooser);
-  if (mCallback) {
-    mCallback->Done(mColor);
-    mCallback = nullptr;
-  }
+
+  callback->Done(mColor);
 
   NS_RELEASE_THIS();
 }
diff --git a/widget/gtk/nsFilePicker.cpp b/widget/gtk/nsFilePicker.cpp
index f3e30bee0ad..b6bcca3e0e2 100644
--- a/widget/gtk/nsFilePicker.cpp
+++ b/widget/gtk/nsFilePicker.cpp
@@ -10,6 +10,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <utility>
+
 #include "AsyncDBus.h"
 #include "GRefPtr.h"
 #include "MozContainer.h"
@@ -664,7 +666,7 @@ void nsFilePicker::DonePortal(GVariant* aResult) {
   }
 
   ClearPortalState();
-  DoneCommon(result);
+  DoneCommon(result, std::move(mCallback));
 }
 #endif
 
@@ -878,6 +880,12 @@ void nsFilePicker::DoneNonPortal(GtkWidget* file_chooser, gint response) {
     return;
   }
 
+  // A null callback means a re-entrant call; the outer one owns the teardown.
+  nsCOMPtr<nsIFilePickerShownCallback> callback = std::move(mCallback);
+  if (!callback) {
+    return;
+  }
+
   mFileChooser = nullptr;
 
   nsIFilePicker::ResultCode result;
@@ -927,11 +935,12 @@ void nsFilePicker::DoneNonPortal(GtkWidget* file_chooser, gint response) {
     mFileChooserDelegate = nullptr;
   }
 
-  DoneCommon(result);
+  DoneCommon(result, std::move(callback));
   NS_RELEASE_THIS();
 }
 
-void nsFilePicker::DoneCommon(ResultCode aResult) {
+void nsFilePicker::DoneCommon(ResultCode aResult,
+                              nsCOMPtr<nsIFilePickerShownCallback> aCallback) {
   if (aResult == ResultCode::returnOK) {
     if (mMode == nsIFilePicker::modeSave) {
       nsCOMPtr<nsIFile> file;
@@ -961,9 +970,8 @@ void nsFilePicker::DoneCommon(ResultCode aResult) {
     }
   }
 
-  if (mCallback) {
-    mCallback->Done(aResult);
-    mCallback = nullptr;
+  if (aCallback) {
+    aCallback->Done(aResult);
   }
 }
 
diff --git a/widget/gtk/nsFilePicker.h b/widget/gtk/nsFilePicker.h
index 492ad69cccc..dcd43efcfbe 100644
--- a/widget/gtk/nsFilePicker.h
+++ b/widget/gtk/nsFilePicker.h
@@ -67,7 +67,7 @@ class nsFilePicker final : public nsBaseFilePicker {
   void ClearPortalState();
 #endif
 
-  void DoneCommon(ResultCode);
+  void DoneCommon(ResultCode, nsCOMPtr<nsIFilePickerShownCallback>);
 
   RefPtr<nsWindow> mParentWidget;
   nsCOMPtr<nsIFilePickerShownCallback> mCallback;
diff --git a/widget/nsFilePickerProxy.cpp b/widget/nsFilePickerProxy.cpp
index de61340a472..4194642853e 100644
--- a/widget/nsFilePickerProxy.cpp
+++ b/widget/nsFilePickerProxy.cpp
@@ -5,6 +5,8 @@
 
 #include "nsFilePickerProxy.h"
 
+#include <utility>
+
 #include "mozilla/dom/BlobImpl.h"
 #include "mozilla/dom/BrowserChild.h"
 #include "mozilla/dom/BrowsingContext.h"
@@ -203,9 +205,8 @@ mozilla::ipc::IPCResult nsFilePickerProxy::Recv__delete__(
     }
   }
 
-  if (mCallback) {
-    mCallback->Done(aResult);
-    mCallback = nullptr;
+  if (nsCOMPtr<nsIFilePickerShownCallback> callback = std::move(mCallback)) {
+    callback->Done(aResult);
   }
 
   return IPC_OK();
@@ -284,9 +285,8 @@ nsFilePickerProxy::GetDomFileOrDirectoryEnumerator(
 void nsFilePickerProxy::ActorDestroy(ActorDestroyReason aWhy) {
   mIPCActive = false;
 
-  if (mCallback) {
-    mCallback->Done(nsIFilePicker::returnCancel);
-    mCallback = nullptr;
+  if (nsCOMPtr<nsIFilePickerShownCallback> callback = std::move(mCallback)) {
+    callback->Done(nsIFilePicker::returnCancel);
   }
 }
 
Loading diff…