зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 0d23532d6a49 (bug 1757802) as requested by dev. CLOSED TREE
This commit is contained in:
Родитель
f4c31e97d8
Коммит
6b6840fbbf
|
@ -664,6 +664,42 @@ struct QueueParamTraits<std::pair<TypeA, TypeB>> {
|
|||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
template <>
|
||||
struct QueueParamTraits<mozilla::ipc::Shmem> {
|
||||
using ParamType = mozilla::ipc::Shmem;
|
||||
|
||||
template <typename U>
|
||||
static bool Write(ProducerView<U>& aProducerView, ParamType&& aParam) {
|
||||
if (!aProducerView.WriteParam(
|
||||
aParam.Id(mozilla::ipc::Shmem::PrivateIPDLCaller()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aParam.RevokeRights(mozilla::ipc::Shmem::PrivateIPDLCaller());
|
||||
aParam.forget(mozilla::ipc::Shmem::PrivateIPDLCaller());
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
static bool Read(ConsumerView<U>& aConsumerView, ParamType* aResult) {
|
||||
ParamType::id_t id;
|
||||
if (!aConsumerView.ReadParam(&id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mozilla::ipc::Shmem::SharedMemory* rawmem =
|
||||
aConsumerView.LookupSharedMemory(id);
|
||||
if (!rawmem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*aResult = mozilla::ipc::Shmem(mozilla::ipc::Shmem::PrivateIPDLCaller(),
|
||||
rawmem, id);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace webgl
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -322,6 +322,21 @@ void IProtocol::Unregister(int32_t aId) {
|
|||
return mToplevel->Unregister(aId);
|
||||
}
|
||||
|
||||
Shmem::SharedMemory* IProtocol::CreateSharedMemory(
|
||||
size_t aSize, SharedMemory::SharedMemoryType aType, bool aUnsafe,
|
||||
int32_t* aId) {
|
||||
return mToplevel->CreateSharedMemory(aSize, aType, aUnsafe, aId);
|
||||
}
|
||||
Shmem::SharedMemory* IProtocol::LookupSharedMemory(int32_t aId) {
|
||||
return mToplevel->LookupSharedMemory(aId);
|
||||
}
|
||||
bool IProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* aSegment) {
|
||||
return mToplevel->IsTrackingSharedMemory(aSegment);
|
||||
}
|
||||
bool IProtocol::DestroySharedMemory(Shmem& aShmem) {
|
||||
return mToplevel->DestroySharedMemory(aShmem);
|
||||
}
|
||||
|
||||
MessageChannel* IProtocol::GetIPCChannel() {
|
||||
return mToplevel->GetIPCChannel();
|
||||
}
|
||||
|
@ -393,8 +408,14 @@ bool IProtocol::AllocShmem(size_t aSize,
|
|||
return false;
|
||||
}
|
||||
|
||||
*aOutMem = Shmem(aSize, aType, false);
|
||||
return aOutMem->IsValid();
|
||||
Shmem::id_t id;
|
||||
Shmem::SharedMemory* rawmem(CreateSharedMemory(aSize, aType, false, &id));
|
||||
if (!rawmem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*aOutMem = Shmem(Shmem::PrivateIPDLCaller(), rawmem, id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IProtocol::AllocUnsafeShmem(size_t aSize,
|
||||
|
@ -406,13 +427,30 @@ bool IProtocol::AllocUnsafeShmem(size_t aSize,
|
|||
return false;
|
||||
}
|
||||
|
||||
*aOutMem = Shmem(aSize, aType, true);
|
||||
return aOutMem->IsValid();
|
||||
Shmem::id_t id;
|
||||
Shmem::SharedMemory* rawmem(CreateSharedMemory(aSize, aType, true, &id));
|
||||
if (!rawmem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*aOutMem = Shmem(Shmem::PrivateIPDLCaller(), rawmem, id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IProtocol::DeallocShmem(Shmem& aMem) {
|
||||
aMem.RevokeRights();
|
||||
return true;
|
||||
bool ok = DestroySharedMemory(aMem);
|
||||
#ifdef DEBUG
|
||||
if (!ok) {
|
||||
if (mSide == ChildSide) {
|
||||
FatalError("bad Shmem");
|
||||
} else {
|
||||
NS_WARNING("bad Shmem");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // DEBUG
|
||||
aMem.forget(Shmem::PrivateIPDLCaller());
|
||||
return ok;
|
||||
}
|
||||
|
||||
void IProtocol::SetManager(IProtocol* aManager) {
|
||||
|
@ -629,6 +667,104 @@ void IToplevelProtocol::Unregister(int32_t aId) {
|
|||
mActorMap.Remove(aId);
|
||||
}
|
||||
|
||||
Shmem::SharedMemory* IToplevelProtocol::CreateSharedMemory(
|
||||
size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, bool aUnsafe,
|
||||
Shmem::id_t* aId) {
|
||||
RefPtr<Shmem::SharedMemory> segment(
|
||||
Shmem::Alloc(Shmem::PrivateIPDLCaller(), aSize, aType, aUnsafe));
|
||||
if (!segment) {
|
||||
return nullptr;
|
||||
}
|
||||
int32_t id = NextId();
|
||||
Shmem shmem(Shmem::PrivateIPDLCaller(), segment.get(), id);
|
||||
|
||||
UniquePtr<Message> descriptor =
|
||||
shmem.MkCreatedMessage(Shmem::PrivateIPDLCaller(), MSG_ROUTING_CONTROL);
|
||||
if (!descriptor) {
|
||||
return nullptr;
|
||||
}
|
||||
Unused << GetIPCChannel()->Send(std::move(descriptor));
|
||||
|
||||
*aId = shmem.Id(Shmem::PrivateIPDLCaller());
|
||||
Shmem::SharedMemory* rawSegment = segment.get();
|
||||
MOZ_ASSERT(!mShmemMap.Contains(*aId), "Don't insert with an existing ID");
|
||||
mShmemMap.InsertOrUpdate(*aId, segment.forget().take());
|
||||
return rawSegment;
|
||||
}
|
||||
|
||||
Shmem::SharedMemory* IToplevelProtocol::LookupSharedMemory(Shmem::id_t aId) {
|
||||
return mShmemMap.Get(aId);
|
||||
}
|
||||
|
||||
bool IToplevelProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* segment) {
|
||||
for (const auto& shmem : mShmemMap.Values()) {
|
||||
if (segment == shmem) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IToplevelProtocol::DestroySharedMemory(Shmem& shmem) {
|
||||
Shmem::id_t aId = shmem.Id(Shmem::PrivateIPDLCaller());
|
||||
Shmem::SharedMemory* segment = LookupSharedMemory(aId);
|
||||
if (!segment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UniquePtr<Message> descriptor =
|
||||
shmem.MkDestroyedMessage(Shmem::PrivateIPDLCaller(), MSG_ROUTING_CONTROL);
|
||||
|
||||
MOZ_ASSERT(mShmemMap.Contains(aId),
|
||||
"Attempting to remove an ID not in the shmem map");
|
||||
mShmemMap.Remove(aId);
|
||||
Shmem::Dealloc(Shmem::PrivateIPDLCaller(), segment);
|
||||
|
||||
MessageChannel* channel = GetIPCChannel();
|
||||
if (!channel->CanSend()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return descriptor && channel->Send(std::move(descriptor));
|
||||
}
|
||||
|
||||
void IToplevelProtocol::DeallocShmems() {
|
||||
for (const auto& shmem : mShmemMap.Values()) {
|
||||
Shmem::Dealloc(Shmem::PrivateIPDLCaller(), shmem);
|
||||
}
|
||||
mShmemMap.Clear();
|
||||
}
|
||||
|
||||
bool IToplevelProtocol::ShmemCreated(const Message& aMsg) {
|
||||
Shmem::id_t id;
|
||||
RefPtr<Shmem::SharedMemory> rawmem(
|
||||
Shmem::OpenExisting(Shmem::PrivateIPDLCaller(), aMsg, &id, true));
|
||||
if (!rawmem) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(!mShmemMap.Contains(id), "Don't insert with an existing ID");
|
||||
mShmemMap.InsertOrUpdate(id, rawmem.forget().take());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IToplevelProtocol::ShmemDestroyed(const Message& aMsg) {
|
||||
Shmem::id_t id;
|
||||
MessageReader reader(aMsg);
|
||||
if (!IPC::ReadParam(&reader, &id)) {
|
||||
return false;
|
||||
}
|
||||
reader.EndRead();
|
||||
|
||||
Shmem::SharedMemory* rawmem = LookupSharedMemory(id);
|
||||
if (rawmem) {
|
||||
MOZ_ASSERT(mShmemMap.Contains(id),
|
||||
"Attempting to remove an ID not in the shmem map");
|
||||
mShmemMap.Remove(id);
|
||||
Shmem::Dealloc(Shmem::PrivateIPDLCaller(), rawmem);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
IPDLResolverInner::IPDLResolverInner(UniquePtr<IPC::Message> aReply,
|
||||
IProtocol* aActor)
|
||||
: mReply(std::move(aReply)),
|
||||
|
|
|
@ -73,8 +73,8 @@ enum {
|
|||
BUILD_IDS_MATCH_MESSAGE_TYPE = kuint16max - 8,
|
||||
BUILD_ID_MESSAGE_TYPE = kuint16max - 7, // unused
|
||||
CHANNEL_OPENED_MESSAGE_TYPE = kuint16max - 6,
|
||||
SHMEM_DESTROYED_MESSAGE_TYPE = kuint16max - 5, // unused
|
||||
SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 4, // unused
|
||||
SHMEM_DESTROYED_MESSAGE_TYPE = kuint16max - 5,
|
||||
SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 4,
|
||||
GOODBYE_MESSAGE_TYPE = kuint16max - 3,
|
||||
CANCEL_MESSAGE_TYPE = kuint16max - 2,
|
||||
|
||||
|
@ -198,6 +198,13 @@ class IProtocol : public HasResultCodes {
|
|||
IProtocol* Lookup(int32_t aId);
|
||||
void Unregister(int32_t aId);
|
||||
|
||||
Shmem::SharedMemory* CreateSharedMemory(size_t aSize,
|
||||
SharedMemory::SharedMemoryType aType,
|
||||
bool aUnsafe, int32_t* aId);
|
||||
Shmem::SharedMemory* LookupSharedMemory(int32_t aId);
|
||||
bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment);
|
||||
bool DestroySharedMemory(Shmem& aShmem);
|
||||
|
||||
MessageChannel* GetIPCChannel();
|
||||
const MessageChannel* GetIPCChannel() const;
|
||||
|
||||
|
@ -392,6 +399,13 @@ class IToplevelProtocol : public IProtocol {
|
|||
IProtocol* Lookup(int32_t aId);
|
||||
void Unregister(int32_t aId);
|
||||
|
||||
Shmem::SharedMemory* CreateSharedMemory(size_t aSize,
|
||||
SharedMemory::SharedMemoryType aType,
|
||||
bool aUnsafe, int32_t* aId);
|
||||
Shmem::SharedMemory* LookupSharedMemory(int32_t aId);
|
||||
bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment);
|
||||
bool DestroySharedMemory(Shmem& aShmem);
|
||||
|
||||
MessageChannel* GetIPCChannel() { return &mChannel; }
|
||||
const MessageChannel* GetIPCChannel() const { return &mChannel; }
|
||||
|
||||
|
@ -425,6 +439,10 @@ class IToplevelProtocol : public IProtocol {
|
|||
|
||||
void SetReplyTimeoutMs(int32_t aTimeoutMs);
|
||||
|
||||
void DeallocShmems();
|
||||
bool ShmemCreated(const Message& aMsg);
|
||||
bool ShmemDestroyed(const Message& aMsg);
|
||||
|
||||
virtual bool ShouldContinueFromReplyTimeout() { return false; }
|
||||
|
||||
// WARNING: This function is called with the MessageChannel monitor held.
|
||||
|
@ -481,6 +499,7 @@ class IToplevelProtocol : public IProtocol {
|
|||
// Used to be on mState
|
||||
int32_t mLastLocalId;
|
||||
IDMap<IProtocol*> mActorMap;
|
||||
IDMap<Shmem::SharedMemory*> mShmemMap;
|
||||
|
||||
MessageChannel mChannel;
|
||||
};
|
||||
|
|
|
@ -9,16 +9,176 @@
|
|||
#include "ProtocolUtils.h"
|
||||
#include "SharedMemoryBasic.h"
|
||||
#include "ShmemMessageUtils.h"
|
||||
#include "chrome/common/ipc_message_utils.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
namespace mozilla::ipc {
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
class ShmemCreated : public IPC::Message {
|
||||
private:
|
||||
typedef Shmem::id_t id_t;
|
||||
|
||||
public:
|
||||
ShmemCreated(int32_t routingId, id_t aIPDLId, size_t aSize,
|
||||
SharedMemory::SharedMemoryType aType)
|
||||
: IPC::Message(routingId, SHMEM_CREATED_MESSAGE_TYPE, 0,
|
||||
HeaderFlags(NESTED_INSIDE_CPOW)) {
|
||||
MOZ_RELEASE_ASSERT(aSize < std::numeric_limits<uint32_t>::max(),
|
||||
"Tried to create Shmem with size larger than 4GB");
|
||||
IPC::MessageWriter writer(*this);
|
||||
IPC::WriteParam(&writer, aIPDLId);
|
||||
IPC::WriteParam(&writer, uint32_t(aSize));
|
||||
IPC::WriteParam(&writer, int32_t(aType));
|
||||
}
|
||||
|
||||
static bool ReadInfo(IPC::MessageReader* aReader, id_t* aIPDLId,
|
||||
size_t* aSize, SharedMemory::SharedMemoryType* aType) {
|
||||
uint32_t size = 0;
|
||||
if (!IPC::ReadParam(aReader, aIPDLId) || !IPC::ReadParam(aReader, &size) ||
|
||||
!IPC::ReadParam(aReader, reinterpret_cast<int32_t*>(aType))) {
|
||||
return false;
|
||||
}
|
||||
*aSize = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Log(const std::string& aPrefix, FILE* aOutf) const {
|
||||
fputs("(special ShmemCreated msg)", aOutf);
|
||||
}
|
||||
};
|
||||
|
||||
class ShmemDestroyed : public IPC::Message {
|
||||
private:
|
||||
typedef Shmem::id_t id_t;
|
||||
|
||||
public:
|
||||
ShmemDestroyed(int32_t routingId, id_t aIPDLId)
|
||||
: IPC::Message(routingId, SHMEM_DESTROYED_MESSAGE_TYPE) {
|
||||
IPC::MessageWriter writer(*this);
|
||||
IPC::WriteParam(&writer, aIPDLId);
|
||||
}
|
||||
};
|
||||
|
||||
static SharedMemory* NewSegment(SharedMemory::SharedMemoryType aType) {
|
||||
if (SharedMemory::TYPE_BASIC == aType) {
|
||||
return new SharedMemoryBasic;
|
||||
} else {
|
||||
NS_ERROR("unknown Shmem type");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static already_AddRefed<SharedMemory> CreateSegment(
|
||||
SharedMemory::SharedMemoryType aType, size_t aNBytes, size_t aExtraSize) {
|
||||
RefPtr<SharedMemory> segment = NewSegment(aType);
|
||||
if (!segment) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t size = SharedMemory::PageAlignedSize(aNBytes + aExtraSize);
|
||||
if (!segment->Create(size) || !segment->Map(size)) {
|
||||
return nullptr;
|
||||
}
|
||||
return segment.forget();
|
||||
}
|
||||
|
||||
static already_AddRefed<SharedMemory> ReadSegment(
|
||||
const IPC::Message& aDescriptor, Shmem::id_t* aId, size_t* aNBytes,
|
||||
size_t aExtraSize) {
|
||||
if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) {
|
||||
NS_ERROR("expected 'shmem created' message");
|
||||
return nullptr;
|
||||
}
|
||||
SharedMemory::SharedMemoryType type;
|
||||
IPC::MessageReader reader(aDescriptor);
|
||||
if (!ShmemCreated::ReadInfo(&reader, aId, aNBytes, &type)) {
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<SharedMemory> segment = NewSegment(type);
|
||||
if (!segment) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!segment->ReadHandle(&reader)) {
|
||||
NS_ERROR("trying to open invalid handle");
|
||||
return nullptr;
|
||||
}
|
||||
reader.EndRead();
|
||||
size_t size = SharedMemory::PageAlignedSize(*aNBytes + aExtraSize);
|
||||
if (!segment->Map(size)) {
|
||||
return nullptr;
|
||||
}
|
||||
// close the handle to the segment after it is mapped
|
||||
segment->CloseHandle();
|
||||
return segment.forget();
|
||||
}
|
||||
|
||||
static void DestroySegment(SharedMemory* aSegment) {
|
||||
// the SharedMemory dtor closes and unmaps the actual OS shmem segment
|
||||
if (aSegment) {
|
||||
aSegment->Release();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
|
||||
static const char sMagic[] =
|
||||
"This little piggy went to market.\n"
|
||||
"This little piggy stayed at home.\n"
|
||||
"This little piggy has roast beef,\n"
|
||||
"This little piggy had none.\n"
|
||||
"And this little piggy cried \"Wee! Wee! Wee!\" all the way home";
|
||||
|
||||
struct Header {
|
||||
// Don't use size_t or bool here because their size depends on the
|
||||
// architecture.
|
||||
uint32_t mSize;
|
||||
uint32_t mUnsafe;
|
||||
char mMagic[sizeof(sMagic)];
|
||||
};
|
||||
|
||||
static void GetSections(Shmem::SharedMemory* aSegment, Header** aHeader,
|
||||
char** aFrontSentinel, char** aData,
|
||||
char** aBackSentinel) {
|
||||
MOZ_ASSERT(aSegment && aFrontSentinel && aData && aBackSentinel,
|
||||
"null param(s)");
|
||||
|
||||
*aFrontSentinel = reinterpret_cast<char*>(aSegment->memory());
|
||||
MOZ_ASSERT(*aFrontSentinel, "null memory()");
|
||||
|
||||
*aHeader = reinterpret_cast<Header*>(*aFrontSentinel);
|
||||
|
||||
size_t pageSize = Shmem::SharedMemory::SystemPageSize();
|
||||
*aData = *aFrontSentinel + pageSize;
|
||||
|
||||
*aBackSentinel = *aFrontSentinel + aSegment->Size() - pageSize;
|
||||
}
|
||||
|
||||
static Header* GetHeader(Shmem::SharedMemory* aSegment) {
|
||||
Header* header;
|
||||
char* dontcare;
|
||||
GetSections(aSegment, &header, &dontcare, &dontcare, &dontcare);
|
||||
return header;
|
||||
}
|
||||
|
||||
static void Protect(SharedMemory* aSegment) {
|
||||
MOZ_ASSERT(aSegment, "null segment");
|
||||
aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
|
||||
aSegment->Size(), RightsNone);
|
||||
}
|
||||
|
||||
static void Unprotect(SharedMemory* aSegment) {
|
||||
MOZ_ASSERT(aSegment, "null segment");
|
||||
aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
|
||||
aSegment->Size(), RightsRead | RightsWrite);
|
||||
}
|
||||
|
||||
//
|
||||
// In debug builds, we specially allocate shmem segments. The layout
|
||||
// is as follows
|
||||
//
|
||||
// Page 0: "front sentinel"
|
||||
// [nothing]
|
||||
// size of mapping
|
||||
// magic bytes
|
||||
// Page 1 through n-1:
|
||||
// user data
|
||||
// Page n: "back sentinel"
|
||||
|
@ -47,6 +207,11 @@ namespace mozilla::ipc {
|
|||
// The receiving process will then create a Shmem from the underlying
|
||||
// segment, and take the segment into the "mapped" state.
|
||||
//
|
||||
// In the "mapping" state, we use the front sentinel to verify the
|
||||
// integrity of the shmem segment. If valid, it has a size_t
|
||||
// containing the number of bytes the user allocated followed by the
|
||||
// magic bytes above.
|
||||
//
|
||||
// In the "mapped" state, the front and back sentinels have no access
|
||||
// rights. They act as guards against buffer overflows and underflows
|
||||
// in client code; if clients touch a sentinel, they die with SIGSEGV.
|
||||
|
@ -56,164 +221,256 @@ namespace mozilla::ipc {
|
|||
// to touch the segment, it dies with SIGSEGV.
|
||||
//
|
||||
|
||||
static size_t MappingSize(size_t aNBytes) {
|
||||
Shmem::Shmem(PrivateIPDLCaller, SharedMemory* aSegment, id_t aId)
|
||||
: mSegment(aSegment), mData(nullptr), mSize(0) {
|
||||
MOZ_ASSERT(mSegment, "null segment");
|
||||
MOZ_ASSERT(aId != 0, "invalid ID");
|
||||
|
||||
Unprotect(mSegment);
|
||||
|
||||
Header* header;
|
||||
char* frontSentinel;
|
||||
char* data;
|
||||
char* backSentinel;
|
||||
GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
|
||||
|
||||
// do a quick validity check to avoid weird-looking crashes in libc
|
||||
char check = *frontSentinel;
|
||||
(void)check;
|
||||
|
||||
MOZ_ASSERT(!strncmp(header->mMagic, sMagic, sizeof(sMagic)),
|
||||
"invalid segment");
|
||||
mSize = static_cast<size_t>(header->mSize);
|
||||
|
||||
size_t pageSize = SharedMemory::SystemPageSize();
|
||||
MOZ_ASSERT(IsPowerOfTwo(pageSize));
|
||||
MOZ_ASSERT(mSegment->Size() - (2 * pageSize) >= mSize,
|
||||
"illegal size in shared memory segment");
|
||||
|
||||
// Extra padding required to align to pagesize.
|
||||
size_t diff = aNBytes & (pageSize - 1);
|
||||
diff = (pageSize - diff) & (pageSize - 1);
|
||||
// transition into the "mapped" state by protecting the front and
|
||||
// back sentinels (which guard against buffer under/overflows)
|
||||
mSegment->Protect(frontSentinel, pageSize, RightsNone);
|
||||
mSegment->Protect(backSentinel, pageSize, RightsNone);
|
||||
|
||||
CheckedInt<size_t> totalSize = aNBytes;
|
||||
totalSize += diff;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Allocate 2 extra pages to act as guard pages for the shared memory region
|
||||
// in debug-mode. No extra space is allocated in release mode.
|
||||
totalSize += 2 * pageSize;
|
||||
#endif
|
||||
|
||||
MOZ_RELEASE_ASSERT(totalSize.isValid());
|
||||
return totalSize.value();
|
||||
// don't set these until we know they're valid
|
||||
mData = data;
|
||||
mId = aId;
|
||||
}
|
||||
|
||||
static Span<char> ConfigureAndGetData(SharedMemory* aSegment) {
|
||||
Span<char> memory{reinterpret_cast<char*>(aSegment->memory()),
|
||||
aSegment->Size()};
|
||||
#ifdef DEBUG
|
||||
size_t pageSize = SharedMemory::SystemPageSize();
|
||||
auto [frontSentinel, suffix] = memory.SplitAt(pageSize);
|
||||
auto [data, backSentinel] = memory.SplitAt(suffix.Length() - pageSize);
|
||||
|
||||
// The sentinel memory regions should be non-readable and non-writable,
|
||||
// whereas the data region needs to be readable & writable.
|
||||
aSegment->Protect(frontSentinel.data(), frontSentinel.size(), RightsNone);
|
||||
aSegment->Protect(data.data(), data.size(), RightsRead | RightsWrite);
|
||||
aSegment->Protect(backSentinel.data(), backSentinel.size(), RightsNone);
|
||||
return data;
|
||||
#else
|
||||
return memory;
|
||||
#endif
|
||||
}
|
||||
|
||||
static RefPtr<SharedMemory> AllocSegment(size_t aNBytes,
|
||||
SharedMemory::SharedMemoryType aType) {
|
||||
size_t mappingSize = MappingSize(aNBytes);
|
||||
|
||||
MOZ_RELEASE_ASSERT(aType == SharedMemory::TYPE_BASIC,
|
||||
"Unknown SharedMemoryType!");
|
||||
RefPtr<SharedMemory> segment = new SharedMemoryBasic;
|
||||
if (!segment->Create(mappingSize) || !segment->Map(mappingSize)) {
|
||||
NS_WARNING("Failed to create or map segment");
|
||||
return nullptr;
|
||||
}
|
||||
return segment;
|
||||
}
|
||||
|
||||
Shmem::Shmem(size_t aNBytes, SharedMemoryType aType, bool aUnsafe)
|
||||
: Shmem(AllocSegment(aNBytes, aType), aNBytes, aUnsafe) {}
|
||||
|
||||
Shmem::Shmem(RefPtr<SharedMemory> aSegment, size_t aNBytes, bool aUnsafe) {
|
||||
if (!aSegment) {
|
||||
return;
|
||||
}
|
||||
size_t mappingSize = MappingSize(aNBytes);
|
||||
if (mappingSize != aSegment->Size()) {
|
||||
NS_WARNING("Segment has an incorrect size");
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = ConfigureAndGetData(aSegment);
|
||||
MOZ_RELEASE_ASSERT(data.size() >= aNBytes);
|
||||
|
||||
mSegment = aSegment;
|
||||
mData = data.data();
|
||||
mSize = aNBytes;
|
||||
#ifdef DEBUG
|
||||
mUnsafe = aUnsafe;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void Shmem::AssertInvariants() const {
|
||||
MOZ_ASSERT(mSegment, "null segment");
|
||||
MOZ_ASSERT(mData, "null data pointer");
|
||||
MOZ_ASSERT(mSize > 0, "invalid size");
|
||||
MOZ_ASSERT(MappingSize(mSize) == mSegment->Size(),
|
||||
"size doesn't match segment");
|
||||
|
||||
// if the segment isn't owned by the current process, these will
|
||||
// trigger SIGSEGV
|
||||
*reinterpret_cast<volatile char*>(mData);
|
||||
*(reinterpret_cast<volatile char*>(mData) + mSize - 1);
|
||||
char checkMappingFront = *reinterpret_cast<char*>(mData);
|
||||
char checkMappingBack = *(reinterpret_cast<char*>(mData) + mSize - 1);
|
||||
|
||||
// avoid "unused" warnings for these variables:
|
||||
Unused << checkMappingFront;
|
||||
Unused << checkMappingBack;
|
||||
}
|
||||
|
||||
void Shmem::RevokeRights() {
|
||||
void Shmem::RevokeRights(PrivateIPDLCaller) {
|
||||
AssertInvariants();
|
||||
|
||||
// If we're not working with an "unsafe" shared memory, revoke access rights
|
||||
// to the entire shared memory region.
|
||||
if (!mUnsafe) {
|
||||
mSegment->Protect(reinterpret_cast<char*>(mSegment->memory()),
|
||||
mSegment->Size(), RightsNone);
|
||||
size_t pageSize = SharedMemory::SystemPageSize();
|
||||
Header* header = GetHeader(mSegment);
|
||||
|
||||
// Open this up for reading temporarily
|
||||
mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsRead);
|
||||
|
||||
if (!header->mUnsafe) {
|
||||
Protect(mSegment);
|
||||
} else {
|
||||
mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsNone);
|
||||
}
|
||||
|
||||
*this = Shmem();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace mozilla::ipc
|
||||
|
||||
namespace IPC {
|
||||
|
||||
void ParamTraits<mozilla::ipc::Shmem>::Write(MessageWriter* aWriter,
|
||||
paramType&& aParam) {
|
||||
aParam.AssertInvariants();
|
||||
|
||||
MOZ_ASSERT(aParam.mSegment->Type() ==
|
||||
mozilla::ipc::SharedMemory::SharedMemoryType::TYPE_BASIC,
|
||||
"Only supported type is TYPE_BASIC");
|
||||
|
||||
WriteParam(aWriter, uint64_t(aParam.mSize));
|
||||
aParam.mSegment->WriteHandle(aWriter);
|
||||
#ifdef DEBUG
|
||||
WriteParam(aWriter, aParam.mUnsafe);
|
||||
#endif
|
||||
|
||||
aParam.RevokeRights();
|
||||
}
|
||||
|
||||
bool ParamTraits<mozilla::ipc::Shmem>::Read(MessageReader* aReader,
|
||||
paramType* aResult) {
|
||||
*aResult = mozilla::ipc::Shmem();
|
||||
// static
|
||||
already_AddRefed<Shmem::SharedMemory> Shmem::Alloc(PrivateIPDLCaller,
|
||||
size_t aNBytes,
|
||||
SharedMemoryType aType,
|
||||
bool aUnsafe,
|
||||
bool aProtect) {
|
||||
NS_ASSERTION(aNBytes <= UINT32_MAX, "Will truncate shmem segment size!");
|
||||
MOZ_ASSERT(!aProtect || !aUnsafe, "protect => !unsafe");
|
||||
|
||||
// Size is sent as uint64_t to deal with IPC between processes with different
|
||||
// `size_t` sizes.
|
||||
uint64_t rawSize = 0;
|
||||
if (!ReadParam(aReader, &rawSize)) {
|
||||
return false;
|
||||
}
|
||||
mozilla::CheckedInt<size_t> size{rawSize};
|
||||
if (!size.isValid() || size == 0) {
|
||||
return false;
|
||||
size_t pageSize = SharedMemory::SystemPageSize();
|
||||
// |2*pageSize| is for the front and back sentinel
|
||||
RefPtr<SharedMemory> segment = CreateSegment(aType, aNBytes, 2 * pageSize);
|
||||
if (!segment) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<mozilla::ipc::SharedMemory> segment =
|
||||
new mozilla::ipc::SharedMemoryBasic;
|
||||
if (!segment->ReadHandle(aReader) ||
|
||||
!segment->Map(mozilla::ipc::MappingSize(size.value()))) {
|
||||
return false;
|
||||
}
|
||||
Header* header;
|
||||
char* frontSentinel;
|
||||
char* data;
|
||||
char* backSentinel;
|
||||
GetSections(segment, &header, &frontSentinel, &data, &backSentinel);
|
||||
|
||||
bool unsafe = false;
|
||||
#ifdef DEBUG
|
||||
if (!ReadParam(aReader, &unsafe)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
// initialize the segment with Shmem-internal information
|
||||
|
||||
*aResult = mozilla::ipc::Shmem(segment, size.value(), unsafe);
|
||||
return aResult->IsValid();
|
||||
// NB: this can't be a static assert because technically pageSize
|
||||
// isn't known at compile time, event though in practice it's always
|
||||
// going to be 4KiB
|
||||
MOZ_ASSERT(sizeof(Header) <= pageSize, "Shmem::Header has gotten too big");
|
||||
memcpy(header->mMagic, sMagic, sizeof(sMagic));
|
||||
header->mSize = static_cast<uint32_t>(aNBytes);
|
||||
header->mUnsafe = aUnsafe;
|
||||
|
||||
if (aProtect) Protect(segment);
|
||||
|
||||
return segment.forget();
|
||||
}
|
||||
|
||||
} // namespace IPC
|
||||
// static
|
||||
already_AddRefed<Shmem::SharedMemory> Shmem::OpenExisting(
|
||||
PrivateIPDLCaller, const IPC::Message& aDescriptor, id_t* aId,
|
||||
bool aProtect) {
|
||||
size_t size;
|
||||
size_t pageSize = SharedMemory::SystemPageSize();
|
||||
// |2*pageSize| is for the front and back sentinels
|
||||
RefPtr<SharedMemory> segment =
|
||||
ReadSegment(aDescriptor, aId, &size, 2 * pageSize);
|
||||
if (!segment) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Header* header = GetHeader(segment);
|
||||
|
||||
if (size != header->mSize) {
|
||||
// Deallocation should zero out the header, so check for that.
|
||||
if (header->mSize || header->mUnsafe || header->mMagic[0] ||
|
||||
memcmp(header->mMagic, &header->mMagic[1],
|
||||
sizeof(header->mMagic) - 1)) {
|
||||
NS_ERROR("Wrong size for this Shmem!");
|
||||
} else {
|
||||
NS_WARNING("Shmem was deallocated");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The caller of this function may not know whether the segment is
|
||||
// unsafe or not
|
||||
if (!header->mUnsafe && aProtect) Protect(segment);
|
||||
|
||||
return segment.forget();
|
||||
}
|
||||
|
||||
// static
|
||||
void Shmem::Dealloc(PrivateIPDLCaller, SharedMemory* aSegment) {
|
||||
if (!aSegment) return;
|
||||
|
||||
size_t pageSize = SharedMemory::SystemPageSize();
|
||||
Header* header;
|
||||
char* frontSentinel;
|
||||
char* data;
|
||||
char* backSentinel;
|
||||
GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
|
||||
|
||||
aSegment->Protect(frontSentinel, pageSize, RightsWrite | RightsRead);
|
||||
memset(header->mMagic, 0, sizeof(sMagic));
|
||||
header->mSize = 0;
|
||||
header->mUnsafe = false; // make it "safe" so as to catch errors
|
||||
|
||||
DestroySegment(aSegment);
|
||||
}
|
||||
|
||||
#else // !defined(DEBUG)
|
||||
|
||||
Shmem::Shmem(PrivateIPDLCaller, SharedMemory* aSegment, id_t aId)
|
||||
: mSegment(aSegment), mData(aSegment->memory()), mSize(0), mId(aId) {
|
||||
mSize = static_cast<size_t>(*PtrToSize(mSegment));
|
||||
MOZ_RELEASE_ASSERT(mSegment->Size() - sizeof(uint32_t) >= mSize,
|
||||
"illegal size in shared memory segment");
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Shmem::SharedMemory> Shmem::Alloc(PrivateIPDLCaller,
|
||||
size_t aNBytes,
|
||||
SharedMemoryType aType,
|
||||
bool /*unused*/,
|
||||
bool /*unused*/) {
|
||||
RefPtr<SharedMemory> segment =
|
||||
CreateSegment(aType, aNBytes, sizeof(uint32_t));
|
||||
if (!segment) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*PtrToSize(segment) = static_cast<uint32_t>(aNBytes);
|
||||
|
||||
return segment.forget();
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Shmem::SharedMemory> Shmem::OpenExisting(
|
||||
PrivateIPDLCaller, const IPC::Message& aDescriptor, id_t* aId,
|
||||
bool /*unused*/) {
|
||||
size_t size;
|
||||
RefPtr<SharedMemory> segment =
|
||||
ReadSegment(aDescriptor, aId, &size, sizeof(uint32_t));
|
||||
if (!segment) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// this is the only validity check done in non-DEBUG builds
|
||||
if (size != static_cast<size_t>(*PtrToSize(segment))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return segment.forget();
|
||||
}
|
||||
|
||||
// static
|
||||
void Shmem::Dealloc(PrivateIPDLCaller, SharedMemory* aSegment) {
|
||||
DestroySegment(aSegment);
|
||||
}
|
||||
|
||||
#endif // if defined(DEBUG)
|
||||
|
||||
UniquePtr<IPC::Message> Shmem::MkCreatedMessage(PrivateIPDLCaller,
|
||||
int32_t routingId) {
|
||||
AssertInvariants();
|
||||
|
||||
auto msg = MakeUnique<ShmemCreated>(routingId, mId, mSize, mSegment->Type());
|
||||
IPC::MessageWriter writer(*msg);
|
||||
if (!mSegment->WriteHandle(&writer)) {
|
||||
return nullptr;
|
||||
}
|
||||
// close the handle to the segment after it is shared
|
||||
mSegment->CloseHandle();
|
||||
return msg;
|
||||
}
|
||||
|
||||
UniquePtr<IPC::Message> Shmem::MkDestroyedMessage(PrivateIPDLCaller,
|
||||
int32_t routingId) {
|
||||
AssertInvariants();
|
||||
return MakeUnique<ShmemDestroyed>(routingId, mId);
|
||||
}
|
||||
|
||||
void IPDLParamTraits<Shmem>::Write(IPC::MessageWriter* aWriter,
|
||||
IProtocol* aActor, Shmem&& aParam) {
|
||||
WriteIPDLParam(aWriter, aActor, aParam.mId);
|
||||
|
||||
aParam.RevokeRights(Shmem::PrivateIPDLCaller());
|
||||
aParam.forget(Shmem::PrivateIPDLCaller());
|
||||
}
|
||||
|
||||
bool IPDLParamTraits<Shmem>::Read(IPC::MessageReader* aReader,
|
||||
IProtocol* aActor, paramType* aResult) {
|
||||
paramType::id_t id;
|
||||
if (!ReadIPDLParam(aReader, aActor, &id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Shmem::SharedMemory* rawmem = aActor->LookupSharedMemory(id);
|
||||
if (rawmem) {
|
||||
*aResult = Shmem(Shmem::PrivateIPDLCaller(), rawmem, id);
|
||||
return true;
|
||||
}
|
||||
*aResult = Shmem();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
|
181
ipc/glue/Shmem.h
181
ipc/glue/Shmem.h
|
@ -19,62 +19,91 @@
|
|||
#include "mozilla/Range.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace IPC {
|
||||
template <typename T>
|
||||
struct ParamTraits;
|
||||
}
|
||||
/**
|
||||
* |Shmem| is one agent in the IPDL shared memory scheme. The way it
|
||||
works is essentially
|
||||
*
|
||||
* (1) C++ code calls, say, |parentActor->AllocShmem(size)|
|
||||
|
||||
namespace mozilla::ipc {
|
||||
* (2) IPDL-generated code creates a |mozilla::ipc::SharedMemory|
|
||||
* wrapping the bare OS shmem primitives. The code then adds the new
|
||||
* SharedMemory to the set of shmem segments being managed by IPDL.
|
||||
*
|
||||
* (3) IPDL-generated code "shares" the new SharedMemory to the child
|
||||
* process, and then sends a special asynchronous IPC message to the
|
||||
* child notifying it of the creation of the segment. (What this
|
||||
* means is OS specific.)
|
||||
*
|
||||
* (4a) The child receives the special IPC message, and using the
|
||||
* |SharedMemory{Basic}::Handle| it was passed, creates a
|
||||
* |mozilla::ipc::SharedMemory| in the child
|
||||
* process.
|
||||
*
|
||||
* (4b) After sending the "shmem-created" IPC message, IPDL-generated
|
||||
* code in the parent returns a |mozilla::ipc::Shmem| back to the C++
|
||||
* caller of |parentActor->AllocShmem()|. The |Shmem| is a "weak
|
||||
* reference" to the underlying |SharedMemory|, which is managed by
|
||||
* IPDL-generated code. C++ consumers of |Shmem| can't get at the
|
||||
* underlying |SharedMemory|.
|
||||
*
|
||||
* If parent code wants to give access rights to the Shmem to the
|
||||
* child, it does so by sending its |Shmem| to the child, in an IPDL
|
||||
* message. The parent's |Shmem| then "dies", i.e. becomes
|
||||
* inaccessible. This process could be compared to passing a
|
||||
* "shmem-access baton" between parent and child.
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
class ShadowLayerForwarder;
|
||||
} // namespace layers
|
||||
|
||||
namespace ipc {
|
||||
|
||||
template <typename P>
|
||||
struct IPDLParamTraits;
|
||||
|
||||
// A `Shmem` is a wrapper around OS-level shared memory. It comes in two major
|
||||
// modes: safe and unsafe.
|
||||
//
|
||||
// Access to the memory within a "safe" shmem is conceptually transferred to the
|
||||
// remote process when the shmem is transferred by marking the mapped memory in
|
||||
// the sending process as inaccessable (debug-mode only). Code should not
|
||||
// attempt to access the shmem after it has been transferred, though it will
|
||||
// remain mapped until all other Shmem references have been dropped.
|
||||
//
|
||||
// Access to the memory within an "unsafe" shmem is not protected in the same
|
||||
// way, and can be shared between multiple processes.
|
||||
//
|
||||
// For now, the primary way to create a `Shmem` is to call the
|
||||
// `IProtocol::AllocShmem` or `IProtocol::AllocUnsafeShmem` methods on an IPDL
|
||||
// actor, however this requirement may be relaxed in the future.
|
||||
class Shmem final {
|
||||
friend struct IPC::ParamTraits<mozilla::ipc::Shmem>;
|
||||
friend struct IPDLParamTraits<mozilla::ipc::Shmem>;
|
||||
#ifdef DEBUG
|
||||
// For ShadowLayerForwarder::CheckSurfaceDescriptor
|
||||
friend class mozilla::layers::ShadowLayerForwarder;
|
||||
#endif
|
||||
|
||||
public:
|
||||
typedef int32_t id_t;
|
||||
// Low-level wrapper around platform shmem primitives.
|
||||
typedef mozilla::ipc::SharedMemory SharedMemory;
|
||||
typedef SharedMemory::SharedMemoryType SharedMemoryType;
|
||||
// Shmem objects should only be constructed directly from SharedMemory
|
||||
// objects by the Shmem implementation itself, or by a select few functions
|
||||
// in ProtocolUtils.{h,cpp}. You should not need to add new instances of
|
||||
// this token.
|
||||
struct PrivateIPDLCaller {};
|
||||
|
||||
Shmem() = default;
|
||||
Shmem() : mSegment(nullptr), mData(nullptr), mSize(0), mId(0) {}
|
||||
|
||||
// Allocates a brand new shared memory region with sufficient size for
|
||||
// `aNBytes` bytes. This region may be transferred to other processes by being
|
||||
// sent over an IPDL actor.
|
||||
Shmem(size_t aNBytes, SharedMemoryType aType, bool aUnsafe);
|
||||
|
||||
// Create a reference to an existing shared memory region. The segment must be
|
||||
// large enough for `aNBytes`.
|
||||
Shmem(RefPtr<SharedMemory> aSegment, size_t aNBytes, bool aUnsafe);
|
||||
|
||||
// NOTE: Some callers are broken if a move constructor is provided here.
|
||||
Shmem(const Shmem& aOther) = default;
|
||||
|
||||
Shmem(PrivateIPDLCaller, SharedMemory* aSegment, id_t aId);
|
||||
|
||||
~Shmem() {
|
||||
// Shmem only holds a "weak ref" to the actual segment, which is
|
||||
// owned by IPDL. So there's nothing interesting to be done here
|
||||
forget(PrivateIPDLCaller());
|
||||
}
|
||||
|
||||
Shmem& operator=(const Shmem& aRhs) = default;
|
||||
|
||||
bool operator==(const Shmem& aRhs) const { return mSegment == aRhs.mSegment; }
|
||||
bool operator!=(const Shmem& aRhs) const { return mSegment != aRhs.mSegment; }
|
||||
|
||||
// Returns whether this Shmem is valid, and contains a reference to internal
|
||||
// state.
|
||||
bool IsValid() const { return mSegment != nullptr; }
|
||||
// Returns whether this Shmem is writable by you, and thus whether you can
|
||||
// transfer writability to another actor.
|
||||
bool IsWritable() const { return mSegment != nullptr; }
|
||||
|
||||
// Legacy names for `IsValid()` - do _NOT_ actually confirm whether or not you
|
||||
// should be able to write to or read from this type.
|
||||
bool IsWritable() const { return IsValid(); }
|
||||
bool IsReadable() const { return IsValid(); }
|
||||
// Returns whether this Shmem is readable by you, and thus whether you can
|
||||
// transfer readability to another actor.
|
||||
bool IsReadable() const { return mSegment != nullptr; }
|
||||
|
||||
// Return a pointer to the user-visible data segment.
|
||||
template <typename T>
|
||||
|
@ -102,36 +131,80 @@ class Shmem final {
|
|||
return {get<T>(), Size<T>()};
|
||||
}
|
||||
|
||||
// For safe shmems in debug mode, immediately revoke all access rights to the
|
||||
// memory when deallocating it.
|
||||
// Also resets this particular `Shmem` instance to an invalid state.
|
||||
// These shouldn't be used directly, use the IPDL interface instead.
|
||||
id_t Id(PrivateIPDLCaller) const { return mId; }
|
||||
|
||||
SharedMemory* Segment(PrivateIPDLCaller) const { return mSegment; }
|
||||
|
||||
#ifndef DEBUG
|
||||
void RevokeRights() { *this = Shmem(); }
|
||||
void RevokeRights(PrivateIPDLCaller) {}
|
||||
#else
|
||||
void RevokeRights();
|
||||
void RevokeRights(PrivateIPDLCaller);
|
||||
#endif
|
||||
|
||||
void forget(PrivateIPDLCaller) {
|
||||
mSegment = nullptr;
|
||||
mData = nullptr;
|
||||
mSize = 0;
|
||||
mId = 0;
|
||||
}
|
||||
|
||||
static already_AddRefed<Shmem::SharedMemory> Alloc(PrivateIPDLCaller,
|
||||
size_t aNBytes,
|
||||
SharedMemoryType aType,
|
||||
bool aUnsafe,
|
||||
bool aProtect = false);
|
||||
|
||||
// Prepare this to be shared with another process. Return an IPC message that
|
||||
// contains enough information for the other process to map this segment in
|
||||
// OpenExisting() below. Return a new message if successful (owned by the
|
||||
// caller), nullptr if not.
|
||||
UniquePtr<IPC::Message> MkCreatedMessage(PrivateIPDLCaller,
|
||||
int32_t routingId);
|
||||
|
||||
// Stop sharing this with another process. Return an IPC message that
|
||||
// contains enough information for the other process to unmap this
|
||||
// segment. Return a new message if successful (owned by the
|
||||
// caller), nullptr if not.
|
||||
UniquePtr<IPC::Message> MkDestroyedMessage(PrivateIPDLCaller,
|
||||
int32_t routingId);
|
||||
|
||||
// Return a SharedMemory instance in this process using the descriptor shared
|
||||
// to us by the process that created the underlying OS shmem resource. The
|
||||
// contents of the descriptor depend on the type of SharedMemory that was
|
||||
// passed to us.
|
||||
static already_AddRefed<SharedMemory> OpenExisting(
|
||||
PrivateIPDLCaller, const IPC::Message& aDescriptor, id_t* aId,
|
||||
bool aProtect = false);
|
||||
|
||||
static void Dealloc(PrivateIPDLCaller, SharedMemory* aSegment);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void AssertAligned() const {
|
||||
MOZ_RELEASE_ASSERT(0 == (mSize % sizeof(T)),
|
||||
"shmem size is not a multiple of sizeof(T)");
|
||||
if (0 != (mSize % sizeof(T))) MOZ_CRASH("shmem is not T-aligned");
|
||||
}
|
||||
|
||||
#ifndef DEBUG
|
||||
#if !defined(DEBUG)
|
||||
void AssertInvariants() const {}
|
||||
|
||||
static uint32_t* PtrToSize(SharedMemory* aSegment) {
|
||||
char* endOfSegment =
|
||||
reinterpret_cast<char*>(aSegment->memory()) + aSegment->Size();
|
||||
return reinterpret_cast<uint32_t*>(endOfSegment - sizeof(uint32_t));
|
||||
}
|
||||
|
||||
#else
|
||||
void AssertInvariants() const;
|
||||
#endif
|
||||
|
||||
RefPtr<SharedMemory> mSegment;
|
||||
void* mData = nullptr;
|
||||
size_t mSize = 0;
|
||||
#ifdef DEBUG
|
||||
bool mUnsafe = false;
|
||||
#endif
|
||||
void* mData;
|
||||
size_t mSize;
|
||||
id_t mId;
|
||||
};
|
||||
|
||||
} // namespace mozilla::ipc
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ifndef mozilla_ipc_Shmem_h
|
||||
|
|
|
@ -11,20 +11,24 @@
|
|||
#include "mozilla/ipc/IPDLParamTraits.h"
|
||||
#include "mozilla/ipc/Shmem.h"
|
||||
|
||||
namespace IPC {
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::ipc::Shmem> {
|
||||
typedef mozilla::ipc::Shmem paramType;
|
||||
struct IPDLParamTraits<Shmem> {
|
||||
typedef Shmem paramType;
|
||||
|
||||
static void Write(IPC::MessageWriter* aWriter, paramType&& aParam);
|
||||
static bool Read(IPC::MessageReader* aReader, paramType* aResult);
|
||||
static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
|
||||
paramType&& aParam);
|
||||
static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
|
||||
paramType* aResult);
|
||||
|
||||
static void Log(const paramType& aParam, std::wstring* aLog) {
|
||||
aLog->append(L"(shmem segment)");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace IPC
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ifndef mozilla_ipc_ShmemMessageUtils_h
|
||||
|
|
|
@ -4227,6 +4227,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
"""
|
||||
DestroySubtree(NormalShutdown);
|
||||
ClearSubtree();
|
||||
DeallocShmems();
|
||||
if (GetLifecycleProxy()) {
|
||||
GetLifecycleProxy()->Release();
|
||||
}
|
||||
|
@ -4242,6 +4243,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
"""
|
||||
DestroySubtree(AbnormalShutdown);
|
||||
ClearSubtree();
|
||||
DeallocShmems();
|
||||
if (GetLifecycleProxy()) {
|
||||
GetLifecycleProxy()->Release();
|
||||
}
|
||||
|
@ -4371,6 +4373,32 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
|
||||
methods = []
|
||||
|
||||
if p.decl.type.isToplevel():
|
||||
|
||||
# "private" message that passes shmem mappings from one process
|
||||
# to the other
|
||||
if p.subtreeUsesShmem():
|
||||
self.asyncSwitch.addcase(
|
||||
CaseLabel("SHMEM_CREATED_MESSAGE_TYPE"),
|
||||
self.genShmemCreatedHandler(),
|
||||
)
|
||||
self.asyncSwitch.addcase(
|
||||
CaseLabel("SHMEM_DESTROYED_MESSAGE_TYPE"),
|
||||
self.genShmemDestroyedHandler(),
|
||||
)
|
||||
else:
|
||||
abort = StmtBlock()
|
||||
abort.addstmts(
|
||||
[
|
||||
_fatalError("this protocol tree does not use shmem"),
|
||||
StmtReturn(_Result.NotKnown),
|
||||
]
|
||||
)
|
||||
self.asyncSwitch.addcase(CaseLabel("SHMEM_CREATED_MESSAGE_TYPE"), abort)
|
||||
self.asyncSwitch.addcase(
|
||||
CaseLabel("SHMEM_DESTROYED_MESSAGE_TYPE"), abort
|
||||
)
|
||||
|
||||
# Keep track of types created with an INOUT ctor. We need to call
|
||||
# Register() or RegisterID() for them depending on the side the managee
|
||||
# is created.
|
||||
|
@ -4489,6 +4517,36 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
|
||||
return methods + [removemanagee, deallocmanagee, Whitespace.NL]
|
||||
|
||||
def genShmemCreatedHandler(self):
|
||||
assert self.protocol.decl.type.isToplevel()
|
||||
|
||||
return StmtCode(
|
||||
"""
|
||||
{
|
||||
if (!ShmemCreated(${msgvar})) {
|
||||
return MsgPayloadError;
|
||||
}
|
||||
return MsgProcessed;
|
||||
}
|
||||
""",
|
||||
msgvar=self.msgvar,
|
||||
)
|
||||
|
||||
def genShmemDestroyedHandler(self):
|
||||
assert self.protocol.decl.type.isToplevel()
|
||||
|
||||
return StmtCode(
|
||||
"""
|
||||
{
|
||||
if (!ShmemDestroyed(${msgvar})) {
|
||||
return MsgPayloadError;
|
||||
}
|
||||
return MsgProcessed;
|
||||
}
|
||||
""",
|
||||
msgvar=self.msgvar,
|
||||
)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# The next few functions are the crux of the IPDL code generator.
|
||||
# They generate code for all the nasty work of message
|
||||
|
|
|
@ -22,6 +22,18 @@ class ProtocolFuzzerHelper {
|
|||
const nsACString& aRemoteType);
|
||||
|
||||
static void CompositorBridgeParentSetup();
|
||||
|
||||
static void AddShmemToProtocol(IToplevelProtocol* aProtocol,
|
||||
Shmem::SharedMemory* aSegment, int32_t aId) {
|
||||
MOZ_ASSERT(!aProtocol->mShmemMap.Contains(aId),
|
||||
"Don't insert with an existing ID");
|
||||
aProtocol->mShmemMap.InsertOrUpdate(aId, aSegment);
|
||||
}
|
||||
|
||||
static void RemoveShmemFromProtocol(IToplevelProtocol* aProtocol,
|
||||
int32_t aId) {
|
||||
aProtocol->mShmemMap.Remove(aId);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
@ -43,6 +55,39 @@ void FuzzProtocol(T* aProtocol, const uint8_t* aData, size_t aSize,
|
|||
continue;
|
||||
}
|
||||
|
||||
uint8_t num_shmems = 0;
|
||||
if (aSize) {
|
||||
num_shmems = *aData;
|
||||
aData++;
|
||||
aSize--;
|
||||
|
||||
for (uint32_t i = 0; i < num_shmems; i++) {
|
||||
if (aSize < sizeof(uint16_t)) {
|
||||
break;
|
||||
}
|
||||
size_t shmem_size = *reinterpret_cast<const uint16_t*>(aData);
|
||||
aData += sizeof(uint16_t);
|
||||
aSize -= sizeof(uint16_t);
|
||||
|
||||
if (shmem_size > aSize) {
|
||||
break;
|
||||
}
|
||||
RefPtr<Shmem::SharedMemory> segment(
|
||||
Shmem::Alloc(Shmem::PrivateIPDLCaller(), shmem_size,
|
||||
SharedMemory::TYPE_BASIC, false));
|
||||
if (!segment) {
|
||||
break;
|
||||
}
|
||||
|
||||
Shmem shmem(Shmem::PrivateIPDLCaller(), segment.get(), i + 1);
|
||||
memcpy(shmem.get<uint8_t>(), aData, shmem_size);
|
||||
ProtocolFuzzerHelper::AddShmemToProtocol(
|
||||
aProtocol, segment.forget().take(), i + 1);
|
||||
|
||||
aData += shmem_size;
|
||||
aSize -= shmem_size;
|
||||
}
|
||||
}
|
||||
// TODO: attach |m.header().num_fds| file descriptors to |m|. MVP can be
|
||||
// empty files, next implementation maybe read a length header from |data|
|
||||
// and then that many bytes.
|
||||
|
@ -53,6 +98,11 @@ void FuzzProtocol(T* aProtocol, const uint8_t* aData, size_t aSize,
|
|||
} else {
|
||||
aProtocol->OnMessageReceived(m);
|
||||
}
|
||||
for (uint32_t i = 0; i < num_shmems; i++) {
|
||||
Shmem::SharedMemory* segment = aProtocol->LookupSharedMemory(i + 1);
|
||||
Shmem::Dealloc(Shmem::PrivateIPDLCaller(), segment);
|
||||
ProtocolFuzzerHelper::RemoveShmemFromProtocol(aProtocol, i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче