High firefox Memory Corruption 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionMemory safety bugs present in Firefox 147 and Thunderbird 147. 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.
ComponentCore
Bug ClassMemory Corruption
Tracker1756056
Fix commit3aa4f024fc89 (firefox) +187/-133
CISA KEVNot listed
CreditedAgi Sferro, Andrew McCreight, Randell Jesup, Tom Schuster
Disclosed2026-02-24

Changed Functions

FunctionChangeNotes
if
mobile/shared/actors/ContentDelegateChild.sys.mjs
modified
actorCreated
mobile/shared/actors/GeckoViewContentChild.sys.mjs
modified
switch
mobile/shared/actors/GeckoViewContentParent.sys.mjs
modified
restoreState
mobile/shared/actors/GeckoViewContentParent.sys.mjs
modified
if
mobile/shared/actors/GeckoViewContentParent.sys.mjs
modified
getMediaPermission
mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
modified
mediaRecordingStatusChanged
mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
modified
dismissPrompt
mobile/shared/actors/GeckoViewPrompterChild.sys.mjs
modified
updatePrompt
mobile/shared/actors/GeckoViewPrompterChild.sys.mjs
modified
switch
mobile/shared/actors/LoadURIDelegateParent.sys.mjs
modified
if
mobile/shared/actors/MediaControlDelegateChild.sys.mjs
modified
switch
mobile/shared/actors/MediaControlDelegateParent.sys.mjs
modified

Files Changed

  • mobile/shared/actors/ContentDelegateChild.sys.mjs
  • mobile/shared/actors/ContentDelegateParent.sys.mjs
  • mobile/shared/actors/GeckoViewContentChild.sys.mjs
  • mobile/shared/actors/GeckoViewContentParent.sys.mjs
  • mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
  • mobile/shared/actors/GeckoViewPermissionParent.sys.mjs
  • mobile/shared/actors/GeckoViewPrompterChild.sys.mjs
  • mobile/shared/actors/GeckoViewPrompterParent.sys.mjs
  • mobile/shared/actors/LoadURIDelegateChild.sys.mjs
  • mobile/shared/actors/LoadURIDelegateParent.sys.mjs
  • mobile/shared/actors/MediaControlDelegateChild.sys.mjs
  • mobile/shared/actors/MediaControlDelegateParent.sys.mjs
  • mobile/shared/actors/ScrollDelegateChild.sys.mjs
  • mobile/shared/actors/ScrollDelegateParent.sys.mjs
  • mobile/shared/actors/SelectionActionDelegateChild.sys.mjs
  • mobile/shared/actors/SelectionActionDelegateParent.sys.mjs
  • mobile/shared/modules/geckoview/GeckoViewActorChild.sys.mjs
  • mobile/shared/modules/geckoview/GeckoViewActorParent.sys.mjs
  • mobile/shared/modules/geckoview/LoadURIDelegate.sys.mjs
  • mobile/shared/modules/geckoview/Messaging.sys.mjs
diff --git a/mobile/shared/actors/ContentDelegateChild.sys.mjs b/mobile/shared/actors/ContentDelegateChild.sys.mjs
index 5269fe3fd9b..f5a30687b2c 100644
--- a/mobile/shared/actors/ContentDelegateChild.sys.mjs
+++ b/mobile/shared/actors/ContentDelegateChild.sys.mjs
@@ -27,10 +27,7 @@ export class ContentDelegateChild extends GeckoViewActorChild {
           return;
         }
         this.lastViewportFit = viewportFit;
-        this.eventDispatcher.sendRequest({
-          type: "GeckoView:DOMMetaViewportFit",
-          viewportfit: viewportFit,
-        });
+        this.sendAsyncMessage("GeckoView:DOMMetaViewportFit", viewportFit);
       }
     );
   }
