Bug 1215167 - Forbid unsafe browser CPOWs (disabled by default for now) (r=mrbkap)

This commit is contained in:
Bill McCloskey 2015-10-23 16:31:54 -07:00
parent 1ffca7ceab
commit 32f4a7d02e
12 changed files with 105 additions and 31 deletions

View File

@ -20,6 +20,7 @@ var Cu = Components.utils;
lifetime_test,
cancel_test,
cancel_test2,
unsafe_test,
];
function go() {
@ -337,3 +338,20 @@ function cancel_test2(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);
});
}

View File

@ -390,6 +390,33 @@
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) {
info("Running tests: " + type);
var node = document.getElementById('cpowbrowser_' + type);
@ -429,6 +456,8 @@
mm.addMessageListener("cpows:cancel_test", recvCancelTest);
mm.addMessageListener("cpows:cancel_sync_message", recvCancelSyncMessage);
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);
}

View File

@ -1894,28 +1894,6 @@ ContentParent::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
ContentParent::OnChannelConnected(int32_t pid)
{

View File

@ -325,8 +325,6 @@ public:
virtual void OnChannelError() override;
virtual void OnBeginSyncTransaction() override;
virtual PCrashReporterParent*
AllocPCrashReporterParent(const NativeThreadId& tid,
const uint32_t& processType) override;

View File

@ -848,9 +848,6 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
AssertWorkerThread();
mMonitor->AssertNotCurrentThreadOwns();
if (mCurrentTransaction == 0)
mListener->OnBeginSyncTransaction();
#ifdef OS_WIN
SyncStackFrame frame(this, false);
NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);

View File

@ -143,6 +143,7 @@ class MessageChannel : HasResultCodes
return !mCxxStackFrames.empty();
}
bool IsInTransaction() const { return mCurrentTransaction != 0; }
void CancelCurrentTransaction();
/**

View File

@ -88,10 +88,6 @@ class MessageListener
virtual void OnExitedCall() {
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,
const Message& child)
{

View File

@ -25,6 +25,8 @@ class JavaScriptChild : public JavaScriptBase<PJavaScriptChild>
void drop(JSObject* obj);
bool allowMessage(JSContext* cx) override { return true; }
protected:
virtual bool isParent() override { return false; }
virtual JSObject* scopeForTargetObjects() override;

View File

@ -48,6 +48,54 @@ JavaScriptParent::init()
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
JavaScriptParent::trace(JSTracer* trc)
{

View File

@ -25,6 +25,8 @@ class JavaScriptParent : public JavaScriptBase<PJavaScriptParent>
void drop(JSObject* obj);
bool allowMessage(JSContext* cx) override;
mozilla::ipc::IProtocol*
CloneProtocol(Channel* aChannel, ProtocolCloneContext* aCtx) override;

View File

@ -150,6 +150,9 @@ const CPOWProxyHandler CPOWProxyHandler::singleton;
JS_ReportError(cx, "cannot use a CPOW whose process is gone"); \
return false; \
} \
if (!owner->allowMessage(cx)) { \
return false; \
} \
{ \
CPOWTimer timer(cx); \
return owner->call args; \

View File

@ -74,6 +74,8 @@ class WrapperOwner : public virtual JavaScriptShared
bool active() { return !inactive_; }
virtual bool allowMessage(JSContext* cx) = 0;
void drop(JSObject* obj);
void updatePointer(JSObject* obj, const JSObject* old);