tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Shmem.cpp (6140B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "Shmem.h"
      8 
      9 #include "ProtocolUtils.h"
     10 #include "ShmemMessageUtils.h"
     11 #include "chrome/common/ipc_message_utils.h"
     12 #include "mozilla/ipc/SharedMemoryHandle.h"
     13 
     14 namespace mozilla {
     15 namespace ipc {
     16 
     17 class ShmemCreated : public IPC::Message {
     18 private:
     19  typedef Shmem::id_t id_t;
     20 
     21 public:
     22  ShmemCreated(routeid_t routingId, id_t aIPDLId,
     23               MutableSharedMemoryHandle&& aHandle)
     24      : IPC::Message(
     25            routingId, SHMEM_CREATED_MESSAGE_TYPE, 0,
     26            HeaderFlags(NESTED_INSIDE_CPOW, CONTROL_PRIORITY, COMPRESSION_NONE,
     27                        LAZY_SEND, NOT_CONSTRUCTOR, ASYNC, NOT_REPLY)) {
     28    IPC::MessageWriter writer(*this);
     29    IPC::WriteParam(&writer, aIPDLId);
     30    IPC::WriteParam(&writer, std::move(aHandle));
     31  }
     32 
     33  static bool ReadInfo(IPC::MessageReader* aReader, id_t* aIPDLId,
     34                       MutableSharedMemoryHandle* aHandle) {
     35    return IPC::ReadParam(aReader, aIPDLId) && IPC::ReadParam(aReader, aHandle);
     36  }
     37 
     38  void Log(const std::string& aPrefix, FILE* aOutf) const {
     39    fputs("(special ShmemCreated msg)", aOutf);
     40  }
     41 };
     42 
     43 class ShmemDestroyed : public IPC::Message {
     44 private:
     45  typedef Shmem::id_t id_t;
     46 
     47 public:
     48  ShmemDestroyed(routeid_t routingId, id_t aIPDLId)
     49      : IPC::Message(
     50            routingId, SHMEM_DESTROYED_MESSAGE_TYPE, 0,
     51            HeaderFlags(NOT_NESTED, NORMAL_PRIORITY, COMPRESSION_NONE,
     52                        LAZY_SEND, NOT_CONSTRUCTOR, ASYNC, NOT_REPLY)) {
     53    IPC::MessageWriter writer(*this);
     54    IPC::WriteParam(&writer, aIPDLId);
     55  }
     56 };
     57 
     58 #if defined(DEBUG)
     59 
     60 static void Protect(const Shmem::Segment* aSegment) {
     61  MOZ_ASSERT(aSegment && *aSegment, "null segment");
     62  shared_memory::LocalProtect(aSegment->DataAs<char>(), aSegment->Size(),
     63                              shared_memory::AccessNone);
     64 }
     65 
     66 static void Unprotect(const Shmem::Segment* aSegment) {
     67  MOZ_ASSERT(aSegment && *aSegment, "null segment");
     68  shared_memory::LocalProtect(aSegment->DataAs<char>(), aSegment->Size(),
     69                              shared_memory::AccessReadWrite);
     70 }
     71 
     72 void Shmem::AssertInvariants() const {
     73  MOZ_ASSERT(mSegment, "null segment");
     74  MOZ_ASSERT(mData, "null data pointer");
     75  MOZ_ASSERT(mSize > 0, "invalid size");
     76  // if the segment isn't owned by the current process, these will
     77  // trigger SIGSEGV
     78  char checkMappingFront = *reinterpret_cast<char*>(mData);
     79  char checkMappingBack = *(reinterpret_cast<char*>(mData) + mSize - 1);
     80 
     81  // avoid "unused" warnings for these variables:
     82  (void)checkMappingFront;
     83  (void)checkMappingBack;
     84 }
     85 
     86 void Shmem::RevokeRights() {
     87  AssertInvariants();
     88 
     89  // When sending a non-unsafe shmem, remove read/write rights from the local
     90  // mapping of the segment.
     91  if (!mUnsafe) {
     92    Protect(mSegment);
     93  }
     94 }
     95 
     96 #endif  // if defined(DEBUG)
     97 
     98 Shmem::Shmem(RefPtr<Segment>&& aSegment, id_t aId, size_t aSize, bool aUnsafe)
     99    : mSegment(std::move(aSegment)),
    100      mData(mSegment->Address()),
    101      mSize(aSize),
    102      mId(aId) {
    103 #ifdef DEBUG
    104  mUnsafe = aUnsafe;
    105  Unprotect(mSegment);
    106 #endif
    107 
    108  MOZ_RELEASE_ASSERT(mSegment->Size() >= mSize,
    109                     "illegal size in shared memory segment");
    110 }
    111 
    112 Shmem::Builder::Builder(size_t aSize) : mSize(aSize) {
    113  if (!aSize) {
    114    return;
    115  }
    116  size_t pageAlignedSize = shared_memory::PageAlignedSize(aSize);
    117  mHandle = shared_memory::Create(pageAlignedSize);
    118  if (!mHandle) {
    119    return;
    120  }
    121  auto mapping = mHandle.Map();
    122  if (!mapping) {
    123    return;
    124  }
    125  mSegment = MakeAndAddRef<Segment>(std::move(mapping));
    126 }
    127 
    128 std::tuple<UniquePtr<IPC::Message>, Shmem> Shmem::Builder::Build(
    129    id_t aId, bool aUnsafe, IPC::Message::routeid_t aRoutingId) {
    130  Shmem shmem(std::move(mSegment), aId, mSize, aUnsafe);
    131  shmem.AssertInvariants();
    132  MOZ_ASSERT(mHandle, "null shmem handle");
    133 
    134  auto msg = MakeUnique<ShmemCreated>(aRoutingId, aId, std::move(mHandle));
    135  return std::make_tuple(std::move(msg), std::move(shmem));
    136 }
    137 
    138 // static
    139 already_AddRefed<Shmem::Segment> Shmem::OpenExisting(
    140    const IPC::Message& aDescriptor, id_t* aId, bool /*unused*/) {
    141  if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) {
    142    NS_ERROR("expected 'shmem created' message");
    143    return nullptr;
    144  }
    145  MutableSharedMemoryHandle handle;
    146  IPC::MessageReader reader(aDescriptor);
    147  if (!ShmemCreated::ReadInfo(&reader, aId, &handle)) {
    148    return nullptr;
    149  }
    150  reader.EndRead();
    151  if (!handle) {
    152    return nullptr;
    153  }
    154 
    155  auto mapping = handle.Map();
    156  if (!mapping) {
    157    return nullptr;
    158  }
    159  return MakeAndAddRef<Shmem::Segment>(std::move(mapping));
    160 }
    161 
    162 UniquePtr<IPC::Message> Shmem::MkDestroyedMessage(
    163    IPC::Message::routeid_t routingId) {
    164  AssertInvariants();
    165  return MakeUnique<ShmemDestroyed>(routingId, mId);
    166 }
    167 
    168 }  // namespace ipc
    169 }  // namespace mozilla
    170 
    171 namespace IPC {
    172 
    173 void ParamTraits<mozilla::ipc::Shmem>::Write(IPC::MessageWriter* aWriter,
    174                                             paramType&& aParam) {
    175  WriteParam(aWriter, aParam.mId);
    176  WriteParam(aWriter, uint32_t(aParam.mSize));
    177 #ifdef DEBUG
    178  WriteParam(aWriter, aParam.mUnsafe);
    179 #endif
    180 
    181  aParam.RevokeRights();
    182  aParam.forget();
    183 }
    184 
    185 bool ParamTraits<mozilla::ipc::Shmem>::Read(IPC::MessageReader* aReader,
    186                                            paramType* aResult) {
    187  if (!aReader->GetActor()) {
    188    return false;
    189  }
    190 
    191  paramType::id_t id;
    192  uint32_t size;
    193  if (!ReadParam(aReader, &id) || !ReadParam(aReader, &size)) {
    194    return false;
    195  }
    196 
    197  bool unsafe = false;
    198 #ifdef DEBUG
    199  if (!ReadParam(aReader, &unsafe)) {
    200    return false;
    201  }
    202 #endif
    203 
    204  auto* segment = aReader->GetActor()->LookupSharedMemory(id);
    205  if (segment) {
    206    if (size > segment->Size()) {
    207      return false;
    208    }
    209 
    210    *aResult = mozilla::ipc::Shmem(segment, id, size, unsafe);
    211    return true;
    212  }
    213  *aResult = mozilla::ipc::Shmem();
    214  return true;
    215 }
    216 
    217 }  // namespace IPC