tor-browser

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

SharedMemoryMapping.h (9253B)


      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 #ifndef mozilla_ipc_SharedMemoryMapping_h
      8 #define mozilla_ipc_SharedMemoryMapping_h
      9 
     10 #include <tuple>
     11 #include <type_traits>
     12 #include <utility>
     13 #include "mozilla/Assertions.h"
     14 #include "mozilla/Span.h"
     15 #include "SharedMemoryHandle.h"
     16 
     17 namespace mozilla::ipc {
     18 
     19 namespace shared_memory {
     20 
     21 /**
     22 * A leaked memory mapping.
     23 *
     24 * This memory will never be unmapped.
     25 */
     26 template <Type T>
     27 struct LeakedMapping : Span<uint8_t> {
     28  using Span::Span;
     29 };
     30 
     31 template <>
     32 struct LeakedMapping<Type::ReadOnly> : Span<const uint8_t> {
     33  using Span::Span;
     34 };
     35 
     36 using LeakedMutableMapping = LeakedMapping<Type::Mutable>;
     37 using LeakedReadOnlyMapping = LeakedMapping<Type::ReadOnly>;
     38 
     39 class MappingBase {
     40 public:
     41  /**
     42   * The size of the mapping.
     43   */
     44  size_t Size() const { return mSize; }
     45 
     46  /**
     47   * The pointer to the mapping in memory.
     48   */
     49  void* Address() const;
     50 
     51  /**
     52   * Whether this shared memory mapping is valid.
     53   */
     54  bool IsValid() const { return (bool)*this; }
     55 
     56  /**
     57   * Whether this shared memory mapping is valid.
     58   */
     59  explicit operator bool() const { return (bool)mMemory; }
     60 
     61 protected:
     62  /**
     63   * Create an empty Mapping.
     64   */
     65  MappingBase();
     66  MOZ_IMPLICIT MappingBase(std::nullptr_t) {}
     67  ~MappingBase() { Unmap(); }
     68 
     69  /**
     70   * Mappings are movable (but not copyable).
     71   */
     72  MappingBase(MappingBase&& aOther)
     73      : mMemory(std::exchange(aOther.mMemory, nullptr)),
     74        mSize(std::exchange(aOther.mSize, 0)) {}
     75 
     76  MappingBase& operator=(MappingBase&& aOther);
     77 
     78  MappingBase(const MappingBase&) = delete;
     79  MappingBase& operator=(const MappingBase&) = delete;
     80 
     81  bool Map(const HandleBase& aHandle, void* aFixedAddress, bool aReadOnly);
     82  bool MapSubregion(const HandleBase& aHandle, uint64_t aOffset, size_t aSize,
     83                    void* aFixedAddress, bool aReadOnly);
     84  void Unmap();
     85 
     86  template <Type T, Type S>
     87  static Mapping<T> ConvertMappingTo(Mapping<S>&& from) {
     88    Mapping<T> to;
     89    static_cast<MappingBase&>(to) = std::move(from);
     90    return to;
     91  }
     92 
     93  std::tuple<void*, size_t> Release() &&;
     94 
     95 private:
     96  void* mMemory = nullptr;
     97  size_t mSize = 0;
     98 };
     99 
    100 template <bool CONST_MEMORY>
    101 struct MappingData : MappingBase {
    102 private:
    103  template <typename T>
    104  using DataType =
    105      std::conditional_t<CONST_MEMORY, std::add_const_t<std::remove_const_t<T>>,
    106                         T>;
    107 
    108 protected:
    109  MappingData() = default;
    110  explicit MappingData(MappingBase&& aOther) : MappingBase(std::move(aOther)) {}
    111 
    112 public:
    113  /**
    114   * Get a pointer to the data in the mapping as a type T.
    115   *
    116   * The mapping data must meet the alignment requirements of @p T.
    117   *
    118   * @tparam T The type of data in the mapping.
    119   *
    120   * @return A pointer of type @p T*.
    121   */
    122  template <typename T>
    123  DataType<T>* DataAs() const {
    124    MOZ_ASSERT((reinterpret_cast<uintptr_t>(Address()) % alignof(T)) == 0,
    125               "memory map does not meet alignment requirements of type");
    126    return static_cast<DataType<T>*>(Address());
    127  }
    128 
    129  /**
    130   * Get a `Span<T>` over the mapping.
    131   *
    132   * The mapping data must meet the alignment requirements of @p T.
    133   *
    134   * @tparam T The type of data in the mapping.
    135   *
    136   * @return A span of type @p T covering as much of the mapping as possible.
    137   */
    138  template <typename T>
    139  Span<DataType<T>> DataAsSpan() const {
    140    return {DataAs<T>(), Size() / sizeof(T)};
    141  }
    142 };
    143 
    144 /**
    145 * A shared memory mapping.
    146 */
    147 template <Type T>
    148 struct Mapping<T> : MappingData<T == Type::ReadOnly> {
    149  /**
    150   * Create an empty Mapping.
    151   */
    152  Mapping() = default;
    153  MOZ_IMPLICIT Mapping(std::nullptr_t) {}
    154 
    155  explicit Mapping(const Handle<T>& aHandle, void* aFixedAddress = nullptr) {
    156    MappingBase::Map(aHandle, aFixedAddress, T == Type::ReadOnly);
    157  }
    158  Mapping(const Handle<T>& aHandle, uint64_t aOffset, size_t aSize,
    159          void* aFixedAddress = nullptr) {
    160    MappingBase::MapSubregion(aHandle, aOffset, aSize, aFixedAddress,
    161                              T == Type::ReadOnly);
    162  }
    163 
    164  /**
    165   * Leak this mapping's memory.
    166   *
    167   * This will cause the memory to be mapped until the process exits.
    168   */
    169  LeakedMapping<T> Release() && {
    170    auto [ptr, size] = std::move(*this).MappingBase::Release();
    171    return LeakedMapping<T>{
    172        static_cast<typename LeakedMapping<T>::pointer>(ptr), size};
    173  }
    174 };
    175 
    176 /**
    177 * A shared memory mapping which has runtime-stored mutability.
    178 */
    179 template <>
    180 struct Mapping<Type::MutableOrReadOnly> : MappingData<true> {
    181  /**
    182   * Create an empty MutableOrReadOnlyMapping.
    183   */
    184  Mapping() = default;
    185  MOZ_IMPLICIT Mapping(std::nullptr_t) {}
    186 
    187  explicit Mapping(const ReadOnlyHandle& aHandle,
    188                   void* aFixedAddress = nullptr);
    189  explicit Mapping(const MutableHandle& aHandle, void* aFixedAddress = nullptr);
    190  MOZ_IMPLICIT Mapping(ReadOnlyMapping&& aMapping);
    191  MOZ_IMPLICIT Mapping(MutableMapping&& aMapping);
    192 
    193  /**
    194   * Return whether the mapping is read-only.
    195   */
    196  bool IsReadOnly() const { return mReadOnly; }
    197 
    198 private:
    199  bool mReadOnly = false;
    200 };
    201 
    202 /**
    203 * A freezable shared memory mapping.
    204 */
    205 template <>
    206 struct Mapping<Type::Freezable> : MappingData<false> {
    207  /**
    208   * Create an empty FreezableMapping.
    209   */
    210  Mapping() = default;
    211  MOZ_IMPLICIT Mapping(std::nullptr_t) {}
    212 
    213  /**
    214   * Freezable mappings take ownership of a handle to ensure there is only one
    215   * writeable mapping at a time.
    216   *
    217   * Call `Unmap()` to get the handle back.
    218   */
    219  explicit Mapping(FreezableHandle&& aHandle, void* aFixedAddress = nullptr);
    220  Mapping(FreezableHandle&& aHandle, uint64_t aOffset, size_t aSize,
    221          void* aFixedAddress = nullptr);
    222 
    223  /**
    224   * Freeze the shared memory region.
    225   */
    226  ReadOnlyHandle Freeze() &&;
    227 
    228  /**
    229   * Freeze the shared memory region.
    230   *
    231   * The returned Mapping will still be valid and writable until it is deleted,
    232   * however no new writable mappings can be created.
    233   */
    234  std::tuple<ReadOnlyHandle, MutableMapping> FreezeWithMutableMapping() &&;
    235 
    236  /**
    237   * Unmap the shared memory, returning the freezable handle.
    238   *
    239   * It is only necessary to call this if you need to get the FreezableHandle
    240   * back.
    241   */
    242  FreezableHandle Unmap() &&;
    243 
    244 protected:
    245  FreezableHandle mHandle;
    246 };
    247 
    248 template <Type T>
    249 struct Mapping<T, true> : public Mapping<T> {
    250  Mapping() {}
    251  MOZ_IMPLICIT Mapping(std::nullptr_t) : Mapping<T>(nullptr) {}
    252 
    253  explicit Mapping(shared_memory::Handle<T>&& aHandle,
    254                   void* aFixedAddress = nullptr)
    255      : Mapping<T>(aHandle, aFixedAddress), mHandle(std::move(aHandle)) {}
    256 
    257  const shared_memory::Handle<T>& Handle() const { return mHandle; };
    258 
    259  std::tuple<shared_memory::Handle<T>, Mapping<T>> Split() && {
    260    auto handle = std::move(mHandle);
    261    return std::make_tuple(std::move(handle), std::move(*this));
    262  }
    263 
    264 private:
    265  shared_memory::Handle<T> mHandle;
    266 };
    267 
    268 // To uphold the guarantees of freezable mappings, we do not allow access to the
    269 // handle (and since this should never be used in this way, we make it a useless
    270 // type).
    271 template <>
    272 struct Mapping<Type::Freezable, true>;
    273 
    274 // The access level permitted for memory protection.
    275 enum Access {
    276  AccessNone = 0,
    277  AccessRead = 1 << 0,
    278  AccessWrite = 1 << 1,
    279  AccessReadWrite = AccessRead | AccessWrite,
    280 };
    281 
    282 /**
    283 * Protect the given memory region.
    284 *
    285 * This protection extends only to the local memory mapping. It doesn't change
    286 * the permissions of other mappings nor the associated handle.
    287 *
    288 * @param aAddr The address at the beginning of the memory region.
    289 * @param aSize The size of the region to protect.
    290 * @param aAccess The access level to allow.
    291 *
    292 * @returns Whether protection was successful.
    293 */
    294 bool LocalProtect(char* aAddr, size_t aSize, Access aAccess);
    295 
    296 /**
    297 * Find a region of free memory.
    298 *
    299 * @param aSize The size of the region to locate.
    300 *
    301 * @returns The start of the memory region, or nullptr on error.
    302 */
    303 void* FindFreeAddressSpace(size_t aSize);
    304 
    305 /**
    306 * Get the system page size.
    307 */
    308 size_t SystemPageSize();
    309 
    310 /**
    311 * Get the system allocation granularity.
    312 *
    313 * This may be distinct from the page size, and controls the required
    314 * alignment for fixed mapping addresses and shared memory offsets.
    315 */
    316 size_t SystemAllocationGranularity();
    317 
    318 /**
    319 * Return a size which is page-aligned and can fit at least `minimum` bytes.
    320 *
    321 * @param aMinimum The minimum number of bytes required.
    322 *
    323 * @returns The page-aligned size that can hold `minimum` bytes.
    324 */
    325 size_t PageAlignedSize(size_t aMinimum);
    326 
    327 }  // namespace shared_memory
    328 
    329 using SharedMemoryMapping = shared_memory::MutableMapping;
    330 using ReadOnlySharedMemoryMapping = shared_memory::ReadOnlyMapping;
    331 using MutableOrReadOnlySharedMemoryMapping =
    332    shared_memory::MutableOrReadOnlyMapping;
    333 using FreezableSharedMemoryMapping = shared_memory::FreezableMapping;
    334 
    335 using SharedMemoryMappingWithHandle = shared_memory::MutableMappingWithHandle;
    336 using ReadOnlySharedMemoryMappingWithHandle =
    337    shared_memory::ReadOnlyMappingWithHandle;
    338 
    339 }  // namespace mozilla::ipc
    340 
    341 #endif