Skip to content
This repository has been archived by the owner on Aug 4, 2022. It is now read-only.

Commit

Permalink
Bug 1072980 - Don't allow CPOWs to be passed to C++ code (r=mrbkap,al…
Browse files Browse the repository at this point in the history
…ly,mconley)
  • Loading branch information
bill-mccloskey committed Jan 29, 2015
1 parent da1fdb9 commit 9c86ff6
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 51 deletions.
3 changes: 2 additions & 1 deletion browser/base/content/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -3760,13 +3760,14 @@ function FillHistoryMenu(aParent) {
let item = document.createElement("menuitem");
let entry = sessionHistory.getEntryAtIndex(j, false);
let uri = entry.URI.spec;
let uriCopy = BrowserUtils.makeURI(uri);

item.setAttribute("uri", uri);
item.setAttribute("label", entry.title || uri);
item.setAttribute("index", j);

if (j != index) {
PlacesUtils.favicons.getFaviconURLForPage(entry.URI, function (aURI) {
PlacesUtils.favicons.getFaviconURLForPage(uriCopy, function (aURI) {
if (aURI) {
let iconURL = PlacesUtils.favicons.getFaviconLinkForIcon(aURI).spec;
iconURL = PlacesUtils.getImageURLForResolution(window, iconURL);
Expand Down
5 changes: 4 additions & 1 deletion browser/base/content/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,10 @@ let handleContentContextMenu = function (event) {
}

let customMenuItems = PageMenuChild.build(event.target);
sendSyncMessage("contextmenu", { editFlags, spellInfo, customMenuItems, addonInfo }, { event, popupNode: event.target });
let principal = event.target.ownerDocument.nodePrincipal;
sendSyncMessage("contextmenu",
{ editFlags, spellInfo, customMenuItems, addonInfo, principal },
{ event, popupNode: event.target });
}
else {
// Break out to the parent window and pass the add-on info along
Expand Down
33 changes: 10 additions & 23 deletions browser/base/content/nsContextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -596,13 +596,15 @@ nsContextMenu.prototype = {
// gContextMenuContentData instead.
if (this.isRemote) {
this.browser = gContextMenuContentData.browser;
this.principal = gContextMenuContentData.principal;
} else {
editFlags = SpellCheckHelper.isEditable(this.target, window);
this.browser = this.target.ownerDocument.defaultView
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler;
this.principal = this.target.ownerDocument.nodePrincipal;
}
this.onSocial = !!this.browser.getAttribute("origin");

Expand Down Expand Up @@ -834,18 +836,6 @@ nsContextMenu.prototype = {
this.linkProtocol == "snews" );
},

_unremotePrincipal: function(aRemotePrincipal) {
if (this.isRemote) {
return Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager)
.getAppCodebasePrincipal(aRemotePrincipal.URI,
aRemotePrincipal.appId,
aRemotePrincipal.isInBrowserElement);
}

return aRemotePrincipal;
},

_isSpellCheckEnabled: function(aNode) {
// We can always force-enable spellchecking on textboxes
if (this.isTargetATextBox(aNode)) {
Expand Down Expand Up @@ -875,22 +865,22 @@ nsContextMenu.prototype = {
// Open linked-to URL in a new window.
openLink : function () {
var doc = this.target.ownerDocument;
urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal));
urlSecurityCheck(this.linkURL, this.principal);
openLinkIn(this.linkURL, "window", this._openLinkInParameters(doc));
},

// Open linked-to URL in a new private window.
openLinkInPrivateWindow : function () {
var doc = this.target.ownerDocument;
urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal));
urlSecurityCheck(this.linkURL, this.principal);
openLinkIn(this.linkURL, "window",
this._openLinkInParameters(doc, { private: true }));
},

// Open linked-to URL in a new tab.
openLinkInTab: function() {
var doc = this.target.ownerDocument;
urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal));
urlSecurityCheck(this.linkURL, this.principal);
var referrerURI = doc.documentURIObject;

// if the mixedContentChannel is present and the referring URI passes
Expand All @@ -917,7 +907,7 @@ nsContextMenu.prototype = {
// open URL in current tab
openLinkInCurrent: function() {
var doc = this.target.ownerDocument;
urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal));
urlSecurityCheck(this.linkURL, this.principal);
openLinkIn(this.linkURL, "current", this._openLinkInParameters(doc));
},

Expand Down Expand Up @@ -1125,8 +1115,7 @@ nsContextMenu.prototype = {
return;

var doc = this.target.ownerDocument;
urlSecurityCheck(this.target.currentURI.spec,
this._unremotePrincipal(doc.nodePrincipal));
urlSecurityCheck(this.target.currentURI.spec, this.principal);

// Confirm since it's annoying if you hit this accidentally.
const kDesktopBackgroundURL =
Expand Down Expand Up @@ -1303,7 +1292,7 @@ nsContextMenu.prototype = {
linkText = this.focusedWindow.getSelection().toString().trim();
else
linkText = this.linkText();
urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal));
urlSecurityCheck(this.linkURL, this.principal);

this.saveHelper(this.linkURL, linkText, null, true, doc);
},
Expand All @@ -1323,14 +1312,12 @@ nsContextMenu.prototype = {
true, false, doc.documentURIObject, doc);
}
else if (this.onImage) {
urlSecurityCheck(this.mediaURL,
this._unremotePrincipal(doc.nodePrincipal));
urlSecurityCheck(this.mediaURL, this.principal);
saveImageURL(this.mediaURL, null, "SaveImageTitle", false,
false, doc.documentURIObject, doc);
}
else if (this.onVideo || this.onAudio) {
urlSecurityCheck(this.mediaURL,
this._unremotePrincipal(doc.nodePrincipal));
urlSecurityCheck(this.mediaURL, this.principal);
var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle";
this.saveHelper(this.mediaURL, null, dialogTitle, false, doc);
}
Expand Down
1 change: 1 addition & 0 deletions browser/base/content/tabbrowser.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3171,6 +3171,7 @@
browser: browser,
editFlags: aMessage.data.editFlags,
spellInfo: spellInfo,
principal: aMessage.data.principal,
customMenuItems: aMessage.data.customMenuItems,
addonInfo: aMessage.data.addonInfo };
let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
Expand Down
6 changes: 3 additions & 3 deletions browser/components/sessionstore/test/browser_500328.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ function checkState(tab) {
// deserialized in the content scope. And in this case, since RegExps are
// not currently Xrayable (see bug 1014991), trying to pull |obj3| (a RegExp)
// off of an Xrayed Object won't work. So we need to waive.
runInContent(tab.linkedBrowser, function(win, event) {
return Cu.waiveXrays(event.state).obj3.toString();
}, aEvent).then(function(stateStr) {
runInContent(tab.linkedBrowser, function(win, state) {
return Cu.waiveXrays(state).obj3.toString();
}, aEvent.state).then(function(stateStr) {
is(stateStr, '/^a$/', "second popstate object.");

// Make sure that the new-elem node is present in the document. If it's
Expand Down
18 changes: 18 additions & 0 deletions dom/base/test/chrome/cpows_parent.xul
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,24 @@
let savedElement = null;
function recvDomTest(message) {
savedElement = message.objects.element;
// Test to ensure that we don't pass CPOWs to C++-implemented interfaces.
// See bug 1072980.
if (test_state == "remote") {
let walker = Components.classes["@mozilla.org/inspector/deep-tree-walker;1"]
.createInstance(Components.interfaces.inIDeepTreeWalker);
const SHOW_ELEMENT = Components.interfaces.nsIDOMNodeFilter.SHOW_ELEMENT;
walker.showAnonymousContent = true;
walker.showSubDocuments = false;
try {
walker.init(savedElement, SHOW_ELEMENT);
ok(false, "expected exception passing CPOW to C++");
} catch (e) {
is(e.result, Components.results.NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE,
"got exception when passing CPOW to C++");
}
}
}
function recvDomTestAfterGC(message) {
Expand Down
16 changes: 16 additions & 0 deletions js/xpconnect/src/XPCWrappedNative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/* Wrapper object for reflecting native xpcom objects into JavaScript. */

#include "xpcprivate.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "nsWrapperCacheInlines.h"
#include "XPCLog.h"
#include "jsprf.h"
Expand Down Expand Up @@ -2171,6 +2172,21 @@ CallMethodHelper::ConvertIndependentParam(uint8_t i)
return false;
}

// Don't allow CPOWs to be passed to native code (in case they try to cast
// to a concrete type).
if (src.isObject() &&
jsipc::IsWrappedCPOW(&src.toObject()) &&
type_tag == nsXPTType::T_INTERFACE &&
!param_iid.Equals(NS_GET_IID(nsISupports)))
{
// Allow passing CPOWs to XPCWrappedJS.
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(mCallee));
if (!wrappedJS) {
ThrowBadParam(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE, i, mCallContext);
return false;
}
}

nsresult err;
if (!XPCConvert::JSData2Native(&dp->val, src, type, &param_iid, &err)) {
ThrowBadParam(err, i, mCallContext);
Expand Down
4 changes: 2 additions & 2 deletions testing/specialpowers/content/specialpowersAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -1601,10 +1601,10 @@ SpecialPowersAPI.prototype = {

var xferable = Components.classes["@mozilla.org/widget/transferable;1"].
createInstance(Components.interfaces.nsITransferable);
// in e10s b-c tests |content.window| is null whereas |window| works fine.
// in e10s b-c tests |content.window| is a CPOW whereas |window| works fine.
// for some non-e10s mochi tests, |window| is null whereas |content.window|
// works fine. So we take whatever is non-null!
xferable.init(this._getDocShell(content.window || window)
xferable.init(this._getDocShell(typeof(window) == "undefined" ? content.window : window)
.QueryInterface(Components.interfaces.nsILoadContext));
xferable.addDataFlavor(flavor);
this._cb.getData(xferable, whichClipboard);
Expand Down
43 changes: 35 additions & 8 deletions toolkit/components/addoncompat/RemoteAddonsParent.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,22 @@ let AboutProtocolParent = {
// We immediately read all the data out of the channel here and
// return it to the child.
openChannel: function(msg) {
function wrapGetInterface(cpow) {
return {
getInterface: function(intf) { return cpow.getInterface(intf); }
};
}

let uri = BrowserUtils.makeURI(msg.data.uri);
let contractID = msg.data.contractID;
let module = Cc[contractID].getService(Ci.nsIAboutModule);
try {
let channel = module.newChannel(uri, null);
channel.notificationCallbacks = msg.objects.notificationCallbacks;
// We're not allowed to set channel.notificationCallbacks to a
// CPOW, since the setter for notificationCallbacks is in C++,
// which can't tolerate CPOWs. Instead we just use a JS object
// that wraps the CPOW.
channel.notificationCallbacks = wrapGetInterface(msg.objects.notificationCallbacks);
if (msg.objects.loadGroupNotificationCallbacks) {
channel.loadGroup = {notificationCallbacks: msg.objects.loadGroupNotificationCallbacks};
} else {
Expand Down Expand Up @@ -434,13 +444,17 @@ let EventTargetParent = {
// If there's already an identical listener, don't do anything.
for (let i = 0; i < forType.length; i++) {
if (forType[i].listener === listener &&
forType[i].target === target &&
forType[i].useCapture === useCapture &&
forType[i].wantsUntrusted === wantsUntrusted) {
return;
}
}

forType.push({listener: listener, wantsUntrusted: wantsUntrusted, useCapture: useCapture});
forType.push({listener: listener,
target: target,
wantsUntrusted: wantsUntrusted,
useCapture: useCapture});
},

removeEventListener: function(addon, target, type, listener, useCapture) {
Expand All @@ -458,7 +472,9 @@ let EventTargetParent = {
let forType = setDefault(listeners, type, []);

for (let i = 0; i < forType.length; i++) {
if (forType[i].listener === listener && forType[i].useCapture === useCapture) {
if (forType[i].listener === listener &&
forType[i].target === target &&
forType[i].useCapture === useCapture) {
forType.splice(i, 1);
NotificationTracker.remove(["event", type, useCapture, addon]);
break;
Expand Down Expand Up @@ -488,19 +504,30 @@ let EventTargetParent = {

// Make a copy in case they call removeEventListener in the listener.
let handlers = [];
for (let {listener, wantsUntrusted, useCapture} of forType) {
for (let {listener, target, wantsUntrusted, useCapture} of forType) {
if ((wantsUntrusted || isTrusted) && useCapture == capturing) {
handlers.push(listener);
handlers.push([listener, target]);
}
}

for (let handler of handlers) {
for (let [handler, target] of handlers) {
let EventProxy = {
get: function(actualEvent, name) {
if (name == "currentTarget") {
return target;
} else {
return actualEvent[name];
}
}
};
let proxyEvent = new Proxy(event, EventProxy);

try {
Prefetcher.withPrefetching(prefetched, cpows, () => {
if ("handleEvent" in handler) {
handler.handleEvent(event);
handler.handleEvent(proxyEvent);
} else {
handler.call(event.target, event);
handler.call(event.target, proxyEvent);
}
});
} catch (e) {
Expand Down
Loading

0 comments on commit 9c86ff6

Please sign in to comment.