Bug 1176034 - MessagePort should force a close() if the structured clone algorithm fails, r=bent

This commit is contained in:
Andrea Marchesini 2015-06-23 15:50:00 -07:00
parent b0a98f661f
commit 0502aa5138
14 changed files with 213 additions and 4 deletions

View File

@ -238,7 +238,13 @@ PostMessageEvent::FreeTransferStructuredClone(uint32_t aTag,
uint64_t aExtraData, uint64_t aExtraData,
void* aClosure) void* aClosure)
{ {
// Nothing to do. if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
MOZ_ASSERT(aClosure);
MOZ_ASSERT(!aContent);
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
MessagePort::ForceClose(scInfo->event->GetPortIdentifier(aExtraData));
}
} }
PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource, PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,

View File

@ -21,6 +21,7 @@
#include "mozilla/dom/WorkerScope.h" #include "mozilla/dom/WorkerScope.h"
#include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h" #include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/unused.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsGlobalWindow.h" #include "nsGlobalWindow.h"
#include "nsPresContext.h" #include "nsPresContext.h"
@ -249,6 +250,50 @@ private:
} }
}; };
class ForceCloseHelper final : public nsIIPCBackgroundChildCreateCallback
{
public:
NS_DECL_ISUPPORTS
static void ForceClose(const MessagePortIdentifier& aIdentifier)
{
PBackgroundChild* actor =
mozilla::ipc::BackgroundChild::GetForCurrentThread();
if (actor) {
unused << actor->SendMessagePortForceClose(aIdentifier.uuid(),
aIdentifier.destinationUuid(),
aIdentifier.sequenceId());
return;
}
nsRefPtr<ForceCloseHelper> helper = new ForceCloseHelper(aIdentifier);
if (NS_WARN_IF(!mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(helper))) {
MOZ_CRASH();
}
}
private:
explicit ForceCloseHelper(const MessagePortIdentifier& aIdentifier)
: mIdentifier(aIdentifier)
{}
~ForceCloseHelper() {}
void ActorFailed() override
{
MOZ_CRASH("Failed to create a PBackgroundChild actor!");
}
void ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
{
ForceClose(mIdentifier);
}
const MessagePortIdentifier mIdentifier;
};
NS_IMPL_ISUPPORTS(ForceCloseHelper, nsIIPCBackgroundChildCreateCallback)
} // anonymous namespace } // anonymous namespace
MessagePort::MessagePort(nsPIDOMWindow* aWindow) MessagePort::MessagePort(nsPIDOMWindow* aWindow)
@ -863,5 +908,11 @@ MessagePort::RemoveDocFromBFCache()
bfCacheEntry->RemoveFromBFCacheSync(); bfCacheEntry->RemoveFromBFCacheSync();
} }
/* static */ void
MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier)
{
ForceCloseHelper::ForceClose(aIdentifier);
}
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla

View File

@ -86,6 +86,9 @@ public:
Create(nsPIDOMWindow* aWindow, const MessagePortIdentifier& aIdentifier, Create(nsPIDOMWindow* aWindow, const MessagePortIdentifier& aIdentifier,
ErrorResult& aRv); ErrorResult& aRv);
static void
ForceClose(const MessagePortIdentifier& aIdentifier);
virtual JSObject* virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;

View File

@ -159,5 +159,19 @@ MessagePortParent::Close()
mEntangled = false; mEntangled = false;
} }
/* static */ bool
MessagePortParent::ForceClose(const nsID& aUUID,
const nsID& aDestinationUUID,
const uint32_t& aSequenceID)
{
MessagePortService* service = MessagePortService::Get();
if (!service) {
NS_WARNING("The service must exist if we want to close an existing MessagePort.");
return false;
}
return service->ForceClose(aUUID, aDestinationUUID, aSequenceID);
}
} // dom namespace } // dom namespace
} // mozilla namespace } // mozilla namespace

View File

@ -36,6 +36,10 @@ public:
return mUUID; return mUUID;
} }
static bool ForceClose(const nsID& aUUID,
const nsID& aDestinationUUID,
const uint32_t& aSequenceID);
private: private:
virtual bool RecvPostMessages(nsTArray<MessagePortMessage>&& aMessages) virtual bool RecvPostMessages(nsTArray<MessagePortMessage>&& aMessages)
override; override;

