diff options
Diffstat (limited to 'ipc/glue/Shmem.cpp')
-rw-r--r-- | ipc/glue/Shmem.cpp | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/ipc/glue/Shmem.cpp b/ipc/glue/Shmem.cpp new file mode 100644 index 0000000000..442c6fb73c --- /dev/null +++ b/ipc/glue/Shmem.cpp @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Shmem.h" + +#include "ProtocolUtils.h" +#include "SharedMemoryBasic.h" +#include "ShmemMessageUtils.h" +#include "chrome/common/ipc_message_utils.h" +#include "mozilla/Unused.h" + +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) + : IPC::Message( + routingId, SHMEM_CREATED_MESSAGE_TYPE, 0, + HeaderFlags(NESTED_INSIDE_CPOW, CONTROL_PRIORITY, COMPRESSION_NONE, + LAZY_SEND, NOT_CONSTRUCTOR, ASYNC, NOT_REPLY)) { + 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)); + } + + static bool ReadInfo(IPC::MessageReader* aReader, id_t* aIPDLId, + size_t* aSize) { + uint32_t size = 0; + if (!IPC::ReadParam(aReader, aIPDLId) || !IPC::ReadParam(aReader, &size)) { + 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, 0, + HeaderFlags(NOT_NESTED, NORMAL_PRIORITY, COMPRESSION_NONE, + LAZY_SEND, NOT_CONSTRUCTOR, ASYNC, NOT_REPLY)) { + IPC::MessageWriter writer(*this); + IPC::WriteParam(&writer, aIPDLId); + } +}; + +static already_AddRefed<SharedMemory> NewSegment() { + return MakeAndAddRef<SharedMemoryBasic>(); +} + +static already_AddRefed<SharedMemory> CreateSegment(size_t aNBytes) { + if (!aNBytes) { + return nullptr; + } + RefPtr<SharedMemory> segment = NewSegment(); + if (!segment) { + return nullptr; + } + size_t size = SharedMemory::PageAlignedSize(aNBytes); + 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) { + if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) { + NS_ERROR("expected 'shmem created' message"); + return nullptr; + } + IPC::MessageReader reader(aDescriptor); + if (!ShmemCreated::ReadInfo(&reader, aId, aNBytes)) { + return nullptr; + } + RefPtr<SharedMemory> segment = NewSegment(); + if (!segment) { + return nullptr; + } + if (!segment->ReadHandle(&reader)) { + NS_ERROR("trying to open invalid handle"); + return nullptr; + } + reader.EndRead(); + if (!*aNBytes) { + return nullptr; + } + size_t size = SharedMemory::PageAlignedSize(*aNBytes); + if (!segment->Map(size)) { + return nullptr; + } + // close the handle to the segment after it is mapped + segment->CloseHandle(); + return segment.forget(); +} + +#if defined(DEBUG) + +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); +} + +void Shmem::AssertInvariants() const { + MOZ_ASSERT(mSegment, "null segment"); + MOZ_ASSERT(mData, "null data pointer"); + MOZ_ASSERT(mSize > 0, "invalid size"); + // if the segment isn't owned by the current process, these will + // trigger SIGSEGV + 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() { + AssertInvariants(); + + // When sending a non-unsafe shmem, remove read/write rights from the local + // mapping of the segment. + if (!mUnsafe) { + Protect(mSegment); + } +} + +#endif // if defined(DEBUG) + +Shmem::Shmem(SharedMemory* aSegment, id_t aId, size_t aSize, bool aUnsafe) + : mSegment(aSegment), mData(aSegment->memory()), mSize(aSize), mId(aId) { +#ifdef DEBUG + mUnsafe = aUnsafe; + Unprotect(mSegment); +#endif + + MOZ_RELEASE_ASSERT(mSegment->Size() >= mSize, + "illegal size in shared memory segment"); +} + +// static +already_AddRefed<Shmem::SharedMemory> Shmem::Alloc(size_t aNBytes) { + RefPtr<SharedMemory> segment = CreateSegment(aNBytes); + if (!segment) { + return nullptr; + } + + return segment.forget(); +} + +// static +already_AddRefed<Shmem::SharedMemory> Shmem::OpenExisting( + const IPC::Message& aDescriptor, id_t* aId, bool /*unused*/) { + size_t size; + RefPtr<SharedMemory> segment = ReadSegment(aDescriptor, aId, &size); + if (!segment) { + return nullptr; + } + + return segment.forget(); +} + +UniquePtr<IPC::Message> Shmem::MkCreatedMessage(int32_t routingId) { + AssertInvariants(); + + auto msg = MakeUnique<ShmemCreated>(routingId, mId, mSize); + 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(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); + WriteIPDLParam(aWriter, aActor, uint32_t(aParam.mSize)); +#ifdef DEBUG + WriteIPDLParam(aWriter, aActor, aParam.mUnsafe); +#endif + + aParam.RevokeRights(); + aParam.forget(); +} + +bool IPDLParamTraits<Shmem>::Read(IPC::MessageReader* aReader, + IProtocol* aActor, paramType* aResult) { + paramType::id_t id; + uint32_t size; + if (!ReadIPDLParam(aReader, aActor, &id) || + !ReadIPDLParam(aReader, aActor, &size)) { + return false; + } + + bool unsafe = false; +#ifdef DEBUG + if (!ReadIPDLParam(aReader, aActor, &unsafe)) { + return false; + } +#endif + + Shmem::SharedMemory* rawmem = aActor->LookupSharedMemory(id); + if (rawmem) { + if (size > rawmem->Size()) { + return false; + } + + *aResult = Shmem(rawmem, id, size, unsafe); + return true; + } + *aResult = Shmem(); + return true; +} + +} // namespace ipc +} // namespace mozilla |