@@ -179,7 +176,6 @@ export class ContentDelegateChild extends GeckoViewActorChild {
 
         if (uri || isImage || isMedia) {
           const msg = {
-            type: "GeckoView:ContextMenu",
             // We don't have full zoom on Android, so using CSS coordinates
             // here is fine, since the CSS coordinate spaces match between the
             // child and parent processes.
@@ -200,13 +196,13 @@ export class ContentDelegateChild extends GeckoViewActorChild {
               null,
           };
 
-          this.eventDispatcher.sendRequest(msg);
+          this.sendAsyncMessage("GeckoView:ContextMenu", msg);
           aEvent.preventDefault();
         }
         break;
       }
       case "MozDOMFullscreen:Request": {
-        this.sendAsyncMessage("GeckoView:DOMFullscreenRequest", {});
+        this.sendAsyncMessage("GeckoView:DOMFullscreenRequest");
         break;
       }
       case "MozDOMFullscreen:Entered":
@@ -219,7 +215,7 @@ export class ContentDelegateChild extends GeckoViewActorChild {
         }
       // fall-through
       case "MozDOMFullscreen:Exit":
-        this.sendAsyncMessage("GeckoView:DOMFullscreenExit", {});
+        this.sendAsyncMessage("GeckoView:DOMFullscreenExit");
         break;
       case "DOMMetaViewportFitChanged":
         if (aEvent.originalTarget.ownerGlobal == this.contentWindow) {
@@ -241,24 +237,17 @@ export class ContentDelegateChild extends GeckoViewActorChild {
             this.contentWindow
           );
           if (manifest) {
-            this.eventDispatcher.sendRequest({
-              type: "GeckoView:WebAppManifest",
-              manifest,
-            });
+            this.sendAsyncMessage("GeckoView:WebAppManifest", manifest);
           }
         });
         break;
       }
       case "MozFirstContentfulPaint": {
-        this.eventDispatcher.sendRequest({
-          type: "GeckoView:FirstContentfulPaint",
-        });
+        this.sendAsyncMessage("GeckoView:FirstContentfulPaint");
         break;
       }
       case "MozPaintStatusReset": {
-        this.eventDispatcher.sendRequest({
-          type: "GeckoView:PaintStatusReset",
-        });
+        this.sendAsyncMessage("GeckoView:PaintStatusReset");
         break;
       }
     }
diff --git a/mobile/shared/actors/ContentDelegateParent.sys.mjs b/mobile/shared/actors/ContentDelegateParent.sys.mjs
index d621c801047..f11fde38f52 100644
--- a/mobile/shared/actors/ContentDelegateParent.sys.mjs
+++ b/mobile/shared/actors/ContentDelegateParent.sys.mjs
@@ -29,6 +29,39 @@ export class ContentDelegateParent extends GeckoViewActorParent {
         this.window.windowUtils.remoteFrameFullscreenChanged(this.browser);
         return null;
       }
+
+      case "GeckoView:DOMMetaViewportFit": {
+        return this.eventDispatcher.sendRequest({
+          viewportfit: aMsg.data,
+          type: "GeckoView:DOMMetaViewportFit",
+        });
+      }
+
+      case "GeckoView:ContextMenu": {
+        return this.eventDispatcher.sendRequest({
+          ...aMsg.data,
+          type: "GeckoView:ContextMenu",
+        });
+      }
+
+      case "GeckoView:WebAppManifest": {
+        return this.eventDispatcher.sendRequest({
+          manifest: aMsg.data,
+          type: "GeckoView:WebAppManifest",
+        });
+      }
+
+      case "GeckoView:FirstContentfulPaint": {
+        return this.eventDispatcher.sendRequest({
+          type: "GeckoView:FirstContentfulPaint",
+        });
+      }
+
+      case "GeckoView:PaintStatusReset": {
+        return this.eventDispatcher.sendRequest({
+          type: "GeckoView:PaintStatusReset",
+        });
+      }
     }
 
     return super.receiveMessage(aMsg);
