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(), &current_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(), &current_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