• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
Take(mac::ScopedMachSendRight handle,Mode mode,size_t size,const UnguessableToken & guid)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 
GetPlatformHandle() const42 mach_port_t PlatformSharedMemoryRegion::GetPlatformHandle() const {
43   return handle_.get();
44 }
45 
IsValid() const46 bool PlatformSharedMemoryRegion::IsValid() const {
47   return handle_.is_valid();
48 }
49 
Duplicate() const50 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 
ConvertToReadOnly()69 bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
70   return ConvertToReadOnly(nullptr);
71 }
72 
ConvertToReadOnly(void * mapped_addr)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 
ConvertToUnsafe()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 
MapAt(off_t offset,size_t size,void ** memory,size_t * mapped_size) const127 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
Create(Mode mode,size_t size)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
CheckPlatformHandlePermissionsCorrespondToMode(PlatformHandle handle,Mode mode,size_t size)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 
PlatformSharedMemoryRegion(mac::ScopedMachSendRight handle,Mode mode,size_t size,const UnguessableToken & guid)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