diff --git a/mobile/shared/actors/GeckoViewContentChild.sys.mjs b/mobile/shared/actors/GeckoViewContentChild.sys.mjs
index 23cfc73de50..cef3910b568 100644
--- a/mobile/shared/actors/GeckoViewContentChild.sys.mjs
+++ b/mobile/shared/actors/GeckoViewContentChild.sys.mjs
@@ -32,8 +32,6 @@ export class GeckoViewContentChild extends GeckoViewActorChild {
   }
 
   actorCreated() {
-    super.actorCreated();
-
     this.pageShow = new Promise(resolve => {
       this.receivedPageShow = resolve;
     });
@@ -335,8 +333,7 @@ export class GeckoViewContentChild extends GeckoViewActorChild {
           aEvent.reason === "presscaret" ||
           aEvent.reason === "releasecaret"
         ) {
-          this.eventDispatcher.sendRequest({
-            type: "GeckoView:PinOnScreen",
+          this.sendAsyncMessage("GeckoView:PinOnScreen", {
             pinned: aEvent.reason === "presscaret",
           });
         }
diff --git a/mobile/shared/actors/GeckoViewContentParent.sys.mjs b/mobile/shared/actors/GeckoViewContentParent.sys.mjs
index 069894ba967..8f61cfaf64c 100644
--- a/mobile/shared/actors/GeckoViewContentParent.sys.mjs
+++ b/mobile/shared/actors/GeckoViewContentParent.sys.mjs
@@ -28,6 +28,20 @@ export class GeckoViewContentParent extends GeckoViewActorParent {
     return this.sendQuery("ContainsFormData");
   }
 
+  async receiveMessage(aMsg) {
+    switch (aMsg.name) {
+      case "GeckoView:PinOnScreen": {
+        return this.eventDispatcher.sendRequest({
+          ...aMsg.data,
+          type: "GeckoView:PinOnScreen",
+        });
+      }
+      default: {
+        return super.receiveMessage(aMsg);
+      }
+    }
+  }
+
   restoreState({ history, switchId, formdata, scrolldata }) {
     if (Services.appinfo.sessionHistoryInParent) {
       const { browsingContext } = this.browser;
diff --git a/mobile/shared/actors/GeckoViewPermissionChild.sys.mjs b/mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
index bbe9457cc5e..d454250bb56 100644
--- a/mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
+++ b/mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
@@ -20,8 +20,7 @@ const MAPPED_TO_EXTENSION_PERMISSIONS = [
 
 export class GeckoViewPermissionChild extends GeckoViewActorChild {
   getMediaPermission(aPermission) {
-    return this.eventDispatcher.sendRequestForResult({
-      type: "GeckoView:MediaPermission",
+    return this.sendQuery("GeckoView:MediaPermission", {
       ...aPermission,
     });
   }
@@ -35,8 +34,7 @@ export class GeckoViewPermissionChild extends GeckoViewActorChild {
   }
 
   mediaRecordingStatusChanged(aDevices) {
-    return this.eventDispatcher.sendRequest({
-      type: "GeckoView:MediaRecordingStatusChanged",
+    return this.sendAsyncMessage("GeckoView:MediaRecordingStatusChanged", {
       devices: aDevices,
     });
   }
@@ -132,8 +130,7 @@ export class GeckoViewPermissionChild extends GeckoViewActorChild {
 
     let allowOrDeny;
     try {
-      allowOrDeny = await this.eventDispatcher.sendRequestForResult({
-        type: "GeckoView:ContentPermission",
+      allowOrDeny = await this.sendQuery("GeckoView:ContentPermission", {
         uri: principal.URI.displaySpec,
         thirdPartyOrigin: aRequest.principal.origin,
         principal: lazy.E10SUtils.serializePrincipal(principal),
diff --git a/mobile/shared/actors/GeckoViewPermissionParent.sys.mjs b/mobile/shared/actors/GeckoViewPermissionParent.sys.mjs
index c4a5cf56cf0..867fc31bdbd 100644
--- a/mobile/shared/actors/GeckoViewPermissionParent.sys.mjs
+++ b/mobile/shared/actors/GeckoViewPermissionParent.sys.mjs
@@ -56,6 +56,24 @@ export class GeckoViewPermissionParent extends GeckoViewActorParent {
       case "AddCameraPermission": {
         return this.addCameraPermission();
       }
+      case "GeckoView:MediaPermission": {
+        return this.eventDispatcher.sendRequestForResult({
+          ...aMessage.data,
+          type: "GeckoView:MediaPermission",
+        });
+      }
+      case "GeckoView:MediaRecordingStatusChanged": {
+        return this.eventDispatcher.sendRequest({
+          ...aMessage.data,
+          type: "GeckoView:MediaRecordingStatusChanged",
+        });
+      }
+      case "GeckoView:ContentPermission": {
+        return this.eventDispatcher.sendRequestForResult({
+          ...aMessage.data,
+          type: "GeckoView:ContentPermission",
+        });
+      }
     }
 
     return super.receiveMessage(aMessage);
diff --git a/mobile/shared/actors/GeckoViewPrompterChild.sys.mjs b/mobile/shared/actors/GeckoViewPrompterChild.sys.mjs
index bb8c4fbcff5..dd84f554cc1 100644
--- a/mobile/shared/actors/GeckoViewPrompterChild.sys.mjs
+++ b/mobile/shared/actors/GeckoViewPrompterChild.sys.mjs
@@ -11,16 +11,14 @@ export class GeckoViewPrompterChild extends GeckoViewActorChild {
   }
 
   dismissPrompt(prompt) {
-    this.eventDispatcher.sendRequest({
-      type: "GeckoView:Prompt:Dismiss",
+    this.sendAsyncMessage("GeckoView:Prompt:Dismiss", {
       id: prompt.id,
     });
     this.unregisterPrompt(prompt);
   }
 
   updatePrompt(message) {
-    this.eventDispatcher.sendRequest({
-      type: "GeckoView:Prompt:Update",
+    this.sendAsyncMessage("GeckoView:Prompt:Update", {
       prompt: message,
     });
   }
@@ -41,8 +39,7 @@ export class GeckoViewPrompterChild extends GeckoViewActorChild {
     // We intentionally do not await here as we want to fire NotifyPromptShow
     // immediately rather than waiting until the user accepts/dismisses the
     // prompt.
-    const result = this.eventDispatcher.sendRequestForResult({
-      type: "GeckoView:Prompt",
+    const result = this.sendQuery("GeckoView:Prompt", {
       prompt: message,
     });
     this.sendAsyncMessage("NotifyPromptShow", {
diff --git a/mobile/shared/actors/GeckoViewPrompterParent.sys.mjs b/mobile/shared/actors/GeckoViewPrompterParent.sys.mjs
index 24b763b114b..6f4d0f76f11 100644
--- a/mobile/shared/actors/GeckoViewPrompterParent.sys.mjs
+++ b/mobile/shared/actors/GeckoViewPrompterParent.sys.mjs
@@ -122,6 +122,26 @@ export class GeckoViewPrompterParent extends GeckoViewActorParent {
         this.rootActor.notifyPromptShow(data.id);
         break;
       }
+      case "GeckoView:Prompt:Dismiss": {
+        return this.eventDispatcher.sendRequest({
+          ...data,
+          type: "GeckoView:Prompt:Dismiss",
+        });
+        break;
+      }
+      case "GeckoView:Prompt:Update": {
+        this.eventDispatcher.sendRequest({
+          ...data,
+          type: "GeckoView:Prompt:Update",
+        });
+        break;
+      }
+      case "GeckoView:Prompt": {
+        return this.eventDispatcher.sendRequestForResult({
+          ...data,
+          type: "GeckoView:Prompt",
+        });
+      }
       default: {
         return super.receiveMessage({ name, data });
       }
diff --git a/mobile/shared/actors/LoadURIDelegateChild.sys.mjs b/mobile/shared/actors/LoadURIDelegateChild.sys.mjs
index 80ab0db0bc1..8a22300f297 100644
--- a/mobile/shared/actors/LoadURIDelegateChild.sys.mjs
+++ b/mobile/shared/actors/LoadURIDelegateChild.sys.mjs
@@ -17,13 +17,26 @@ export class LoadURIDelegateChild extends GeckoViewActorChild {
     debug`handleLoadError: uri=${aUri && aUri.spec}
                              displaySpec=${aUri && aUri.displaySpec}
                              error=${aError}`;
+    let errorClass = 0;
+    try {
+      const nssErrorsService = Cc[
+        "@mozilla.org/nss_errors_service;1"
+      ].getService(Ci.nsINSSErrorsService);
+      errorClass = nssErrorsService.getErrorClass(aError);
+    } catch (e) {}
+
+    const msg = {
+      uri: aUri && aUri.spec,
+      error: aError,
+      errorModule: aErrorModule,
+      errorClass,
+    };
+
+    let errorPagePromise = this.sendQuery("GeckoView:OnLoadError", msg);
 
     return lazy.LoadURIDelegate.handleLoadError(
       this.contentWindow,
-      this.eventDispatcher,
-      aUri,
-      aError,
-      aErrorModule
+      errorPagePromise
     );
   }
 }
diff --git a/mobile/shared/actors/LoadURIDelegateParent.sys.mjs b/mobile/shared/actors/LoadURIDelegateParent.sys.mjs
index 0446d7d88a4..ead98dc1130 100644
--- a/mobile/shared/actors/LoadURIDelegateParent.sys.mjs
+++ b/mobile/shared/actors/LoadURIDelegateParent.sys.mjs
@@ -4,5 +4,18 @@
 
 import { GeckoViewActorParent } from "resource://gre/modules/GeckoViewActorParent.sys.mjs";
 
-// For this.eventDispatcher in the child
-export class LoadURIDelegateParent extends GeckoViewActorParent {}
+export class LoadURIDelegateParent extends GeckoViewActorParent {
+  async receiveMessage({ name, data }) {
+    switch (name) {
+      case "GeckoView:OnLoadError": {
+        return this.eventDispatcher.sendRequestForResult({
+          ...data,
+          type: "GeckoView:OnLoadError",
+        });
+      }
+      default: {
+        return super.receiveMessage({ name, data });
+      }
+    }
+  }
+}
diff --git a/mobile/shared/actors/MediaControlDelegateChild.sys.mjs b/mobile/shared/actors/MediaControlDelegateChild.sys.mjs
index 1db32b33f05..0fa1daabd47 100644
--- a/mobile/shared/actors/MediaControlDelegateChild.sys.mjs
+++ b/mobile/shared/actors/MediaControlDelegateChild.sys.mjs
@@ -34,11 +34,13 @@ export class MediaControlDelegateChild extends GeckoViewActorChild {
       debug`No fullscreen media element found.`;
     }
 
-    const activated = await this.eventDispatcher.sendRequestForResult({
-      type: "GeckoView:MediaSession:Fullscreen",
-      metadata: lazy.MediaUtils.getMetadata(mediaElement) ?? {},
-      enabled: !!element,
-    });
+    const activated = await this.sendQuery(
+      "GeckoView:MediaSession:Fullscreen",
+      {
+        metadata: lazy.MediaUtils.getMetadata(mediaElement) ?? {},
+        enabled: !!element,
+      }
+    );
     if (activated) {
       return;
     }
diff --git a/mobile/shared/actors/MediaControlDelegateParent.sys.mjs b/mobile/shared/actors/MediaControlDelegateParent.sys.mjs
index f0c3f479846..ad19dcf9f23 100644
--- a/mobile/shared/actors/MediaControlDelegateParent.sys.mjs
+++ b/mobile/shared/actors/MediaControlDelegateParent.sys.mjs
@@ -4,5 +4,18 @@
 
 import { GeckoViewActorParent } from "resource://gre/modules/GeckoViewActorParent.sys.mjs";
 
-// For this.eventDispatcher in the child
-export class MediaControlDelegateParent extends GeckoViewActorParent {}
+export class MediaControlDelegateParent extends GeckoViewActorParent {
+  async receiveMessage({ name, data }) {
+    switch (name) {
+      case "GeckoView:MediaSession:Fullscreen": {
+        return this.eventDispatcher.sendRequestForResult({
+          ...data,
+          type: "GeckoView:MediaSession:Fullscreen",
+        });
+      }
+      default: {
+        return super.receiveMessage({ name, data });
... (file diff truncated)
Loading diff…