tor-browser

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

SharedMemoryMapping.cpp (6243B)


      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 /* This source code was derived from Chromium code, and as such is also subject
      8 * to the [Chromium license](ipc/chromium/src/LICENSE). */
      9 
     10 #include "mozilla/ipc/SharedMemoryHandle.h"
     11 #include "mozilla/ipc/SharedMemoryMapping.h"
     12 #include "SharedMemoryPlatform.h"
     13 
     14 #include "mozilla/Atomics.h"
     15 #include "mozilla/CheckedInt.h"
     16 #include "nsIMemoryReporter.h"
     17 
     18 #ifdef FUZZING
     19 #  include "mozilla/ipc/SharedMemoryFuzzer.h"
     20 #endif
     21 
     22 namespace mozilla::ipc::shared_memory {
     23 
     24 class MappingReporter final : public nsIMemoryReporter {
     25  ~MappingReporter() = default;
     26 
     27 public:
     28  static Atomic<size_t> mapped;
     29 
     30  NS_DECL_THREADSAFE_ISUPPORTS
     31 
     32  NS_IMETHOD
     33  CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
     34                 bool aAnonymize) override {
     35    MOZ_COLLECT_REPORT(
     36        "shmem-mapped", KIND_OTHER, UNITS_BYTES, mapped,
     37        "Memory shared with other processes that is mapped into the address "
     38        "space.");
     39 
     40    return NS_OK;
     41  }
     42 };
     43 
     44 Atomic<size_t> MappingReporter::mapped;
     45 
     46 NS_IMPL_ISUPPORTS(MappingReporter, nsIMemoryReporter)
     47 
     48 static void RegisterMappingMemoryReporter() {
     49  static Atomic<bool> registered;
     50  if (registered.compareExchange(false, true)) {
     51    RegisterStrongMemoryReporter(new MappingReporter());
     52  }
     53 }
     54 
     55 MappingBase::MappingBase() = default;
     56 
     57 MappingBase& MappingBase::operator=(MappingBase&& aOther) {
     58  // Swap members with `aOther`, and unmap that mapping.
     59  std::swap(aOther.mMemory, mMemory);
     60  std::swap(aOther.mSize, mSize);
     61  aOther.Unmap();
     62  return *this;
     63 }
     64 
     65 void* MappingBase::Address() const {
     66 #ifdef FUZZING
     67  return SharedMemoryFuzzer::MutateSharedMemory(mMemory, mSize);
     68 #else
     69  return mMemory;
     70 #endif
     71 }
     72 
     73 bool MappingBase::Map(const HandleBase& aHandle, void* aFixedAddress,
     74                      bool aReadOnly) {
     75  // Invalid handles will fail and result in an invalid mapping.
     76  if (!aHandle) {
     77    return false;
     78  }
     79  // Verify that the handle size can be stored as a mapping size first
     80  // (otherwise it won't be possible to map in the address space and the Map
     81  // call will fail).
     82  CheckedInt<size_t> checkedSize(aHandle.Size());
     83  if (!checkedSize.isValid()) {
     84    MOZ_LOG_FMT(gSharedMemoryLog, LogLevel::Error,
     85                "handle size to map exceeds address space size");
     86    return false;
     87  }
     88 
     89  return MapSubregion(aHandle, /* aOffset */ 0, checkedSize.value(),
     90                      aFixedAddress, aReadOnly);
     91 }
     92 
     93 bool MappingBase::MapSubregion(const HandleBase& aHandle, uint64_t aOffset,
     94                               size_t aSize, void* aFixedAddress,
     95                               bool aReadOnly) {
     96  CheckedInt<uint64_t> endOffset(aOffset);
     97  endOffset += aSize;
     98  if (!endOffset.isValid() || endOffset.value() > aHandle.Size()) {
     99    MOZ_LOG_FMT(gSharedMemoryLog, LogLevel::Error,
    100                "cannot map region exceeding aHandle.Size()");
    101    return false;
    102  }
    103 
    104  RegisterMappingMemoryReporter();
    105 
    106  if (auto mem =
    107          Platform::Map(aHandle, aOffset, aSize, aFixedAddress, aReadOnly)) {
    108    mMemory = *mem;
    109    mSize = aSize;
    110    MappingReporter::mapped += mSize;
    111    return true;
    112  }
    113  return false;
    114 }
    115 
    116 void MappingBase::Unmap() {
    117  if (IsValid()) {
    118    Platform::Unmap(mMemory, mSize);
    119 
    120    MOZ_ASSERT(MappingReporter::mapped >= mSize,
    121               "Can't unmap more than mapped");
    122    MappingReporter::mapped -= mSize;
    123  }
    124  mMemory = nullptr;
    125  mSize = 0;
    126 }
    127 
    128 std::tuple<void*, size_t> MappingBase::Release() && {
    129  // NOTE: this doesn't reduce gShmemMapped since it _is_ still mapped memory
    130  // (and will be until the process terminates).
    131  return std::make_tuple(std::exchange(mMemory, nullptr),
    132                         std::exchange(mSize, 0));
    133 }
    134 
    135 MutableOrReadOnlyMapping::Mapping(const MutableHandle& aHandle,
    136                                  void* aFixedAddress)
    137    : mReadOnly(false) {
    138  Map(aHandle, aFixedAddress, false);
    139 }
    140 
    141 MutableOrReadOnlyMapping::Mapping(const ReadOnlyHandle& aHandle,
    142                                  void* aFixedAddress)
    143    : mReadOnly(true) {
    144  Map(aHandle, aFixedAddress, true);
    145 }
    146 
    147 MutableOrReadOnlyMapping::Mapping(MutableMapping&& aMapping)
    148    : MappingData(std::move(aMapping)), mReadOnly(false) {}
    149 
    150 MutableOrReadOnlyMapping::Mapping(ReadOnlyMapping&& aMapping)
    151    : MappingData(std::move(aMapping)), mReadOnly(true) {}
    152 
    153 // We still store the handle if `Map` fails: the user may want to get it back
    154 // (for instance, if fixed-address mapping doesn't work they may try mapping
    155 // without one).
    156 FreezableMapping::Mapping(FreezableHandle&& aHandle, void* aFixedAddress)
    157    : mHandle(std::move(aHandle)) {
    158  Map(mHandle, aFixedAddress, false);
    159 }
    160 
    161 FreezableMapping::Mapping(FreezableHandle&& aHandle, uint64_t aOffset,
    162                          size_t aSize, void* aFixedAddress)
    163    : mHandle(std::move(aHandle)) {
    164  MapSubregion(mHandle, aOffset, aSize, aFixedAddress, false);
    165 }
    166 
    167 ReadOnlyHandle FreezableMapping::Freeze() && {
    168  return std::move(*this).Unmap().Freeze();
    169 }
    170 
    171 std::tuple<ReadOnlyHandle, MutableMapping>
    172 FreezableMapping::FreezeWithMutableMapping() && {
    173  auto handle = std::move(mHandle);
    174  return std::make_tuple(std::move(handle).Freeze(),
    175                         ConvertMappingTo<Type::Mutable>(std::move(*this)));
    176 }
    177 
    178 FreezableHandle FreezableMapping::Unmap() && {
    179  auto handle = std::move(mHandle);
    180  *this = nullptr;
    181  return handle;
    182 }
    183 
    184 bool LocalProtect(char* aAddr, size_t aSize, Access aAccess) {
    185  return Platform::Protect(aAddr, aSize, aAccess);
    186 }
    187 
    188 void* FindFreeAddressSpace(size_t aSize) {
    189  return Platform::FindFreeAddressSpace(aSize);
    190 }
    191 
    192 size_t SystemPageSize() { return Platform::PageSize(); }
    193 
    194 size_t SystemAllocationGranularity() {
    195  return Platform::AllocationGranularity();
    196 }
    197 
    198 size_t PageAlignedSize(size_t aMinimum) {
    199  const size_t pageSize = Platform::PageSize();
    200  size_t nPagesNeeded = size_t(ceil(double(aMinimum) / double(pageSize)));
    201  return pageSize * nPagesNeeded;
    202 }
    203 
    204 }  // namespace mozilla::ipc::shared_memory