Bug 1215167 - Forbid unsafe browser CPOWs (disabled by default for now) (r=mrbkap)
This commit is contained in:
@@ -20,6 +20,7 @@ var Cu = Components.utils;
|
|||||||
lifetime_test,
|
lifetime_test,
|
||||||
cancel_test,
|
cancel_test,
|
||||||
cancel_test2,
|
cancel_test2,
|
||||||
|
unsafe_test,
|
||||||
];
|
];
|
||||||
|
|
||||||
function go() {
|
function go() {
|
||||||
@@ -337,3 +338,20 @@ function cancel_test2(finish)
|
|||||||
if (fin1 && fin2) finish();
|
if (fin1 && fin2) finish();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function unsafe_test(finish)
|
||||||
|
{
|
||||||
|
if (!is_remote) {
|
||||||
|
// Only run this test when running out-of-process.
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {}
|
||||||
|
|
||||||
|
sendAsyncMessage("cpows:unsafe", null, {f});
|
||||||
|
addMessageListener("cpows:unsafe_done", msg => {
|
||||||
|
sendRpcMessage("cpows:safe", null, {f});
|
||||||
|
addMessageListener("cpows:safe_done", finish);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -390,6 +390,33 @@
|
|||||||
msg.target.messageManager.sendAsyncMessage("cpows:cancel_test2_done");
|
msg.target.messageManager.sendAsyncMessage("cpows:cancel_test2_done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function recvUnsafe(msg) {
|
||||||
|
let failed = false;
|
||||||
|
|
||||||
|
const PREF_UNSAFE_FORBIDDEN = "dom.ipc.cpows.forbid-unsafe-from-browser";
|
||||||
|
opener.wrappedJSObject.SpecialPowers.setBoolPref(PREF_UNSAFE_FORBIDDEN, true);
|
||||||
|
try {
|
||||||
|
msg.objects.f();
|
||||||
|
} catch (e if /unsafe CPOW usage forbidden/.test(String(e))) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
opener.wrappedJSObject.SpecialPowers.clearUserPref(PREF_UNSAFE_FORBIDDEN);
|
||||||
|
ok(failed, "CPOW should fail when unsafe");
|
||||||
|
msg.target.messageManager.sendAsyncMessage("cpows:unsafe_done");
|
||||||
|
}
|
||||||
|
|
||||||
|
function recvSafe(msg) {
|
||||||
|
const PREF_UNSAFE_FORBIDDEN = "dom.ipc.cpows.forbid-unsafe-from-browser";
|
||||||
|
opener.wrappedJSObject.SpecialPowers.setBoolPref(PREF_UNSAFE_FORBIDDEN, true);
|
||||||
|
try {
|
||||||
|
msg.objects.f();
|
||||||
|
} catch (e if /unsafe CPOW usage forbidden/.test(String(e))) {
|
||||||
|
ok(false, "cpow failed");
|
||||||
|
}
|
||||||
|
opener.wrappedJSObject.SpecialPowers.clearUserPref(PREF_UNSAFE_FORBIDDEN);
|
||||||
|
msg.target.messageManager.sendAsyncMessage("cpows:safe_done");
|
||||||
|
}
|
||||||
|
|
||||||
function run_tests(type) {
|
function run_tests(type) {
|
||||||
info("Running tests: " + type);
|
info("Running tests: " + type);
|
||||||
var node = document.getElementById('cpowbrowser_' + type);
|
var node = document.getElementById('cpowbrowser_' + type);
|
||||||
@@ -429,6 +456,8 @@
|
|||||||
mm.addMessageListener("cpows:cancel_test", recvCancelTest);
|
mm.addMessageListener("cpows:cancel_test", recvCancelTest);
|
||||||
mm.addMessageListener("cpows:cancel_sync_message", recvCancelSyncMessage);
|
mm.addMessageListener("cpows:cancel_sync_message", recvCancelSyncMessage);
|
||||||
mm.addMessageListener("cpows:cancel_test2", recvCancelTest2);
|
mm.addMessageListener("cpows:cancel_test2", recvCancelTest2);
|
||||||
|
mm.addMessageListener("cpows:unsafe", recvUnsafe);
|
||||||
|
mm.addMessageListener("cpows:safe", recvSafe);
|
||||||
mm.loadFrameScript("chrome://mochitests/content/chrome/dom/base/test/chrome/cpows_child.js", true);
|
mm.loadFrameScript("chrome://mochitests/content/chrome/dom/base/test/chrome/cpows_child.js", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1910,28 +1910,6 @@ ContentParent::OnChannelError()
|
|||||||
PContentParent::OnChannelError();
|
PContentParent::OnChannelError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
ContentParent::OnBeginSyncTransaction() {
|
|
||||||
if (XRE_IsParentProcess()) {
|
|
||||||
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
|
|
||||||
JSContext *cx = nsContentUtils::GetCurrentJSContext();
|
|
||||||
if (!sDisableUnsafeCPOWWarnings) {
|
|
||||||
if (console && cx) {
|
|
||||||
nsAutoString filename;
|
|
||||||
uint32_t lineno = 0, column = 0;
|
|
||||||
nsJSUtils::GetCallingLocation(cx, filename, &lineno, &column);
|
|
||||||
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
|
|
||||||
error->Init(NS_LITERAL_STRING("unsafe CPOW usage"), filename,
|
|
||||||
EmptyString(), lineno, column,
|
|
||||||
nsIScriptError::warningFlag, "chrome javascript");
|
|
||||||
console->LogMessage(error);
|
|
||||||
} else {
|
|
||||||
NS_WARNING("Unsafe synchronous IPC message");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ContentParent::OnChannelConnected(int32_t pid)
|
ContentParent::OnChannelConnected(int32_t pid)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -327,8 +327,6 @@ public:
|
|||||||
|
|
||||||
virtual void OnChannelError() override;
|
virtual void OnChannelError() override;
|
||||||
|
|
||||||
virtual void OnBeginSyncTransaction() override;
|
|
||||||
|
|
||||||
virtual PCrashReporterParent*
|
virtual PCrashReporterParent*
|
||||||
AllocPCrashReporterParent(const NativeThreadId& tid,
|
AllocPCrashReporterParent(const NativeThreadId& tid,
|
||||||
const uint32_t& processType) override;
|
const uint32_t& processType) override;
|
||||||
|
|||||||
@@ -848,9 +848,6 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
|
|||||||
AssertWorkerThread();
|
AssertWorkerThread();
|
||||||
mMonitor->AssertNotCurrentThreadOwns();
|
mMonitor->AssertNotCurrentThreadOwns();
|
||||||
|
|
||||||
if (mCurrentTransaction == 0)
|
|
||||||
mListener->OnBeginSyncTransaction();
|
|
||||||
|
|
||||||
#ifdef OS_WIN
|
#ifdef OS_WIN
|
||||||
SyncStackFrame frame(this, false);
|
SyncStackFrame frame(this, false);
|
||||||
NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
|
NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ class MessageChannel : HasResultCodes
|
|||||||
return !mCxxStackFrames.empty();
|
return !mCxxStackFrames.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsInTransaction() const { return mCurrentTransaction != 0; }
|
||||||
void CancelCurrentTransaction();
|
void CancelCurrentTransaction();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -88,10 +88,6 @@ class MessageListener
|
|||||||
virtual void OnExitedCall() {
|
virtual void OnExitedCall() {
|
||||||
NS_RUNTIMEABORT("default impl shouldn't be invoked");
|
NS_RUNTIMEABORT("default impl shouldn't be invoked");
|
||||||
}
|
}
|
||||||
/* This callback is called when a sync message is sent that begins a new IPC transaction
|
|
||||||
(i.e., when it is not part of an existing sequence of nested messages). */
|
|
||||||
virtual void OnBeginSyncTransaction() {
|
|
||||||
}
|
|
||||||
virtual RacyInterruptPolicy MediateInterruptRace(const Message& parent,
|
virtual RacyInterruptPolicy MediateInterruptRace(const Message& parent,
|
||||||
const Message& child)
|
const Message& child)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ class JavaScriptChild : public JavaScriptBase<PJavaScriptChild>
|
|||||||
|
|
||||||
void drop(JSObject* obj);
|
void drop(JSObject* obj);
|
||||||
|
|
||||||
|
bool allowMessage(JSContext* cx) override { return true; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool isParent() override { return false; }
|
virtual bool isParent() override { return false; }
|
||||||
virtual JSObject* scopeForTargetObjects() override;
|
virtual JSObject* scopeForTargetObjects() override;
|
||||||
|
|||||||
@@ -48,6 +48,54 @@ JavaScriptParent::init()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ForbidUnsafeBrowserCPOWs()
|
||||||
|
{
|
||||||
|
static bool result;
|
||||||
|
static bool cached = false;
|
||||||
|
if (!cached) {
|
||||||
|
cached = true;
|
||||||
|
Preferences::AddBoolVarCache(&result, "dom.ipc.cpows.forbid-unsafe-from-browser", false);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
JavaScriptParent::allowMessage(JSContext* cx)
|
||||||
|
{
|
||||||
|
MessageChannel* channel = GetIPCChannel();
|
||||||
|
if (channel->IsInTransaction())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (ForbidUnsafeBrowserCPOWs()) {
|
||||||
|
if (JSObject* global = JS::CurrentGlobalOrNull(cx)) {
|
||||||
|
if (!JS::AddonIdOfObject(global)) {
|
||||||
|
JS_ReportError(cx, "unsafe CPOW usage forbidden");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool disableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS");
|
||||||
|
if (!disableUnsafeCPOWWarnings) {
|
||||||
|
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
|
||||||
|
if (console && cx) {
|
||||||
|
nsAutoString filename;
|
||||||
|
uint32_t lineno = 0, column = 0;
|
||||||
|
nsJSUtils::GetCallingLocation(cx, filename, &lineno, &column);
|
||||||
|
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
|
||||||
|
error->Init(NS_LITERAL_STRING("unsafe CPOW usage"), filename,
|
||||||
|
EmptyString(), lineno, column,
|
||||||
|
nsIScriptError::warningFlag, "chrome javascript");
|
||||||
|
console->LogMessage(error);
|
||||||
|
} else {
|
||||||
|
NS_WARNING("Unsafe synchronous IPC message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
JavaScriptParent::trace(JSTracer* trc)
|
JavaScriptParent::trace(JSTracer* trc)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ class JavaScriptParent : public JavaScriptBase<PJavaScriptParent>
|
|||||||
|
|
||||||
void drop(JSObject* obj);
|
void drop(JSObject* obj);
|
||||||
|
|
||||||
|
bool allowMessage(JSContext* cx) override;
|
||||||
|
|
||||||
mozilla::ipc::IProtocol*
|
mozilla::ipc::IProtocol*
|
||||||
CloneProtocol(Channel* aChannel, ProtocolCloneContext* aCtx) override;
|
CloneProtocol(Channel* aChannel, ProtocolCloneContext* aCtx) override;
|
||||||
|
|
||||||
|
|||||||
@@ -150,6 +150,9 @@ const CPOWProxyHandler CPOWProxyHandler::singleton;
|
|||||||
JS_ReportError(cx, "cannot use a CPOW whose process is gone"); \
|
JS_ReportError(cx, "cannot use a CPOW whose process is gone"); \
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
|
if (!owner->allowMessage(cx)) { \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
{ \
|
{ \
|
||||||
CPOWTimer timer(cx); \
|
CPOWTimer timer(cx); \
|
||||||
return owner->call args; \
|
return owner->call args; \
|
||||||
@@ -978,8 +981,9 @@ InstanceOf(JSObject* proxy, const nsID* id, bool* bp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
DOMInstanceOf(JSContext* cx, JSObject* proxy, int prototypeID, int depth, bool* bp)
|
DOMInstanceOf(JSContext* cx, JSObject* proxyArg, int prototypeID, int depth, bool* bp)
|
||||||
{
|
{
|
||||||
|
RootedObject proxy(cx, proxyArg);
|
||||||
FORWARD(domInstanceOf, (cx, proxy, prototypeID, depth, bp));
|
FORWARD(domInstanceOf, (cx, proxy, prototypeID, depth, bp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ class WrapperOwner : public virtual JavaScriptShared
|
|||||||
|
|
||||||
bool active() { return !inactive_; }
|
bool active() { return !inactive_; }
|
||||||
|
|
||||||
|
virtual bool allowMessage(JSContext* cx) = 0;
|
||||||
|
|
||||||
void drop(JSObject* obj);
|
void drop(JSObject* obj);
|
||||||
void updatePointer(JSObject* obj, const JSObject* old);
|
void updatePointer(JSObject* obj, const JSObject* old);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user