1 // Copyright 2018 The Chromium Authors
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/vm_map.h>
8
9 #include "base/apple/mach_logging.h"
10 #include "base/apple/scoped_mach_vm.h"
11
12 namespace base::subtle {
13
14 // static
Take(apple::ScopedMachSendRight handle,Mode mode,size_t size,const UnguessableToken & guid)15 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
16 apple::ScopedMachSendRight handle,
17 Mode mode,
18 size_t size,
19 const UnguessableToken& guid) {
20 if (!handle.is_valid()) {
21 return {};
22 }
23
24 if (size == 0) {
25 return {};
26 }
27
28 if (size > static_cast<size_t>(std::numeric_limits<int>::max())) {
29 return {};
30 }
31
32 CHECK(
33 CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size));
34
35 return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid);
36 }
37
GetPlatformHandle() const38 mach_port_t PlatformSharedMemoryRegion::GetPlatformHandle() const {
39 return handle_.get();
40 }
41
IsValid() const42 bool PlatformSharedMemoryRegion::IsValid() const {
43 return handle_.is_valid();
44 }
45
Duplicate() const46 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const {
47 if (!IsValid()) {
48 return {};
49 }
50
51 CHECK_NE(mode_, Mode::kWritable)
52 << "Duplicating a writable shared memory region is prohibited";
53
54 // Increment the ref count.
55 kern_return_t kr = mach_port_mod_refs(mach_task_self(), handle_.get(),
56 MACH_PORT_RIGHT_SEND, 1);
57 if (kr != KERN_SUCCESS) {
58 MACH_DLOG(ERROR, kr) << "mach_port_mod_refs";
59 return {};
60 }
61
62 return PlatformSharedMemoryRegion(apple::ScopedMachSendRight(handle_.get()),
63 mode_, size_, guid_);
64 }
65
ConvertToReadOnly()66 bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
67 return ConvertToReadOnly(nullptr);
68 }
69
ConvertToReadOnly(void * mapped_addr)70 bool PlatformSharedMemoryRegion::ConvertToReadOnly(void* mapped_addr) {
71 if (!IsValid()) {
72 return false;
73 }
74
75 CHECK_EQ(mode_, Mode::kWritable)
76 << "Only writable shared memory region can be converted to read-only";
77
78 apple::ScopedMachSendRight handle_copy(handle_.release());
79
80 void* temp_addr = mapped_addr;
81 apple::ScopedMachVM scoped_memory;
82 if (!temp_addr) {
83 // Intentionally lower current prot and max prot to |VM_PROT_READ|.
84 kern_return_t kr =
85 vm_map(mach_task_self(), reinterpret_cast<vm_address_t*>(&temp_addr),
86 size_, 0, VM_FLAGS_ANYWHERE, handle_copy.get(), 0, FALSE,
87 VM_PROT_READ, VM_PROT_READ, VM_INHERIT_NONE);
88 if (kr != KERN_SUCCESS) {
89 MACH_DLOG(ERROR, kr) << "vm_map";
90 return false;
91 }
92 scoped_memory.reset(reinterpret_cast<vm_address_t>(temp_addr),
93 mach_vm_round_page(size_));
94 }
95
96 // Make new memory object.
97 memory_object_size_t allocation_size = size_;
98 apple::ScopedMachSendRight named_right;
99 kern_return_t kr = mach_make_memory_entry_64(
100 mach_task_self(), &allocation_size,
101 reinterpret_cast<memory_object_offset_t>(temp_addr), VM_PROT_READ,
102 apple::ScopedMachSendRight::Receiver(named_right).get(), MACH_PORT_NULL);
103 if (kr != KERN_SUCCESS) {
104 MACH_DLOG(ERROR, kr) << "mach_make_memory_entry_64";
105 return false;
106 }
107 DCHECK_GE(allocation_size, size_);
108
109 handle_ = std::move(named_right);
110 mode_ = Mode::kReadOnly;
111 return true;
112 }
113
ConvertToUnsafe()114 bool PlatformSharedMemoryRegion::ConvertToUnsafe() {
115 if (!IsValid()) {
116 return false;
117 }
118
119 CHECK_EQ(mode_, Mode::kWritable)
120 << "Only writable shared memory region can be converted to unsafe";
121
122 mode_ = Mode::kUnsafe;
123 return true;
124 }
125
126 // static
Create(Mode mode,size_t size)127 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
128 size_t size) {
129 if (size == 0) {
130 return {};
131 }
132
133 if (size > static_cast<size_t>(std::numeric_limits<int>::max())) {
134 return {};
135 }
136
137 CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will "
138 "lead to this region being non-modifiable";
139
140 memory_object_size_t vm_size = size;
141 apple::ScopedMachSendRight named_right;
142 kern_return_t kr = mach_make_memory_entry_64(
143 mach_task_self(), &vm_size,
144 0, // Address.
145 MAP_MEM_NAMED_CREATE | VM_PROT_READ | VM_PROT_WRITE,
146 apple::ScopedMachSendRight::Receiver(named_right).get(),
147 MACH_PORT_NULL); // Parent handle.
148 // Crash as soon as shm allocation fails to debug the issue
149 // https://crbug.com/872237.
150 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_make_memory_entry_64";
151 DCHECK_GE(vm_size, size);
152
153 return PlatformSharedMemoryRegion(std::move(named_right), mode, size,
154 UnguessableToken::Create());
155 }
156
157 // static
CheckPlatformHandlePermissionsCorrespondToMode(PlatformSharedMemoryHandle handle,Mode mode,size_t size)158 bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
159 PlatformSharedMemoryHandle handle,
160 Mode mode,
161 size_t size) {
162 vm_address_t temp_addr = 0;
163 kern_return_t kr =
164 vm_map(mach_task_self(), &temp_addr, size, 0, VM_FLAGS_ANYWHERE, handle,
165 0, FALSE, VM_PROT_READ | VM_PROT_WRITE,
166 VM_PROT_READ | VM_PROT_WRITE, VM_INHERIT_NONE);
167 if (kr == KERN_SUCCESS) {
168 kern_return_t kr_deallocate =
169 vm_deallocate(mach_task_self(), temp_addr, size);
170 // TODO(https://crbug.com/838365): convert to DLOG when bug fixed.
171 MACH_LOG_IF(ERROR, kr_deallocate != KERN_SUCCESS, kr_deallocate)
172 << "vm_deallocate";
173 } else if (kr != KERN_INVALID_RIGHT) {
174 MACH_LOG(ERROR, kr) << "vm_map";
175 return false;
176 }
177
178 bool is_read_only = kr == KERN_INVALID_RIGHT;
179 bool expected_read_only = mode == Mode::kReadOnly;
180
181 if (is_read_only != expected_read_only) {
182 // TODO(https://crbug.com/838365): convert to DLOG when bug fixed.
183 LOG(ERROR) << "VM region has a wrong protection mask: it is"
184 << (is_read_only ? " " : " not ") << "read-only but it should"
185 << (expected_read_only ? " " : " not ") << "be";
186 return false;
187 }
188
189 return true;
190 }
191
PlatformSharedMemoryRegion(apple::ScopedMachSendRight handle,Mode mode,size_t size,const UnguessableToken & guid)192 PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
193 apple::ScopedMachSendRight handle,
194 Mode mode,
195 size_t size,
196 const UnguessableToken& guid)
197 : handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {}
198
199 } // namespace base::subtle
200