View File

@ -6,16 +6,27 @@
#include "MessagePortService.h" #include "MessagePortService.h"
#include "MessagePortParent.h" #include "MessagePortParent.h"
#include "SharedMessagePortMessage.h" #include "SharedMessagePortMessage.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/StaticPtr.h" #include "mozilla/StaticPtr.h"
#include "mozilla/unused.h" #include "mozilla/unused.h"
#include "nsDataHashtable.h" #include "nsDataHashtable.h"
#include "nsTArray.h" #include "nsTArray.h"
using mozilla::ipc::AssertIsOnBackgroundThread;
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
namespace { namespace {
StaticRefPtr<MessagePortService> gInstance; StaticRefPtr<MessagePortService> gInstance;
void
AssertIsInMainProcess()
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
}
} // anonymous namespace } // anonymous namespace
class MessagePortService::MessagePortServiceData final class MessagePortService::MessagePortServiceData final
@ -53,9 +64,21 @@ public:
FallibleTArray<nsRefPtr<SharedMessagePortMessage>> mMessages; FallibleTArray<nsRefPtr<SharedMessagePortMessage>> mMessages;
}; };
/* static */ MessagePortService*
MessagePortService::Get()
{
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
return gInstance;
}
/* static */ MessagePortService* /* static */ MessagePortService*
MessagePortService::GetOrCreate() MessagePortService::GetOrCreate()
{ {
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
if (!gInstance) { if (!gInstance) {
gInstance = new MessagePortService(); gInstance = new MessagePortService();
} }
@ -248,6 +271,12 @@ MessagePortService::CloseAll(const nsID& aUUID)
CloseAll(destinationUUID); CloseAll(destinationUUID);
// CloseAll calls itself recursively and it can happen that it deletes
// itself. Before continuing we must check if we are still alive.
if (!gInstance) {
return;
}
#ifdef DEBUG #ifdef DEBUG
mPorts.EnumerateRead(CloseAllDebugCheck, const_cast<nsID*>(&aUUID)); mPorts.EnumerateRead(CloseAllDebugCheck, const_cast<nsID*>(&aUUID));
#endif #endif
@ -324,5 +353,26 @@ MessagePortService::ParentDestroy(MessagePortParent* aParent)
CloseAll(aParent->ID()); CloseAll(aParent->ID());
} }
bool
MessagePortService::ForceClose(const nsID& aUUID,
const nsID& aDestinationUUID,
const uint32_t& aSequenceID)
{
MessagePortServiceData* data;
if (!mPorts.Get(aUUID, &data)) {
NS_WARNING("Unknown MessagePort in ForceClose()");
return true;
}
if (!data->mDestinationUUID.Equals(aDestinationUUID) ||
data->mSequenceID != aSequenceID) {
NS_WARNING("DestinationUUID and/or sequenceID do not match.");
return false;
}
CloseAll(aUUID);
return true;
}
} // dom namespace } // dom namespace
} // mozilla namespace } // mozilla namespace

View File

