Home | History | Annotate | Download | only in memory
      1 // Copyright 2018 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/memory/platform_shared_memory_region.h"
      6 
      7 #include <mach/mach_vm.h>
      8 
      9 #include "base/mac/mach_logging.h"
     10 #include "base/mac/scoped_mach_vm.h"
     11 #include "base/numerics/checked_math.h"
     12 #include "build/build_config.h"
     13 
     14 #if defined(OS_IOS)
     15 #error "MacOS only - iOS uses platform_shared_memory_region_posix.cc"
     16 #endif
     17 
     18 namespace base {
     19 namespace subtle {
     20 
     21 // static
     22 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
     23     mac::ScopedMachSendRight handle,
     24     Mode mode,
     25     size_t size,
     26     const UnguessableToken& guid) {
     27   if (!handle.is_valid())
     28     return {};
     29 
     30   if (size == 0)
     31     return {};
     32 
     33   if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
     34     return {};
     35 
     36   CHECK(
     37       CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size));
     38 
     39   return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid);
     40 }
     41 
     42 mach_port_t PlatformSharedMemoryRegion::GetPlatformHandle() const {
     43   return handle_.get();
     44 }
     45 
     46 bool PlatformSharedMemoryRegion::IsValid() const {
     47   return handle_.is_valid();
     48 }
     49 
     50 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const {
     51   if (!IsValid())
     52     return {};
     53 
     54   CHECK_NE(mode_, Mode::kWritable)
     55       << "Duplicating a writable shared memory region is prohibited";
     56 
     57   // Increment the ref count.
     58   kern_return_t kr = mach_port_mod_refs(mach_task_self(), handle_.get(),
     59                                         MACH_PORT_RIGHT_SEND, 1);
     60   if (kr != KERN_SUCCESS) {
     61     MACH_DLOG(ERROR, kr) << "mach_port_mod_refs";
     62     return {};
     63   }
     64 
     65   return PlatformSharedMemoryRegion(mac::ScopedMachSendRight(handle_.get()),
     66                                     mode_, size_, guid_);
     67 }
     68 
     69 bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
     70   return ConvertToReadOnly(nullptr);
     71 }
     72 
     73 bool PlatformSharedMemoryRegion::ConvertToReadOnly(void* mapped_addr) {
     74   if (!IsValid())
     75     return false;
     76 
     77   CHECK_EQ(mode_, Mode::kWritable)
     78       << "Only writable shared memory region can be converted to read-only";
     79 
     80   mac::ScopedMachSendRight handle_copy(handle_.release());
     81 
     82   void* temp_addr = mapped_addr;
     83   mac::ScopedMachVM scoped_memory;
     84   if (!temp_addr) {
     85     // Intentionally lower current prot and max prot to |VM_PROT_READ|.
     86     kern_return_t kr = mach_vm_map(
     87         mach_task_self(), reinterpret_cast<mach_vm_address_t*>(&temp_addr),
     88         size_, 0, VM_FLAGS_ANYWHERE, handle_copy.get(), 0, FALSE, VM_PROT_READ,
     89         VM_PROT_READ, VM_INHERIT_NONE);
     90     if (kr != KERN_SUCCESS) {
     91       MACH_DLOG(ERROR, kr) << "mach_vm_map";
     92       return false;
     93     }
     94     scoped_memory.reset(reinterpret_cast<vm_address_t>(temp_addr),
     95                         mach_vm_round_page(size_));
     96   }
     97 
     98   // Make new memory object.
     99   memory_object_size_t allocation_size = size_;
    100   mac::ScopedMachSendRight named_right;
    101   kern_return_t kr = mach_make_memory_entry_64(
    102       mach_task_self(), &allocation_size,
    103       reinterpret_cast<memory_object_offset_t>(temp_addr), VM_PROT_READ,
    104       named_right.receive(), MACH_PORT_NULL);
    105   if (kr != KERN_SUCCESS) {
    106     MACH_DLOG(ERROR, kr) << "mach_make_memory_entry_64";
    107     return false;
    108   }
    109   DCHECK_GE(allocation_size, size_);
    110 
    111   handle_ = std::move(named_right);
    112   mode_ = Mode::kReadOnly;
    113   return true;
    114 }
    115 
    116 bool PlatformSharedMemoryRegion::ConvertToUnsafe() {
    117   if (!IsValid())
    118     return false;
    119 
    120   CHECK_EQ(mode_, Mode::kWritable)
    121       << "Only writable shared memory region can be converted to unsafe";
    122 
    123   mode_ = Mode::kUnsafe;
    124   return true;
    125 }
    126 
    127 bool PlatformSharedMemoryRegion::MapAt(off_t offset,
    128                                        size_t size,
    129                                        void** memory,
    130                                        size_t* mapped_size) const {
    131   if (!IsValid())
    132     return false;
    133 
    134   size_t end_byte;
    135   if (!CheckAdd(offset, size).AssignIfValid(&end_byte) || end_byte > size_) {
    136     return false;
    137   }
    138 
    139   bool write_allowed = mode_ != Mode::kReadOnly;
    140   vm_prot_t vm_prot_write = write_allowed ? VM_PROT_WRITE : 0;
    141   kern_return_t kr = mach_vm_map(
    142       mach_task_self(),
    143       reinterpret_cast<mach_vm_address_t*>(memory),  // Output parameter
    144       size,
    145       0,  // Alignment mask
    146       VM_FLAGS_ANYWHERE, handle_.get(), offset,
    147       FALSE,                         // Copy
    148       VM_PROT_READ | vm_prot_write,  // Current protection
    149       VM_PROT_READ | vm_prot_write,  // Maximum protection
    150       VM_INHERIT_NONE);
    151   if (kr != KERN_SUCCESS) {
    152     MACH_DLOG(ERROR, kr) << "mach_vm_map";
    153     return false;
    154   }
    155 
    156   *mapped_size = size;
    157   DCHECK_EQ(0U,
    158             reinterpret_cast<uintptr_t>(*memory) & (kMapMinimumAlignment - 1));
    159   return true;
    160 }
    161 
    162 // static
    163 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
    164                                                               size_t size) {
    165   if (size == 0)
    166     return {};
    167 
    168   if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
    169     return {};
    170 
    171   CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will "
    172                                      "lead to this region being non-modifiable";
    173 
    174   mach_vm_size_t vm_size = size;
    175   mac::ScopedMachSendRight named_right;
    176   kern_return_t kr = mach_make_memory_entry_64(
    177       mach_task_self(), &vm_size,
    178       0,  // Address.
    179       MAP_MEM_NAMED_CREATE | VM_PROT_READ | VM_PROT_WRITE,
    180       named_right.receive(),
    181       MACH_PORT_NULL);  // Parent handle.
    182   if (kr != KERN_SUCCESS) {
    183     MACH_DLOG(ERROR, kr) << "mach_make_memory_entry_64";
    184     return {};
    185   }
    186   DCHECK_GE(vm_size, size);
    187 
    188   return PlatformSharedMemoryRegion(std::move(named_right), mode, size,
    189                                     UnguessableToken::Create());
    190 }
    191 
    192 // static
    193 bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
    194     PlatformHandle handle,
    195     Mode mode,
    196     size_t size) {
    197   mach_vm_address_t temp_addr = 0;
    198   kern_return_t kr =
    199       mach_vm_map(mach_task_self(), &temp_addr, size, 0, VM_FLAGS_ANYWHERE,
    200                   handle, 0, FALSE, VM_PROT_READ | VM_PROT_WRITE,
    201                   VM_PROT_READ | VM_PROT_WRITE, VM_INHERIT_NONE);
    202   if (kr == KERN_SUCCESS) {
    203     kern_return_t kr_deallocate =
    204         mach_vm_deallocate(mach_task_self(), temp_addr, size);
    205     MACH_DLOG_IF(ERROR, kr_deallocate != KERN_SUCCESS, kr_deallocate)
    206         << "mach_vm_deallocate";
    207   } else if (kr != KERN_INVALID_RIGHT) {
    208     MACH_DLOG(ERROR, kr) << "mach_vm_map";
    209     return false;
    210   }
    211 
    212   bool is_read_only = kr == KERN_INVALID_RIGHT;
    213   bool expected_read_only = mode == Mode::kReadOnly;
    214 
    215   if (is_read_only != expected_read_only) {
    216     DLOG(ERROR) << "VM region has a wrong protection mask: it is"
    217                 << (is_read_only ? " " : " not ") << "read-only but it should"
    218                 << (expected_read_only ? " " : " not ") << "be";
    219     return false;
    220   }
    221 
    222   return true;
    223 }
    224 
    225 PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
    226     mac::ScopedMachSendRight handle,
    227     Mode mode,
    228     size_t size,
    229     const UnguessableToken& guid)
    230     : handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {}
    231 
    232 }  // namespace subtle
    233 }  // namespace base
    234