Backed out changeset 0d23532d6a49 (bug 1757802) as requested by dev. CLOSED TREE

This commit is contained in:
Butkovits Atila 2022-04-22 23:20:08 +03:00
Родитель f4c31e97d8
Коммит 6b6840fbbf
8 изменённых файлов: 839 добавлений и 206 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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);
}
}
}