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. 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'. 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|. 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|. 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 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. 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|. 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 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. 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. 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: 193 SharedMemoryMacMultiProcessTest() {} 194 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 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. 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 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. 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 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. 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. 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. 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. 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. 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. 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. 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