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