Firefox · Toolkit
CVE-2026-92015
Logic Error in Toolkit
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
iftoolkit/components/extensions/ExtensionParent.sys.mjs |
modified | |
constructortoolkit/components/extensions/ExtensionParent.sys.mjs |
modified | |
openNativetoolkit/components/extensions/ExtensionParent.sys.mjs |
modified | |
trackNativeAppPorttoolkit/components/extensions/ExtensionParent.sys.mjs |
modified | |
untrackNativeAppPorttoolkit/components/extensions/ExtensionParent.sys.mjs |
modified | |
ProxyContextParenttoolkit/components/extensions/ExtensionParent.sys.mjs |
modified | |
ExtensionPageContextParenttoolkit/components/extensions/ExtensionParent.sys.mjs |
modified | |
BackgroundWorkerContextParenttoolkit/components/extensions/ExtensionParent.sys.mjs |
modified | |
recvContextLoadedtoolkit/components/extensions/ExtensionParent.sys.mjs |
modified | |
getContextByIdtoolkit/components/extensions/ExtensionParent.sys.mjs |
modified | |
getContexttoolkit/components/extensions/ExtensionParent.sys.mjs |
modified | |
getContextUncheckedtoolkit/components/extensions/ExtensionParent.sys.mjs |
modified |
Files Changed
toolkit/components/extensions/ExtensionParent.sys.mjs
Patch
diff --git a/toolkit/components/extensions/ExtensionParent.sys.mjs b/toolkit/components/extensions/ExtensionParent.sys.mjs
index 941a5353917..ddc2bc53b4b 100644
--- a/toolkit/components/extensions/ExtensionParent.sys.mjs
+++ b/toolkit/components/extensions/ExtensionParent.sys.mjs
@@ -62,19 +62,6 @@ schemaURLs.add("chrome://extensions/content/schemas/experiments.json");
let GlobalManager;
let ParentAPIManager;
-function verifyActorForContext(actor, context) {
- if (JSWindowActorParent.isInstance(actor)) {
- let target = actor.browsingContext.top.embedderElement;
- if (context.parentMessageManager !== target.messageManager) {
- throw new Error("Got message on unexpected message manager");
- }
- } else if (JSProcessActorParent.isInstance(actor)) {
- if (actor.manager.remoteType !== context.extension.remoteType) {
- throw new Error("Got message from unexpected process");
- }
- }
-}
-
// This object loads the ext-*.js scripts that define the extension API.
let apiManager = new (class extends SchemaAPIManager {
constructor() {
@@ -267,7 +254,7 @@ const ProxyMessenger = {
},
openNative(nativeApp, sender) {
- let context = ParentAPIManager.getContextById(sender.childId);
+ let context = ParentAPIManager.getContext(sender.childId, sender.actor);
if (context.extension.hasPermission("geckoViewAddons")) {
return new lazy.GeckoViewConnection(
this.getSender(context.extension, sender),
@@ -477,30 +464,17 @@ const ProxyMessenger = {
},
trackNativeAppPort(port) {
- if (!port?.native) {
- return;
- }
-
- try {
- let context = ParentAPIManager.getContextById(port.senderChildId);
+ if (port?.native) {
+ // The childId was verified against its actor in openNative().
+ let context = ParentAPIManager.getContextUnchecked(port.senderChildId);
context?.trackNativeAppPort(port);
- } catch {
- // getContextById will throw if the context has been destroyed
- // in the meantime.
}
},
untrackNativeAppPort(port) {
- if (!port?.native) {
- return;
- }
-
- try {
- let context = ParentAPIManager.getContextById(port.senderChildId);
+ if (port?.native) {
+ let context = ParentAPIManager.getContextUnchecked(port.senderChildId);
context?.untrackNativeAppPort(port);
- } catch {
- // getContextById will throw if the context has been destroyed
- // in the meantime.
}
},
};
@@ -590,19 +564,21 @@ GlobalManager = {
* parent side of a proxied API.
*/
class ProxyContextParent extends BaseContext {
- constructor(envType, extension, params, browsingContext, principal) {
+ constructor(envType, extension, params, actor, principal) {
super(envType, extension);
this.childId = params.childId;
this.uri = Services.io.newURI(params.url);
- this.browsingContext = browsingContext;
+ // The actor is tied to the WindowGlobal or DOMProcess.
+ this.actor = actor;
+ this.browsingContext = actor.browsingContext ?? null;
this.incognito = params.incognito;
this.listenerPromises = new Set();
// browsingContext is null when subclassed by BackgroundWorkerContextParent.
- const xulBrowser = browsingContext?.top.embedderElement;
+ const xulBrowser = this.browsingContext?.top.embedderElement;
// This message manager is used by ParentAPIManager to send messages and to
// close the ProxyContext if the underlying message manager closes. This
// message manager object may change when `xulBrowser` swaps docshells, e.g.
@@ -834,11 +810,11 @@ class ContentScriptContextParent extends ProxyContextParent {}
* ExtensionChild.sys.mjs.
*/
class ExtensionPageContextParent extends ProxyContextParent {
- constructor(envType, extension, params, browsingContext) {
- super(envType, extension, params, browsingContext, extension.principal);
+ constructor(envType, extension, params, actor) {
+ super(envType, extension, params, actor, extension.principal);
this.viewType = params.viewType;
- this.isTopContext = browsingContext.top === browsingContext;
+ this.isTopContext = this.browsingContext.top === this.browsingContext;
this.extension.views.add(this);
@@ -1042,11 +1018,12 @@ class DevToolsExtensionPageContextParent extends ExtensionPageContextParent {
* worker script.
*/
class BackgroundWorkerContextParent extends ProxyContextParent {
- constructor(envType, extension, params) {
+ constructor(envType, extension, params, actor) {
// TODO: split out from ProxyContextParent a base class that
// doesn't expect a browsingContext and one for contexts that are
// expected to have a browsingContext associated.
- super(envType, extension, params, null, extension.principal);
+ // ProxyContextParent still reads actor.browsingContext.
+ super(envType, extension, params, actor, extension.principal);
this.viewType = params.viewType;
this.workerDescriptorId = params.workerDescriptorId;
@@ -1161,7 +1138,7 @@ ParentAPIManager = {
envType,
extension,
data,
- actor.browsingContext
+ actor
);
// TODO: make WindowContext a first-class member of BaseContext and
// use it here. For now, just set innerWindowID (already declared on
@@ -1173,7 +1150,7 @@ ParentAPIManager = {
envType,
extension,
data,
- actor.browsingContext
+ actor
);
}
} else if (JSProcessActorParent.isInstance(actor)) {
@@ -1193,7 +1170,12 @@ ParentAPIManager = {
`Unexpected viewType ${data.viewType} on an extension process actor`
);
}
- context = new BackgroundWorkerContextParent(envType, extension, data);
+ context = new BackgroundWorkerContextParent(
+ envType,
+ extension,
+ data,
+ actor
+ );
} else {
// Unreacheable: JSWindowActorParent and JSProcessActorParent are the
// only actors.
@@ -1207,7 +1189,7 @@ ParentAPIManager = {
envType,
extension,
data,
- actor.browsingContext,
+ actor,
principal
);
} else {
@@ -1217,8 +1199,7 @@ ParentAPIManager = {
},
recvContextLoaded(data, { actor }) {
- let context = this.getContextById(data.childId);
- verifyActorForContext(actor, context);
+ let context = this.getContext(data.childId, actor);
const { extension } = context;
extension.emit("extension-proxy-context-load:completed", context);
},
@@ -1273,11 +1254,9 @@ ParentAPIManager = {
},
async recvAPICall(data, { actor }) {
- let context = this.getContextById(data.childId);
+ let context = this.getContext(data.childId, actor);
let target = actor.browsingContext?.top.embedderElement;
- verifyActorForContext(actor, context);
-
let reply = result => {
if (target && !context.parentMessageManager) {
Services.console.logStringMessage(
@@ -1352,9 +1331,7 @@ ParentAPIManager = {
},
async recvAddListener(data, { actor }) {
- let context = this.getContextById(data.childId);
-
- verifyActorForContext(actor, context);
+ let context = this.getContext(data.childId, actor);
let { childId, alreadyLogged = false } = data;
let handlingUserInput = false;
@@ -1432,8 +1409,8 @@ ParentAPIManager = {
}
},
- async recvRemoveListener(data) {
- let context = this.getContextById(data.childId);
+ async recvRemoveListener(data, { actor }) {
+ let context = this.getContext(data.childId, actor);
let listener = context.listenerProxies.get(data.listenerId);
let handler = await context.apiCan.asyncFindAPIPath(data.path);
@@ -1451,13 +1428,18 @@ ParentAPIManager = {
}
},
- getContextById(childId) {
+ getContext(childId, actor) {
let context = this.proxyContexts.get(childId);
- if (!context) {
+ if (!context || context.actor !== actor) {
throw new Error("WebExtension context not found!");
}
return context;
},
+
+ // Only use when checking the actor is not needed.
+ getContextUnchecked(childId) {
+ return this.proxyContexts.get(childId);
+ },
};
ParentAPIManager.init();
@@ -2140,7 +2122,10 @@ async function promiseBackgroundViewLoaded(browser) {
}
if (childId) {
- return ParentAPIManager.getContextById(childId);
+ let context = ParentAPIManager.getContextUnchecked(childId);
+ if (context && context.xulBrowser === browser) {
+ return context;
+ }
}
}
Loading diff…
References
On This Page