1 // Copyright 2015 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 <mach/mach.h>
6 #include <mach/mach_vm.h>
7 #include <servers/bootstrap.h>
8 #include <stddef.h>
9 #include <stdint.h>
10
11 #include "base/command_line.h"
12 #include "base/mac/mac_util.h"
13 #include "base/mac/mach_logging.h"
14 #include "base/mac/scoped_mach_port.h"
15 #include "base/macros.h"
16 #include "base/memory/shared_memory.h"
17 #include "base/process/process_handle.h"
18 #include "base/rand_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/sys_info.h"
21 #include "base/test/multiprocess_test.h"
22 #include "base/test/test_timeouts.h"
23 #include "testing/multiprocess_func_list.h"
24
25 namespace base {
26
27 namespace {
28
29 // Gets the current and maximum protection levels of the memory region.
30 // Returns whether the operation was successful.
31 // |current| and |max| are output variables only populated on success.
GetProtections(void * address,size_t size,int * current,int * max)32 bool GetProtections(void* address, size_t size, int* current, int* max) {
33 vm_region_info_t region_info;
34 mach_vm_address_t mem_address = reinterpret_cast<mach_vm_address_t>(address);
35 mach_vm_size_t mem_size = size;
36 vm_region_basic_info_64 basic_info;
37
38 region_info = reinterpret_cast<vm_region_recurse_info_t>(&basic_info);
39 vm_region_flavor_t flavor = VM_REGION_BASIC_INFO_64;
40 memory_object_name_t memory_object;
41 mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
42
43 kern_return_t kr =
44 mach_vm_region(mach_task_self(), &mem_address, &mem_size, flavor,
45 region_info, &count, &memory_object);
46 if (kr != KERN_SUCCESS) {
47 MACH_LOG(ERROR, kr) << "Failed to get region info.";
48 return false;
49 }
50
51 *current = basic_info.protection;
52 *max = basic_info.max_protection;
53 return true;
54 }
55
56 // Creates a new SharedMemory with the given |size|, filled with 'a'.
CreateSharedMemory(int size)57 std::unique_ptr<SharedMemory> CreateSharedMemory(int size) {
58 SharedMemoryHandle shm(size);
59 if (!shm.IsValid()) {
60 LOG(ERROR) << "Failed to make SharedMemoryHandle";
61 return nullptr;
62 }
63 std::unique_ptr<SharedMemory> shared_memory(new SharedMemory(shm, false));
64 shared_memory->Map(size);
65 memset(shared_memory->memory(), 'a', size);
66 return shared_memory;
67 }
68
69 static const std::string g_service_switch_name = "service_name";
70
71 // Structs used to pass a mach port from client to server.
72 struct MachSendPortMessage {
73 mach_msg_header_t header;
74 mach_msg_body_t body;
75 mach_msg_port_descriptor_t data;
76 };
77 struct MachReceivePortMessage {
78 mach_msg_header_t header;
79 mach_msg_body_t body;
80 mach_msg_port_descriptor_t data;
81 mach_msg_trailer_t trailer;
82 };
83
84 // Makes the current process into a Mach Server with the given |service_name|.
BecomeMachServer(const char * service_name)85 mach_port_t BecomeMachServer(const char* service_name) {
86 mach_port_t port;
87 kern_return_t kr = bootstrap_check_in(bootstrap_port, service_name, &port);
88 MACH_CHECK(kr == KERN_SUCCESS, kr) << "BecomeMachServer";
89 return port;
90 }
91
92 // Returns the mach port for the Mach Server with the given |service_name|.
LookupServer(const char * service_name)93 mach_port_t LookupServer(const char* service_name) {
94 mach_port_t server_port;
95 kern_return_t kr =
96 bootstrap_look_up(bootstrap_port, service_name, &server_port);
97 MACH_CHECK(kr == KERN_SUCCESS, kr) << "LookupServer";
98 return server_port;
99 }
100
MakeReceivingPort()101 mach_port_t MakeReceivingPort() {
102 mach_port_t client_port;
103 kern_return_t kr =
104 mach_port_allocate(mach_task_self(), // our task is acquiring
105 MACH_PORT_RIGHT_RECEIVE, // a new receive right
106 &client_port); // with this name
107 MACH_CHECK(kr == KERN_SUCCESS, kr) << "MakeReceivingPort";
108 return client_port;
109 }
110
111 // Blocks until a mach message is sent to |server_port|. This mach message
112 // must contain a mach port. Returns that mach port.
ReceiveMachPort(mach_port_t port_to_listen_on)113 mach_port_t ReceiveMachPort(mach_port_t port_to_listen_on) {
114 MachReceivePortMessage recv_msg;
115 mach_msg_header_t* recv_hdr = &(recv_msg.header);
116 recv_hdr->msgh_local_port = port_to_listen_on;
117 recv_hdr->msgh_size = sizeof(recv_msg);
118 kern_return_t kr =
119 mach_msg(recv_hdr, // message buffer
120 MACH_RCV_MSG, // option indicating service
121 0, // send size
122 recv_hdr->msgh_size, // size of header + body
123 port_to_listen_on, // receive name
124 MACH_MSG_TIMEOUT_NONE, // no timeout, wait forever
125 MACH_PORT_NULL); // no notification port
126 MACH_CHECK(kr == KERN_SUCCESS, kr) << "ReceiveMachPort";
127 mach_port_t other_task_port = recv_msg.data.name;
128 return other_task_port;
129 }
130
131 // Passes a copy of the send right of |port_to_send| to |receiving_port|.
SendMachPort(mach_port_t receiving_port,mach_port_t port_to_send,int disposition)132 void SendMachPort(mach_port_t receiving_port,
133 mach_port_t port_to_send,
134 int disposition) {
135 MachSendPortMessage send_msg;
136 mach_msg_header_t* send_hdr;
137 send_hdr = &(send_msg.header);
138 send_hdr->msgh_bits =
139 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;
140 send_hdr->msgh_size = sizeof(send_msg);
141 send_hdr->msgh_remote_port = receiving_port;
142 send_hdr->msgh_local_port = MACH_PORT_NULL;
143 send_hdr->msgh_reserved = 0;
144 send_hdr->msgh_id = 0;
145 send_msg.body.msgh_descriptor_count = 1;
146 send_msg.data.name = port_to_send;
147 send_msg.data.disposition = disposition;
148 send_msg.data.type = MACH_MSG_PORT_DESCRIPTOR;
149 int kr = mach_msg(send_hdr, // message buffer
150 MACH_SEND_MSG, // option indicating send
151 send_hdr->msgh_size, // size of header + body
152 0, // receive limit
153 MACH_PORT_NULL, // receive name
154 MACH_MSG_TIMEOUT_NONE, // no timeout, wait forever
155 MACH_PORT_NULL); // no notification port
156 MACH_CHECK(kr == KERN_SUCCESS, kr) << "SendMachPort";
157 }
158
CreateRandomServiceName()159 std::string CreateRandomServiceName() {
160 return StringPrintf("SharedMemoryMacMultiProcessTest.%llu", RandUint64());
161 }
162
163 // Sets up the mach communication ports with the server. Returns a port to which
164 // the server will send mach objects.
CommonChildProcessSetUp()165 mach_port_t CommonChildProcessSetUp() {
166 CommandLine cmd_line = *CommandLine::ForCurrentProcess();
167 std::string service_name =
168 cmd_line.GetSwitchValueASCII(g_service_switch_name);
169 mac::ScopedMachSendRight server_port(LookupServer(service_name.c_str()));
170 mach_port_t client_port = MakeReceivingPort();
171
172 // Send the port that this process is listening on to the server.
173 SendMachPort(server_port.get(), client_port, MACH_MSG_TYPE_MAKE_SEND);
174 return client_port;
175 }
176
177 // The number of active names in the current task's port name space.
GetActiveNameCount()178 mach_msg_type_number_t GetActiveNameCount() {
179 mach_port_name_array_t name_array;
180 mach_msg_type_number_t names_count;
181 mach_port_type_array_t type_array;
182 mach_msg_type_number_t types_count;
183 kern_return_t kr = mach_port_names(mach_task_self(), &name_array,
184 &names_count, &type_array, &types_count);
185 MACH_CHECK(kr == KERN_SUCCESS, kr) << "GetActiveNameCount";
186 return names_count;
187 }
188
189 } // namespace
190
191 class SharedMemoryMacMultiProcessTest : public MultiProcessTest {
192 public:
SharedMemoryMacMultiProcessTest()193 SharedMemoryMacMultiProcessTest() {}
194
MakeCmdLine(const std::string & procname)195 CommandLine MakeCmdLine(const std::string& procname) override {
196 CommandLine command_line = MultiProcessTest::MakeCmdLine(procname);
197 // Pass the service name to the child process.
198 command_line.AppendSwitchASCII(g_service_switch_name, service_name_);
199 return command_line;
200 }
201
SetUpChild(const std::string & name)202 void SetUpChild(const std::string& name) {
203 // Make a random service name so that this test doesn't conflict with other
204 // similar tests.
205 service_name_ = CreateRandomServiceName();
206 server_port_.reset(BecomeMachServer(service_name_.c_str()));
207 child_process_ = SpawnChild(name);
208 client_port_.reset(ReceiveMachPort(server_port_.get()));
209 }
210
211 static const int s_memory_size = 99999;
212
213 protected:
214 std::string service_name_;
215
216 // A port on which the main process listens for mach messages from the child
217 // process.
218 mac::ScopedMachReceiveRight server_port_;
219
220 // A port on which the child process listens for mach messages from the main
221 // process.
222 mac::ScopedMachSendRight client_port_;
223
224 base::Process child_process_;
225 DISALLOW_COPY_AND_ASSIGN(SharedMemoryMacMultiProcessTest);
226 };
227
228 // Tests that content written to shared memory in the server process can be read
229 // by the child process.
TEST_F(SharedMemoryMacMultiProcessTest,MachBasedSharedMemory)230 TEST_F(SharedMemoryMacMultiProcessTest, MachBasedSharedMemory) {
231 SetUpChild("MachBasedSharedMemoryClient");
232
233 std::unique_ptr<SharedMemory> shared_memory(
234 CreateSharedMemory(s_memory_size));
235
236 // Send the underlying memory object to the client process.
237 SendMachPort(client_port_.get(), shared_memory->handle().GetMemoryObject(),
238 MACH_MSG_TYPE_COPY_SEND);
239 int rv = -1;
240 ASSERT_TRUE(child_process_.WaitForExitWithTimeout(
241 TestTimeouts::action_timeout(), &rv));
242 EXPECT_EQ(0, rv);
243 }
244
MULTIPROCESS_TEST_MAIN(MachBasedSharedMemoryClient)245 MULTIPROCESS_TEST_MAIN(MachBasedSharedMemoryClient) {
246 mac::ScopedMachReceiveRight client_port(CommonChildProcessSetUp());
247 // The next mach port should be for a memory object.
248 mach_port_t memory_object = ReceiveMachPort(client_port.get());
249 SharedMemoryHandle shm(memory_object,
250 SharedMemoryMacMultiProcessTest::s_memory_size,
251 GetCurrentProcId());
252 SharedMemory shared_memory(shm, false);
253 shared_memory.Map(SharedMemoryMacMultiProcessTest::s_memory_size);
254 const char* start = static_cast<const char*>(shared_memory.memory());
255 for (int i = 0; i < SharedMemoryMacMultiProcessTest::s_memory_size; ++i) {
256 DCHECK_EQ(start[i], 'a');
257 }
258 return 0;
259 }
260
261 // Tests that mapping shared memory with an offset works correctly.
TEST_F(SharedMemoryMacMultiProcessTest,MachBasedSharedMemoryWithOffset)262 TEST_F(SharedMemoryMacMultiProcessTest, MachBasedSharedMemoryWithOffset) {
263 SetUpChild("MachBasedSharedMemoryWithOffsetClient");
264
265 SharedMemoryHandle shm(s_memory_size);
266 ASSERT_TRUE(shm.IsValid());
267 SharedMemory shared_memory(shm, false);
268 shared_memory.Map(s_memory_size);
269
270 size_t page_size = SysInfo::VMAllocationGranularity();
271 char* start = static_cast<char*>(shared_memory.memory());
272 memset(start, 'a', page_size);
273 memset(start + page_size, 'b', page_size);
274 memset(start + 2 * page_size, 'c', page_size);
275
276 // Send the underlying memory object to the client process.
277 SendMachPort(
278 client_port_.get(), shm.GetMemoryObject(), MACH_MSG_TYPE_COPY_SEND);
279 int rv = -1;
280 ASSERT_TRUE(child_process_.WaitForExitWithTimeout(
281 TestTimeouts::action_timeout(), &rv));
282 EXPECT_EQ(0, rv);
283 }
284
MULTIPROCESS_TEST_MAIN(MachBasedSharedMemoryWithOffsetClient)285 MULTIPROCESS_TEST_MAIN(MachBasedSharedMemoryWithOffsetClient) {
286 mac::ScopedMachReceiveRight client_port(CommonChildProcessSetUp());
287 // The next mach port should be for a memory object.
288 mach_port_t memory_object = ReceiveMachPort(client_port.get());
289 SharedMemoryHandle shm(memory_object,
290 SharedMemoryMacMultiProcessTest::s_memory_size,
291 GetCurrentProcId());
292 SharedMemory shared_memory(shm, false);
293 size_t page_size = SysInfo::VMAllocationGranularity();
294 shared_memory.MapAt(page_size, 2 * page_size);
295 const char* start = static_cast<const char*>(shared_memory.memory());
296 for (size_t i = 0; i < page_size; ++i) {
297 DCHECK_EQ(start[i], 'b');
298 }
299 for (size_t i = page_size; i < 2 * page_size; ++i) {
300 DCHECK_EQ(start[i], 'c');
301 }
302 return 0;
303 }
304
305 // Tests that duplication and closing has the right effect on Mach reference
306 // counts.
TEST_F(SharedMemoryMacMultiProcessTest,MachDuplicateAndClose)307 TEST_F(SharedMemoryMacMultiProcessTest, MachDuplicateAndClose) {
308 mach_msg_type_number_t active_name_count = GetActiveNameCount();
309
310 // Making a new SharedMemoryHandle increments the name count.
311 SharedMemoryHandle shm(s_memory_size);
312 ASSERT_TRUE(shm.IsValid());
313 EXPECT_EQ(active_name_count + 1, GetActiveNameCount());
314
315 // Duplicating the SharedMemoryHandle increments the ref count, but doesn't
316 // make a new name.
317 shm.Duplicate();
318 EXPECT_EQ(active_name_count + 1, GetActiveNameCount());
319
320 // Closing the SharedMemoryHandle decrements the ref count. The first time has
321 // no effect.
322 shm.Close();
323 EXPECT_EQ(active_name_count + 1, GetActiveNameCount());
324
325 // Closing the SharedMemoryHandle decrements the ref count. The second time
326 // destroys the port.
327 shm.Close();
328 EXPECT_EQ(active_name_count, GetActiveNameCount());
329 }
330
331 // Tests that Mach shared memory can be mapped and unmapped.
TEST_F(SharedMemoryMacMultiProcessTest,MachUnmapMap)332 TEST_F(SharedMemoryMacMultiProcessTest, MachUnmapMap) {
333 mach_msg_type_number_t active_name_count = GetActiveNameCount();
334
335 std::unique_ptr<SharedMemory> shared_memory =
336 CreateSharedMemory(s_memory_size);
337 ASSERT_TRUE(shared_memory->Unmap());
338 ASSERT_TRUE(shared_memory->Map(s_memory_size));
339 shared_memory.reset();
340 EXPECT_EQ(active_name_count, GetActiveNameCount());
341 }
342
343 // Tests that passing a SharedMemoryHandle to a SharedMemory object also passes
344 // ownership, and that destroying the SharedMemory closes the SharedMemoryHandle
345 // as well.
TEST_F(SharedMemoryMacMultiProcessTest,MachSharedMemoryTakesOwnership)346 TEST_F(SharedMemoryMacMultiProcessTest, MachSharedMemoryTakesOwnership) {
347 mach_msg_type_number_t active_name_count = GetActiveNameCount();
348
349 // Making a new SharedMemoryHandle increments the name count.
350 SharedMemoryHandle shm(s_memory_size);
351 ASSERT_TRUE(shm.IsValid());
352 EXPECT_EQ(active_name_count + 1, GetActiveNameCount());
353
354 // Name count doesn't change when mapping the memory.
355 std::unique_ptr<SharedMemory> shared_memory(new SharedMemory(shm, false));
356 shared_memory->Map(s_memory_size);
357 EXPECT_EQ(active_name_count + 1, GetActiveNameCount());
358
359 // Destroying the SharedMemory object frees the resource.
360 shared_memory.reset();
361 EXPECT_EQ(active_name_count, GetActiveNameCount());
362 }
363
364 // Tests that the read-only flag works.
TEST_F(SharedMemoryMacMultiProcessTest,MachReadOnly)365 TEST_F(SharedMemoryMacMultiProcessTest, MachReadOnly) {
366 std::unique_ptr<SharedMemory> shared_memory(
367 CreateSharedMemory(s_memory_size));
368
369 SharedMemoryHandle shm2 = shared_memory->handle().Duplicate();
370 ASSERT_TRUE(shm2.IsValid());
371 SharedMemory shared_memory2(shm2, true);
372 shared_memory2.Map(s_memory_size);
373 ASSERT_DEATH(memset(shared_memory2.memory(), 'b', s_memory_size), "");
374 }
375
376 // Tests that the method ShareToProcess() works.
TEST_F(SharedMemoryMacMultiProcessTest,MachShareToProcess)377 TEST_F(SharedMemoryMacMultiProcessTest, MachShareToProcess) {
378 mach_msg_type_number_t active_name_count = GetActiveNameCount();
379
380 {
381 std::unique_ptr<SharedMemory> shared_memory(
382 CreateSharedMemory(s_memory_size));
383
384 SharedMemoryHandle shm2;
385 ASSERT_TRUE(shared_memory->ShareToProcess(GetCurrentProcId(), &shm2));
386 ASSERT_TRUE(shm2.IsValid());
387 SharedMemory shared_memory2(shm2, true);
388 shared_memory2.Map(s_memory_size);
389
390 ASSERT_EQ(0, memcmp(shared_memory->memory(), shared_memory2.memory(),
391 s_memory_size));
392 }
393
394 EXPECT_EQ(active_name_count, GetActiveNameCount());
395 }
396
397 // Tests that the method ShareReadOnlyToProcess() creates a memory object that
398 // is read only.
TEST_F(SharedMemoryMacMultiProcessTest,MachShareToProcessReadonly)399 TEST_F(SharedMemoryMacMultiProcessTest, MachShareToProcessReadonly) {
400 std::unique_ptr<SharedMemory> shared_memory(
401 CreateSharedMemory(s_memory_size));
402
403 // Check the protection levels.
404 int current_prot, max_prot;
405 ASSERT_TRUE(GetProtections(shared_memory->memory(),
406 shared_memory->mapped_size(), ¤t_prot,
407 &max_prot));
408 ASSERT_EQ(VM_PROT_READ | VM_PROT_WRITE, current_prot);
409 ASSERT_EQ(VM_PROT_READ | VM_PROT_WRITE, max_prot);
410
411 // Make a new memory object.
412 SharedMemoryHandle shm2;
413 ASSERT_TRUE(shared_memory->ShareReadOnlyToProcess(GetCurrentProcId(), &shm2));
414 ASSERT_TRUE(shm2.IsValid());
415
416 // Mapping with |readonly| set to |false| should fail.
417 SharedMemory shared_memory2(shm2, false);
418 shared_memory2.Map(s_memory_size);
419 ASSERT_EQ(nullptr, shared_memory2.memory());
420
421 // Now trying mapping with |readonly| set to |true|.
422 SharedMemory shared_memory3(shm2.Duplicate(), true);
423 shared_memory3.Map(s_memory_size);
424 ASSERT_NE(nullptr, shared_memory3.memory());
425
426 // Check the protection levels.
427 ASSERT_TRUE(GetProtections(shared_memory3.memory(),
428 shared_memory3.mapped_size(), ¤t_prot,
429 &max_prot));
430 ASSERT_EQ(VM_PROT_READ, current_prot);
431 ASSERT_EQ(VM_PROT_READ, max_prot);
432
433 // The memory should still be readonly, since the underlying memory object
434 // is readonly.
435 ASSERT_DEATH(memset(shared_memory2.memory(), 'b', s_memory_size), "");
436 }
437
438 // Tests that the method ShareReadOnlyToProcess() doesn't leak.
TEST_F(SharedMemoryMacMultiProcessTest,MachShareToProcessReadonlyLeak)439 TEST_F(SharedMemoryMacMultiProcessTest, MachShareToProcessReadonlyLeak) {
440 mach_msg_type_number_t active_name_count = GetActiveNameCount();
441
442 {
443 std::unique_ptr<SharedMemory> shared_memory(
444 CreateSharedMemory(s_memory_size));
445
446 SharedMemoryHandle shm2;
447 ASSERT_TRUE(
448 shared_memory->ShareReadOnlyToProcess(GetCurrentProcId(), &shm2));
449 ASSERT_TRUE(shm2.IsValid());
450
451 // Intentionally map with |readonly| set to |false|.
452 SharedMemory shared_memory2(shm2, false);
453 shared_memory2.Map(s_memory_size);
454 }
455
456 EXPECT_EQ(active_name_count, GetActiveNameCount());
457 }
458
459 } // namespace base
460