@ -20,6 +20,7 @@ class MessagePortService final
public: public:
NS_INLINE_DECL_REFCOUNTING(MessagePortService) NS_INLINE_DECL_REFCOUNTING(MessagePortService)
static MessagePortService* Get();
static MessagePortService* GetOrCreate(); static MessagePortService* GetOrCreate();
bool RequestEntangling(MessagePortParent* aParent, bool RequestEntangling(MessagePortParent* aParent,
@ -38,6 +39,10 @@ public:
void ParentDestroy(MessagePortParent* aParent); void ParentDestroy(MessagePortParent* aParent);
bool ForceClose(const nsID& aUUID,
const nsID& aDestinationUUID,
const uint32_t& aSequenceID);
private: private:
~MessagePortService() {} ~MessagePortService() {}

View File

@ -141,7 +141,7 @@ ReadTransfer(JSContext* aCx, JSStructuredCloneReader* aReader,
ErrorResult rv; ErrorResult rv;
nsRefPtr<MessagePort> port = nsRefPtr<MessagePort> port =
MessagePort::Create(closure->mWindow, MessagePort::Create(closure->mWindow,
closure->mClosure.mMessagePortIdentifiers[(uint32_t)aExtraData], closure->mClosure.mMessagePortIdentifiers[aExtraData],
rv); rv);
if (NS_WARN_IF(rv.Failed())) { if (NS_WARN_IF(rv.Failed())) {
return false; return false;
@ -195,13 +195,27 @@ WriteTransfer(JSContext* aCx, JS::Handle<JSObject*> aObj, void* aClosure,
return true; return true;
} }
void
FreeTransfer(uint32_t aTag, JS::TransferableOwnership aOwnership,
void* aContent, uint64_t aExtraData, void* aClosure)
{
MOZ_ASSERT(aClosure);
auto* closure = static_cast<StructuredCloneClosureInternal*>(aClosure);
if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
MOZ_ASSERT(!aContent);
MOZ_ASSERT(aExtraData < closure->mClosure.mMessagePortIdentifiers.Length());
MessagePort::ForceClose(closure->mClosure.mMessagePortIdentifiers[(uint32_t)aExtraData]);
}
}
const JSStructuredCloneCallbacks gCallbacks = { const JSStructuredCloneCallbacks gCallbacks = {
Read, Read,
Write, Write,
Error, Error,
ReadTransfer, ReadTransfer,
WriteTransfer, WriteTransfer,
nullptr FreeTransfer,
}; };
} // anonymous namespace } // anonymous namespace

View File

@ -23,3 +23,4 @@ support-files =
[test_messageChannel_sharedWorker.html] [test_messageChannel_sharedWorker.html]
[test_messageChannel_sharedWorker2.html] [test_messageChannel_sharedWorker2.html]
[test_messageChannel_any.html] [test_messageChannel_any.html]
[test_messageChannel_forceClose.html]

View File

@ -0,0 +1,36 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1176034
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1176034 - start/close</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1176034">Mozilla Bug 1176034</a>
<div id="content"></div>
<pre id="test">
</pre>
<script type="application/javascript">
function runTests() {
var mc = new MessageChannel();
try {
postMessage(42, "*", [ mc.port1, window ]);
ok(false, "Something went wrong.");
} catch(e) {
ok(true, "PostMessage should fail and we should not leak.");
}
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTests);
</script>
</body>
</html>

View File

@ -763,7 +763,14 @@ struct WorkerStructuredCloneCallbacks
FreeTransfer(uint32_t aTag, JS::TransferableOwnership aOwnership, FreeTransfer(uint32_t aTag, JS::TransferableOwnership aOwnership,
void *aContent, uint64_t aExtraData, void* aClosure) void *aContent, uint64_t aExtraData, void* aClosure)
{ {
// Nothing to do. if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
MOZ_ASSERT(aClosure);
MOZ_ASSERT(!aContent);
auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
MOZ_ASSERT(aExtraData < closure->mMessagePortIdentifiers.Length());
dom::MessagePort::ForceClose(closure->mMessagePortIdentifiers[aExtraData]);
}
} }
}; };

View File

@ -588,6 +588,17 @@ BackgroundParentImpl::DeallocPMessagePortParent(PMessagePortParent* aActor)
return true; return true;
} }
bool
BackgroundParentImpl::RecvMessagePortForceClose(const nsID& aUUID,
const nsID& aDestinationUUID,
const uint32_t& aSequenceID)
{
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
return MessagePortParent::ForceClose(aUUID, aDestinationUUID, aSequenceID);
}
} // namespace ipc } // namespace ipc
} // namespace mozilla } // namespace mozilla

View File

@ -137,6 +137,11 @@ protected:
virtual bool virtual bool
DeallocPMessagePortParent(PMessagePortParent* aActor) override; DeallocPMessagePortParent(PMessagePortParent* aActor) override;
virtual bool
RecvMessagePortForceClose(const nsID& aUUID,
const nsID& aDestinationUUID,
const uint32_t& aSequenceID) override;
}; };
} // namespace ipc } // namespace ipc

View File

@ -60,6 +60,8 @@ parent:
PMessagePort(nsID uuid, nsID destinationUuid, uint32_t sequenceId); PMessagePort(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
MessagePortForceClose(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
child: child:
PCache(); PCache();
PCacheStreamControl(); PCacheStreamControl();