Home | History | Annotate | Download | only in memory
      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(), &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.
    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