tor-browser

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

SharedMemoryPlatform_mach.cpp (7317B)


      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 "SharedMemoryPlatform.h"
      8 
      9 #include <utility>
     10 
     11 #include <mach/vm_map.h>
     12 #include <mach/mach_port.h>
     13 #if defined(XP_IOS)
     14 #  include <mach/vm_map.h>
     15 #  define mach_vm_address_t vm_address_t
     16 #  define mach_vm_map vm_map
     17 #  define mach_vm_read vm_read
     18 #  define mach_vm_region_recurse vm_region_recurse_64
     19 #  define mach_vm_size_t vm_size_t
     20 #else
     21 #  include <mach/mach_vm.h>
     22 #endif
     23 #include <pthread.h>
     24 #include <sys/mman.h>  // mprotect
     25 #include <unistd.h>
     26 
     27 #if defined(XP_MACOSX) && defined(__x86_64__)
     28 #  include "prenv.h"
     29 #endif
     30 #include <prtypes.h>
     31 
     32 #include "mozilla/IntegerPrintfMacros.h"
     33 #include "mozilla/Printf.h"
     34 #include "nsDebug.h"
     35 
     36 #ifdef DEBUG
     37 #  define LOG_ERROR(str, args...)                                  \
     38    PR_BEGIN_MACRO                                                 \
     39    mozilla::SmprintfPointer msg = mozilla::Smprintf(str, ##args); \
     40    NS_WARNING(msg.get());                                         \
     41    PR_END_MACRO
     42 #else
     43 #  define LOG_ERROR(str, args...) \
     44    do { /* nothing */            \
     45    } while (0)
     46 #endif
     47 
     48 namespace mozilla::ipc::shared_memory {
     49 
     50 static inline void* toPointer(mach_vm_address_t aAddress) {
     51  return reinterpret_cast<void*>(static_cast<uintptr_t>(aAddress));
     52 }
     53 
     54 static inline mach_vm_address_t toVMAddress(void* aPointer) {
     55  return static_cast<mach_vm_address_t>(reinterpret_cast<uintptr_t>(aPointer));
     56 }
     57 
     58 static Maybe<PlatformHandle> CreateImpl(size_t aSize, bool aFreezable) {
     59  memory_object_size_t memoryObjectSize = round_page(aSize);
     60  PlatformHandle handle;
     61 
     62  kern_return_t kr =
     63      mach_make_memory_entry_64(mach_task_self(), &memoryObjectSize, 0,
     64                                MAP_MEM_NAMED_CREATE | VM_PROT_DEFAULT,
     65                                getter_Transfers(handle), MACH_PORT_NULL);
     66  if (kr != KERN_SUCCESS || memoryObjectSize < round_page(aSize)) {
     67    LOG_ERROR("Failed to make memory entry (%zu bytes). %s (%x)\n", aSize,
     68              mach_error_string(kr), kr);
     69    return Nothing();
     70  }
     71  return Some(std::move(handle));
     72 }
     73 
     74 bool Platform::Create(MutableHandle& aHandle, size_t aSize) {
     75  if (auto ph = CreateImpl(aSize, false)) {
     76    aHandle.mHandle = std::move(*ph);
     77    aHandle.SetSize(aSize);
     78    return true;
     79  }
     80  return false;
     81 }
     82 
     83 bool Platform::CreateFreezable(FreezableHandle& aHandle, size_t aSize) {
     84  if (auto ph = CreateImpl(aSize, true)) {
     85    aHandle.mHandle = std::move(*ph);
     86    aHandle.SetSize(aSize);
     87    return true;
     88  }
     89  return false;
     90 }
     91 
     92 PlatformHandle Platform::CloneHandle(const PlatformHandle& aHandle) {
     93  return mozilla::RetainMachSendRight(aHandle.get());
     94 }
     95 
     96 static Maybe<void*> MapMemory(uint64_t aOffset, size_t aSize,
     97                              void* aFixedAddress,
     98                              const mozilla::UniqueMachSendRight& aPort,
     99                              bool aReadOnly) {
    100  kern_return_t kr;
    101  mach_vm_address_t address = toVMAddress(aFixedAddress);
    102 
    103  vm_prot_t vmProtection = VM_PROT_READ;
    104  if (!aReadOnly) {
    105    vmProtection |= VM_PROT_WRITE;
    106  }
    107 
    108  kr = mach_vm_map(mach_task_self(), &address, round_page(aSize), 0,
    109                   aFixedAddress ? VM_FLAGS_FIXED : VM_FLAGS_ANYWHERE,
    110                   aPort.get(), aOffset, false, vmProtection, vmProtection,
    111                   VM_INHERIT_NONE);
    112  if (kr != KERN_SUCCESS) {
    113    if (!aFixedAddress) {
    114      LOG_ERROR(
    115          "Failed to map shared memory (%zu bytes) into %x, port %x. %s (%x)\n",
    116          aSize, mach_task_self(), mach_port_t(aPort.get()),
    117          mach_error_string(kr), kr);
    118    }
    119    return Nothing();
    120  }
    121 
    122  if (aFixedAddress && aFixedAddress != toPointer(address)) {
    123    kr = vm_deallocate(mach_task_self(), address, aSize);
    124    if (kr != KERN_SUCCESS) {
    125      LOG_ERROR(
    126          "Failed to unmap shared memory at unsuitable address "
    127          "(%zu bytes) from %x, port %x. %s (%x)\n",
    128          aSize, mach_task_self(), mach_port_t(aPort.get()),
    129          mach_error_string(kr), kr);
    130    }
    131    return Nothing();
    132  }
    133 
    134  return Some(toPointer(address));
    135 }
    136 
    137 bool Platform::Freeze(FreezableHandle& aHandle) {
    138  memory_object_size_t memoryObjectSize = round_page(aHandle.Size());
    139 
    140  mozilla::UniqueMachSendRight port;
    141 
    142  // Temporarily map memory (as readonly) to get an address.
    143  auto memory = MapMemory(0, memoryObjectSize, nullptr, aHandle.mHandle, true);
    144  if (!memory) {
    145    return false;
    146  }
    147 
    148  kern_return_t kr = mach_make_memory_entry_64(
    149      mach_task_self(), &memoryObjectSize,
    150      static_cast<memory_object_offset_t>(reinterpret_cast<uintptr_t>(*memory)),
    151      VM_PROT_READ, getter_Transfers(port), MACH_PORT_NULL);
    152 
    153  // Immediately try to deallocate, regardless of success.
    154  {
    155    kern_return_t kr =
    156        vm_deallocate(mach_task_self(), toVMAddress(*memory), memoryObjectSize);
    157    if (kr != KERN_SUCCESS) {
    158      LOG_ERROR("Failed to deallocate shared memory. %s (%x)\n",
    159                mach_error_string(kr), kr);
    160    }
    161  }
    162 
    163  if (kr != KERN_SUCCESS || memoryObjectSize < round_page(aHandle.Size())) {
    164    LOG_ERROR("Failed to make memory entry (%llu bytes). %s (%x)\n",
    165              aHandle.Size(), mach_error_string(kr), kr);
    166    return false;
    167  }
    168 
    169  aHandle.mHandle = std::move(port);
    170 
    171  return true;
    172 }
    173 
    174 Maybe<void*> Platform::Map(const HandleBase& aHandle, uint64_t aOffset,
    175                           size_t aSize, void* aFixedAddress, bool aReadOnly) {
    176  return MapMemory(aOffset, aSize, aFixedAddress, aHandle.mHandle, aReadOnly);
    177 }
    178 
    179 void Platform::Unmap(void* aMemory, size_t aSize) {
    180  vm_address_t vm_address = toVMAddress(aMemory);
    181  kern_return_t kr =
    182      vm_deallocate(mach_task_self(), vm_address, round_page(aSize));
    183  if (kr != KERN_SUCCESS) {
    184    LOG_ERROR("Failed to deallocate shared memory. %s (%x)\n",
    185              mach_error_string(kr), kr);
    186  }
    187 }
    188 
    189 bool Platform::Protect(char* aAddr, size_t aSize, Access aAccess) {
    190  int flags = PROT_NONE;
    191  if (aAccess & AccessRead) flags |= PROT_READ;
    192  if (aAccess & AccessWrite) flags |= PROT_WRITE;
    193 
    194  return 0 == mprotect(aAddr, aSize, flags);
    195 }
    196 
    197 void* Platform::FindFreeAddressSpace(size_t aSize) {
    198  mach_vm_address_t address = 0;
    199  aSize = round_page(aSize);
    200  if (mach_vm_map(mach_task_self(), &address, aSize, 0, VM_FLAGS_ANYWHERE,
    201                  MEMORY_OBJECT_NULL, 0, false, VM_PROT_NONE, VM_PROT_NONE,
    202                  VM_INHERIT_NONE) != KERN_SUCCESS ||
    203      vm_deallocate(mach_task_self(), address, aSize) != KERN_SUCCESS) {
    204    return nullptr;
    205  }
    206  return toPointer(address);
    207 }
    208 
    209 size_t Platform::PageSize() {
    210 #if defined(XP_MACOSX) && defined(__x86_64__)
    211  static std::atomic<size_t> sPageSizeOverride = 0;
    212 
    213  if (sPageSizeOverride == 0) {
    214    if (PR_GetEnv("MOZ_SHMEM_PAGESIZE_16K")) {
    215      sPageSizeOverride = 16 * 1024;
    216    } else {
    217      sPageSizeOverride = sysconf(_SC_PAGESIZE);
    218    }
    219  }
    220  return sPageSizeOverride;
    221 #else
    222  return sysconf(_SC_PAGESIZE);
    223 #endif
    224 }
    225 
    226 size_t Platform::AllocationGranularity() { return PageSize(); }
    227 
    228 bool Platform::IsSafeToMap(const PlatformHandle&) { return true; }
    229 
    230 }  // namespace mozilla::ipc::shared_memory