blob: 330db03af9618d2842b90ed7b5ef1b11d64f8a80 [file] [log] [blame]
erikchenb3481e22015-10-06 19:59:391// 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 "ipc/attachment_broker_privileged_mac.h"
6
7#include <mach/mach.h>
8#include <mach/mach_vm.h>
avi246998d82015-12-22 02:39:049#include <stddef.h>
10#include <stdint.h>
erikchenb3481e22015-10-06 19:59:3911
12#include <map>
danakj03de39b22016-04-23 04:21:0913#include <memory>
erikchenb3481e22015-10-06 19:59:3914
15#include "base/command_line.h"
erikchen328bf3f2015-10-24 00:46:5416#include "base/mac/mac_util.h"
erikchenb3481e22015-10-06 19:59:3917#include "base/mac/mach_logging.h"
amistrye04cef72016-03-12 04:51:4318#include "base/mac/mach_port_util.h"
erikchenb3481e22015-10-06 19:59:3919#include "base/mac/scoped_mach_port.h"
avi246998d82015-12-22 02:39:0420#include "base/macros.h"
erikchenb3481e22015-10-06 19:59:3921#include "base/memory/shared_memory.h"
22#include "base/process/port_provider_mac.h"
23#include "base/process/process_handle.h"
erikchenb3481e22015-10-06 19:59:3924#include "base/sys_info.h"
25#include "base/test/multiprocess_test.h"
26#include "base/test/test_timeouts.h"
erikchen89d74232015-10-12 18:24:0127#include "ipc/test_util_mac.h"
erikchenb3481e22015-10-06 19:59:3928#include "testing/multiprocess_func_list.h"
29
30namespace IPC {
31
32namespace {
33
34static const std::string g_service_switch_name = "service_name";
35
erikchenb3481e22015-10-06 19:59:3936// Sends a uint32_t to a mach port.
37void SendUInt32(mach_port_t port, uint32_t message) {
38 int message_size = sizeof(uint32_t);
39 int total_size = message_size + sizeof(mach_msg_header_t);
40 void* buffer = malloc(total_size);
41 mach_msg_header_t* header = (mach_msg_header_t*)buffer;
42 header->msgh_remote_port = port;
43 header->msgh_local_port = MACH_PORT_NULL;
44 header->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
45 header->msgh_reserved = 0;
erikchenc4c76682015-10-06 23:25:2946 header->msgh_id = 0;
erikchenb3481e22015-10-06 19:59:3947 header->msgh_size = total_size;
48 memcpy(static_cast<char*>(buffer) + sizeof(mach_msg_header_t), &message,
49 message_size);
50
51 kern_return_t kr;
52 kr = mach_msg(static_cast<mach_msg_header_t*>(buffer), MACH_SEND_MSG,
53 total_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
54 MACH_PORT_NULL);
55 MACH_CHECK(kr == KERN_SUCCESS, kr) << "SendUInt32";
56 free(buffer);
57}
58
59// Receives a uint32_t from a mach port.
60uint32_t ReceiveUInt32(mach_port_t listening_port) {
61 int message_size = sizeof(uint32_t);
62 int total_size =
63 message_size + sizeof(mach_msg_header_t) + sizeof(mach_msg_trailer_t);
64 int options = MACH_RCV_MSG;
65 void* buffer = malloc(total_size);
66
67 int kr =
68 mach_msg(static_cast<mach_msg_header_t*>(buffer), options, 0, total_size,
69 listening_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
70 MACH_CHECK(kr == KERN_SUCCESS, kr) << "ReceiveUInt32";
71
72 uint32_t response;
73 memcpy(&response, static_cast<char*>(buffer) + sizeof(mach_msg_header_t),
74 message_size);
75
76 free(buffer);
77 return response;
78}
79
erikchenb3481e22015-10-06 19:59:3980// Sets up the mach communication ports with the server. Returns a port to which
81// the server will send mach objects.
82// |original_name_count| is an output variable that describes the number of
83// active names in this task before the task port is shared with the server.
84base::mac::ScopedMachReceiveRight CommonChildProcessSetUp(
85 mach_msg_type_number_t* original_name_count) {
86 base::CommandLine cmd_line = *base::CommandLine::ForCurrentProcess();
87 std::string service_name =
88 cmd_line.GetSwitchValueASCII(g_service_switch_name);
89 base::mac::ScopedMachSendRight server_port(
90 LookupServer(service_name.c_str()));
91 base::mac::ScopedMachReceiveRight client_port(MakeReceivingPort());
92
93 // |server_port| is a newly allocated right which will be deallocated once
94 // this method returns.
95 *original_name_count = GetActiveNameCount() - 1;
96
97 // Send the port that this process is listening on to the server.
markda902e182015-10-20 18:36:1398 SendMachPort(server_port.get(), client_port.get(), MACH_MSG_TYPE_MAKE_SEND);
erikchenb3481e22015-10-06 19:59:3999
100 // Send the task port for this process.
markda902e182015-10-20 18:36:13101 SendMachPort(server_port.get(), mach_task_self(), MACH_MSG_TYPE_COPY_SEND);
erikchenb3481e22015-10-06 19:59:39102 return client_port;
103}
104
105// Creates a new shared memory region populated with 'a'.
danakj03de39b22016-04-23 04:21:09106std::unique_ptr<base::SharedMemory> CreateAndPopulateSharedMemoryHandle(
erikchenb3481e22015-10-06 19:59:39107 size_t size) {
108 base::SharedMemoryHandle shm(size);
danakj03de39b22016-04-23 04:21:09109 std::unique_ptr<base::SharedMemory> shared_memory(
erikchenb3481e22015-10-06 19:59:39110 new base::SharedMemory(shm, false));
111 shared_memory->Map(size);
112 memset(shared_memory->memory(), 'a', size);
113 return shared_memory;
114}
115
116// Create a shared memory region from a memory object. The returned object takes
117// ownership of |memory_object|.
danakj03de39b22016-04-23 04:21:09118std::unique_ptr<base::SharedMemory> MapMemoryObject(mach_port_t memory_object,
119 size_t size) {
erikchenb3481e22015-10-06 19:59:39120 base::SharedMemoryHandle shm(memory_object, size, base::GetCurrentProcId());
danakj03de39b22016-04-23 04:21:09121 std::unique_ptr<base::SharedMemory> shared_memory(
erikchenb3481e22015-10-06 19:59:39122 new base::SharedMemory(shm, false));
123 shared_memory->Map(size);
124 return shared_memory;
125}
126
erikchen7c556432015-11-11 22:07:27127class MockPortProvider : public base::PortProvider {
128 public:
129 MockPortProvider() {}
130 ~MockPortProvider() override {}
131 mach_port_t TaskForPid(base::ProcessHandle process) const override {
132 return MACH_PORT_NULL;
133 }
134};
135
erikchenb3481e22015-10-06 19:59:39136} // namespace
137
138class AttachmentBrokerPrivilegedMacMultiProcessTest
139 : public base::MultiProcessTest {
140 public:
141 AttachmentBrokerPrivilegedMacMultiProcessTest() {}
142
143 base::CommandLine MakeCmdLine(const std::string& procname) override {
144 base::CommandLine command_line = MultiProcessTest::MakeCmdLine(procname);
145 // Pass the service name to the child process.
146 command_line.AppendSwitchASCII(g_service_switch_name, service_name_);
147 return command_line;
148 }
149
150 void SetUpChild(const std::string& name) {
151 // Make a random service name so that this test doesn't conflict with other
152 // similar tests.
153 service_name_ = CreateRandomServiceName();
154 server_port_.reset(BecomeMachServer(service_name_.c_str()).release());
155 child_process_ = SpawnChild(name);
markda902e182015-10-20 18:36:13156 client_port_.reset(ReceiveMachPort(server_port_.get()).release());
157 client_task_port_.reset(ReceiveMachPort(server_port_.get()).release());
erikchenb3481e22015-10-06 19:59:39158 }
159
160 static const int s_memory_size = 99999;
161
162 protected:
163 std::string service_name_;
164
165 // A port on which the main process listens for mach messages from the child
166 // process.
167 base::mac::ScopedMachReceiveRight server_port_;
168
169 // A port on which the child process listens for mach messages from the main
170 // process.
171 base::mac::ScopedMachSendRight client_port_;
172
173 // Child process's task port.
174 base::mac::ScopedMachSendRight client_task_port_;
175
erikchen7c556432015-11-11 22:07:27176 // Dummy port provider.
177 MockPortProvider port_provider_;
178
erikchenb3481e22015-10-06 19:59:39179 base::Process child_process_;
180 DISALLOW_COPY_AND_ASSIGN(AttachmentBrokerPrivilegedMacMultiProcessTest);
181};
182
183// The attachment broker inserts a right for a memory object into the
184// destination task.
185TEST_F(AttachmentBrokerPrivilegedMacMultiProcessTest, InsertRight) {
186 SetUpChild("InsertRightClient");
187 mach_msg_type_number_t original_name_count = GetActiveNameCount();
erikchen7c556432015-11-11 22:07:27188 IPC::AttachmentBrokerPrivilegedMac broker(&port_provider_);
erikchenb3481e22015-10-06 19:59:39189
190 // Create some shared memory.
danakj03de39b22016-04-23 04:21:09191 std::unique_ptr<base::SharedMemory> shared_memory =
erikchenb3481e22015-10-06 19:59:39192 CreateAndPopulateSharedMemoryHandle(s_memory_size);
193 ASSERT_TRUE(shared_memory->handle().IsValid());
194
erikchen81543db2015-10-12 17:19:51195 // Insert the memory object into the destination task, via an intermediate
196 // port.
197 IncrementMachRefCount(shared_memory->handle().GetMemoryObject(),
198 MACH_PORT_RIGHT_SEND);
amistrye04cef72016-03-12 04:51:43199 mach_port_name_t inserted_memory_object = base::CreateIntermediateMachPort(
200 client_task_port_.get(),
201 base::mac::ScopedMachSendRight(shared_memory->handle().GetMemoryObject()),
202 nullptr);
erikchenb3481e22015-10-06 19:59:39203 EXPECT_NE(inserted_memory_object,
204 static_cast<mach_port_name_t>(MACH_PORT_NULL));
markda902e182015-10-20 18:36:13205 SendUInt32(client_port_.get(), inserted_memory_object);
erikchenb3481e22015-10-06 19:59:39206
207 // Check that no names have been leaked.
208 shared_memory.reset();
209 EXPECT_EQ(original_name_count, GetActiveNameCount());
210
211 int rv = -1;
212 ASSERT_TRUE(child_process_.WaitForExitWithTimeout(
213 TestTimeouts::action_timeout(), &rv));
214 EXPECT_EQ(0, rv);
215}
216
217MULTIPROCESS_TEST_MAIN(InsertRightClient) {
218 mach_msg_type_number_t original_name_count = 0;
219 base::mac::ScopedMachReceiveRight client_port(
220 CommonChildProcessSetUp(&original_name_count).release());
markda902e182015-10-20 18:36:13221 base::mac::ScopedMachReceiveRight inserted_port(
222 ReceiveUInt32(client_port.get()));
223 base::mac::ScopedMachSendRight memory_object(
224 ReceiveMachPort(inserted_port.get()));
erikchenb3481e22015-10-06 19:59:39225 inserted_port.reset();
226
227 // The server should have inserted a right into this process.
228 EXPECT_EQ(original_name_count + 1, GetActiveNameCount());
229
230 // Map the memory object and check its contents.
danakj03de39b22016-04-23 04:21:09231 std::unique_ptr<base::SharedMemory> shared_memory(MapMemoryObject(
erikchenb3481e22015-10-06 19:59:39232 memory_object.release(),
233 AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
234 const char* start = static_cast<const char*>(shared_memory->memory());
235 for (int i = 0;
236 i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
237 DCHECK_EQ(start[i], 'a');
238 }
239
240 // Check that no names have been leaked.
241 shared_memory.reset();
242 EXPECT_EQ(original_name_count, GetActiveNameCount());
243
244 return 0;
245}
246
247// The attachment broker inserts the right for a memory object into the
248// destination task twice.
249TEST_F(AttachmentBrokerPrivilegedMacMultiProcessTest, InsertSameRightTwice) {
250 SetUpChild("InsertSameRightTwiceClient");
251 mach_msg_type_number_t original_name_count = GetActiveNameCount();
erikchen7c556432015-11-11 22:07:27252 IPC::AttachmentBrokerPrivilegedMac broker(&port_provider_);
erikchenb3481e22015-10-06 19:59:39253
254 // Create some shared memory.
danakj03de39b22016-04-23 04:21:09255 std::unique_ptr<base::SharedMemory> shared_memory =
erikchenb3481e22015-10-06 19:59:39256 CreateAndPopulateSharedMemoryHandle(s_memory_size);
257 ASSERT_TRUE(shared_memory->handle().IsValid());
258
erikchen81543db2015-10-12 17:19:51259 // Insert the memory object into the destination task, via an intermediate
260 // port, twice.
erikchenb3481e22015-10-06 19:59:39261 for (int i = 0; i < 2; ++i) {
erikchen81543db2015-10-12 17:19:51262 IncrementMachRefCount(shared_memory->handle().GetMemoryObject(),
263 MACH_PORT_RIGHT_SEND);
amistrye04cef72016-03-12 04:51:43264 mach_port_name_t inserted_memory_object = base::CreateIntermediateMachPort(
markda902e182015-10-20 18:36:13265 client_task_port_.get(),
266 base::mac::ScopedMachSendRight(
amistrye04cef72016-03-12 04:51:43267 shared_memory->handle().GetMemoryObject()),
268 nullptr);
erikchenb3481e22015-10-06 19:59:39269 EXPECT_NE(inserted_memory_object,
270 static_cast<mach_port_name_t>(MACH_PORT_NULL));
markda902e182015-10-20 18:36:13271 SendUInt32(client_port_.get(), inserted_memory_object);
erikchenb3481e22015-10-06 19:59:39272 }
273
274 // Check that no names have been leaked.
275 shared_memory.reset();
276 EXPECT_EQ(original_name_count, GetActiveNameCount());
277
278 int rv = -1;
279 ASSERT_TRUE(child_process_.WaitForExitWithTimeout(
280 TestTimeouts::action_timeout(), &rv));
281 EXPECT_EQ(0, rv);
282}
283
284MULTIPROCESS_TEST_MAIN(InsertSameRightTwiceClient) {
285 mach_msg_type_number_t original_name_count = 0;
286 base::mac::ScopedMachReceiveRight client_port(
287 CommonChildProcessSetUp(&original_name_count).release());
288
289 // Receive two memory objects.
markda902e182015-10-20 18:36:13290 base::mac::ScopedMachReceiveRight inserted_port(
291 ReceiveUInt32(client_port.get()));
292 base::mac::ScopedMachReceiveRight inserted_port2(
293 ReceiveUInt32(client_port.get()));
294 base::mac::ScopedMachSendRight memory_object(
295 ReceiveMachPort(inserted_port.get()));
erikchenb3481e22015-10-06 19:59:39296 base::mac::ScopedMachSendRight memory_object2(
markda902e182015-10-20 18:36:13297 ReceiveMachPort(inserted_port2.get()));
erikchenb3481e22015-10-06 19:59:39298 inserted_port.reset();
299 inserted_port2.reset();
300
301 // Both rights are for the same Mach port, so only one new name should appear.
302 EXPECT_EQ(original_name_count + 1, GetActiveNameCount());
303
304 // Map both memory objects and check their contents.
danakj03de39b22016-04-23 04:21:09305 std::unique_ptr<base::SharedMemory> shared_memory(MapMemoryObject(
erikchenb3481e22015-10-06 19:59:39306 memory_object.release(),
307 AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
308 char* start = static_cast<char*>(shared_memory->memory());
309 for (int i = 0;
310 i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
311 DCHECK_EQ(start[i], 'a');
312 }
313
danakj03de39b22016-04-23 04:21:09314 std::unique_ptr<base::SharedMemory> shared_memory2(MapMemoryObject(
erikchenb3481e22015-10-06 19:59:39315 memory_object2.release(),
316 AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
317 char* start2 = static_cast<char*>(shared_memory2->memory());
318 for (int i = 0;
319 i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
320 DCHECK_EQ(start2[i], 'a');
321 }
322
323 // Check that the contents of both regions are shared.
324 start[0] = 'b';
325 DCHECK_EQ(start2[0], 'b');
326
327 // After releasing one shared memory region, the name count shouldn't change,
328 // since another reference exists.
329 shared_memory.reset();
330 EXPECT_EQ(original_name_count + 1, GetActiveNameCount());
331
332 // After releasing the second shared memory region, the name count should be
333 // as if no names were ever inserted
334 shared_memory2.reset();
335 EXPECT_EQ(original_name_count, GetActiveNameCount());
336
337 return 0;
338}
339
340// The attachment broker inserts the rights for two memory objects into the
341// destination task.
342TEST_F(AttachmentBrokerPrivilegedMacMultiProcessTest, InsertTwoRights) {
343 SetUpChild("InsertTwoRightsClient");
344 mach_msg_type_number_t original_name_count = GetActiveNameCount();
erikchen7c556432015-11-11 22:07:27345 IPC::AttachmentBrokerPrivilegedMac broker(&port_provider_);
erikchenb3481e22015-10-06 19:59:39346
347 for (int i = 0; i < 2; ++i) {
348 // Create some shared memory.
danakj03de39b22016-04-23 04:21:09349 std::unique_ptr<base::SharedMemory> shared_memory =
erikchenb3481e22015-10-06 19:59:39350 CreateAndPopulateSharedMemoryHandle(s_memory_size);
351 ASSERT_TRUE(shared_memory->handle().IsValid());
352
erikchen81543db2015-10-12 17:19:51353 // Insert the memory object into the destination task, via an intermediate
354 // port.
355 IncrementMachRefCount(shared_memory->handle().GetMemoryObject(),
356 MACH_PORT_RIGHT_SEND);
amistrye04cef72016-03-12 04:51:43357 mach_port_name_t inserted_memory_object = base::CreateIntermediateMachPort(
markda902e182015-10-20 18:36:13358 client_task_port_.get(),
359 base::mac::ScopedMachSendRight(
amistrye04cef72016-03-12 04:51:43360 shared_memory->handle().GetMemoryObject()),
361 nullptr);
erikchenb3481e22015-10-06 19:59:39362 EXPECT_NE(inserted_memory_object,
363 static_cast<mach_port_name_t>(MACH_PORT_NULL));
markda902e182015-10-20 18:36:13364 SendUInt32(client_port_.get(), inserted_memory_object);
erikchenb3481e22015-10-06 19:59:39365 }
366
367 // Check that no names have been leaked.
368 EXPECT_EQ(original_name_count, GetActiveNameCount());
369
370 int rv = -1;
371 ASSERT_TRUE(child_process_.WaitForExitWithTimeout(
372 TestTimeouts::action_timeout(), &rv));
373 EXPECT_EQ(0, rv);
374}
375
376MULTIPROCESS_TEST_MAIN(InsertTwoRightsClient) {
377 mach_msg_type_number_t original_name_count = 0;
378 base::mac::ScopedMachReceiveRight client_port(
379 CommonChildProcessSetUp(&original_name_count).release());
380
381 // Receive two memory objects.
markda902e182015-10-20 18:36:13382 base::mac::ScopedMachReceiveRight inserted_port(
383 ReceiveUInt32(client_port.get()));
384 base::mac::ScopedMachReceiveRight inserted_port2(
385 ReceiveUInt32(client_port.get()));
386 base::mac::ScopedMachSendRight memory_object(
387 ReceiveMachPort(inserted_port.get()));
erikchenb3481e22015-10-06 19:59:39388 base::mac::ScopedMachSendRight memory_object2(
markda902e182015-10-20 18:36:13389 ReceiveMachPort(inserted_port2.get()));
erikchenb3481e22015-10-06 19:59:39390 inserted_port.reset();
391 inserted_port2.reset();
392
393 // There should be two new names to reflect the two new shared memory regions.
394 EXPECT_EQ(original_name_count + 2, GetActiveNameCount());
395
396 // Map both memory objects and check their contents.
danakj03de39b22016-04-23 04:21:09397 std::unique_ptr<base::SharedMemory> shared_memory(MapMemoryObject(
erikchenb3481e22015-10-06 19:59:39398 memory_object.release(),
399 AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
400 char* start = static_cast<char*>(shared_memory->memory());
401 for (int i = 0;
402 i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
403 DCHECK_EQ(start[i], 'a');
404 }
405
danakj03de39b22016-04-23 04:21:09406 std::unique_ptr<base::SharedMemory> shared_memory2(MapMemoryObject(
erikchenb3481e22015-10-06 19:59:39407 memory_object2.release(),
408 AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
409 char* start2 = static_cast<char*>(shared_memory2->memory());
410 for (int i = 0;
411 i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
412 DCHECK_EQ(start2[i], 'a');
413 }
414
415 // Check that the contents of both regions are not shared.
416 start[0] = 'b';
417 DCHECK_EQ(start2[0], 'a');
418
419 // After releasing one shared memory region, the name count should decrement.
420 shared_memory.reset();
421 EXPECT_EQ(original_name_count + 1, GetActiveNameCount());
422 shared_memory2.reset();
423 EXPECT_EQ(original_name_count, GetActiveNameCount());
424
425 return 0;
426}
427
428} // namespace IPC