blob: dbecc735c149c190062045732dda05d96b99ad4a [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>
13
14#include "base/command_line.h"
erikchen328bf3f2015-10-24 00:46:5415#include "base/mac/mac_util.h"
erikchenb3481e22015-10-06 19:59:3916#include "base/mac/mach_logging.h"
amistrye04cef72016-03-12 04:51:4317#include "base/mac/mach_port_util.h"
erikchenb3481e22015-10-06 19:59:3918#include "base/mac/scoped_mach_port.h"
avi246998d82015-12-22 02:39:0419#include "base/macros.h"
erikchenb3481e22015-10-06 19:59:3920#include "base/memory/shared_memory.h"
21#include "base/process/port_provider_mac.h"
22#include "base/process/process_handle.h"
erikchenb3481e22015-10-06 19:59:3923#include "base/sys_info.h"
24#include "base/test/multiprocess_test.h"
25#include "base/test/test_timeouts.h"
erikchen89d74232015-10-12 18:24:0126#include "ipc/test_util_mac.h"
erikchenb3481e22015-10-06 19:59:3927#include "testing/multiprocess_func_list.h"
28
29namespace IPC {
30
31namespace {
32
33static const std::string g_service_switch_name = "service_name";
34
erikchenb3481e22015-10-06 19:59:3935// Sends a uint32_t to a mach port.
36void SendUInt32(mach_port_t port, uint32_t message) {
37 int message_size = sizeof(uint32_t);
38 int total_size = message_size + sizeof(mach_msg_header_t);
39 void* buffer = malloc(total_size);
40 mach_msg_header_t* header = (mach_msg_header_t*)buffer;
41 header->msgh_remote_port = port;
42 header->msgh_local_port = MACH_PORT_NULL;
43 header->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
44 header->msgh_reserved = 0;
erikchenc4c76682015-10-06 23:25:2945 header->msgh_id = 0;
erikchenb3481e22015-10-06 19:59:3946 header->msgh_size = total_size;
47 memcpy(static_cast<char*>(buffer) + sizeof(mach_msg_header_t), &message,
48 message_size);
49
50 kern_return_t kr;
51 kr = mach_msg(static_cast<mach_msg_header_t*>(buffer), MACH_SEND_MSG,
52 total_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
53 MACH_PORT_NULL);
54 MACH_CHECK(kr == KERN_SUCCESS, kr) << "SendUInt32";
55 free(buffer);
56}
57
58// Receives a uint32_t from a mach port.
59uint32_t ReceiveUInt32(mach_port_t listening_port) {
60 int message_size = sizeof(uint32_t);
61 int total_size =
62 message_size + sizeof(mach_msg_header_t) + sizeof(mach_msg_trailer_t);
63 int options = MACH_RCV_MSG;
64 void* buffer = malloc(total_size);
65
66 int kr =
67 mach_msg(static_cast<mach_msg_header_t*>(buffer), options, 0, total_size,
68 listening_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
69 MACH_CHECK(kr == KERN_SUCCESS, kr) << "ReceiveUInt32";
70
71 uint32_t response;
72 memcpy(&response, static_cast<char*>(buffer) + sizeof(mach_msg_header_t),
73 message_size);
74
75 free(buffer);
76 return response;
77}
78
erikchenb3481e22015-10-06 19:59:3979// Sets up the mach communication ports with the server. Returns a port to which
80// the server will send mach objects.
81// |original_name_count| is an output variable that describes the number of
82// active names in this task before the task port is shared with the server.
83base::mac::ScopedMachReceiveRight CommonChildProcessSetUp(
84 mach_msg_type_number_t* original_name_count) {
85 base::CommandLine cmd_line = *base::CommandLine::ForCurrentProcess();
86 std::string service_name =
87 cmd_line.GetSwitchValueASCII(g_service_switch_name);
88 base::mac::ScopedMachSendRight server_port(
89 LookupServer(service_name.c_str()));
90 base::mac::ScopedMachReceiveRight client_port(MakeReceivingPort());
91
92 // |server_port| is a newly allocated right which will be deallocated once
93 // this method returns.
94 *original_name_count = GetActiveNameCount() - 1;
95
96 // Send the port that this process is listening on to the server.
markda902e182015-10-20 18:36:1397 SendMachPort(server_port.get(), client_port.get(), MACH_MSG_TYPE_MAKE_SEND);
erikchenb3481e22015-10-06 19:59:3998
99 // Send the task port for this process.
markda902e182015-10-20 18:36:13100 SendMachPort(server_port.get(), mach_task_self(), MACH_MSG_TYPE_COPY_SEND);
erikchenb3481e22015-10-06 19:59:39101 return client_port;
102}
103
104// Creates a new shared memory region populated with 'a'.
105scoped_ptr<base::SharedMemory> CreateAndPopulateSharedMemoryHandle(
106 size_t size) {
107 base::SharedMemoryHandle shm(size);
108 scoped_ptr<base::SharedMemory> shared_memory(
109 new base::SharedMemory(shm, false));
110 shared_memory->Map(size);
111 memset(shared_memory->memory(), 'a', size);
112 return shared_memory;
113}
114
115// Create a shared memory region from a memory object. The returned object takes
116// ownership of |memory_object|.
117scoped_ptr<base::SharedMemory> MapMemoryObject(mach_port_t memory_object,
118 size_t size) {
119 base::SharedMemoryHandle shm(memory_object, size, base::GetCurrentProcId());
120 scoped_ptr<base::SharedMemory> shared_memory(
121 new base::SharedMemory(shm, false));
122 shared_memory->Map(size);
123 return shared_memory;
124}
125
erikchen7c556432015-11-11 22:07:27126class MockPortProvider : public base::PortProvider {
127 public:
128 MockPortProvider() {}
129 ~MockPortProvider() override {}
130 mach_port_t TaskForPid(base::ProcessHandle process) const override {
131 return MACH_PORT_NULL;
132 }
133};
134
erikchenb3481e22015-10-06 19:59:39135} // namespace
136
137class AttachmentBrokerPrivilegedMacMultiProcessTest
138 : public base::MultiProcessTest {
139 public:
140 AttachmentBrokerPrivilegedMacMultiProcessTest() {}
141
142 base::CommandLine MakeCmdLine(const std::string& procname) override {
143 base::CommandLine command_line = MultiProcessTest::MakeCmdLine(procname);
144 // Pass the service name to the child process.
145 command_line.AppendSwitchASCII(g_service_switch_name, service_name_);
146 return command_line;
147 }
148
149 void SetUpChild(const std::string& name) {
150 // Make a random service name so that this test doesn't conflict with other
151 // similar tests.
152 service_name_ = CreateRandomServiceName();
153 server_port_.reset(BecomeMachServer(service_name_.c_str()).release());
154 child_process_ = SpawnChild(name);
markda902e182015-10-20 18:36:13155 client_port_.reset(ReceiveMachPort(server_port_.get()).release());
156 client_task_port_.reset(ReceiveMachPort(server_port_.get()).release());
erikchenb3481e22015-10-06 19:59:39157 }
158
159 static const int s_memory_size = 99999;
160
161 protected:
162 std::string service_name_;
163
164 // A port on which the main process listens for mach messages from the child
165 // process.
166 base::mac::ScopedMachReceiveRight server_port_;
167
168 // A port on which the child process listens for mach messages from the main
169 // process.
170 base::mac::ScopedMachSendRight client_port_;
171
172 // Child process's task port.
173 base::mac::ScopedMachSendRight client_task_port_;
174
erikchen7c556432015-11-11 22:07:27175 // Dummy port provider.
176 MockPortProvider port_provider_;
177
erikchenb3481e22015-10-06 19:59:39178 base::Process child_process_;
179 DISALLOW_COPY_AND_ASSIGN(AttachmentBrokerPrivilegedMacMultiProcessTest);
180};
181
182// The attachment broker inserts a right for a memory object into the
183// destination task.
184TEST_F(AttachmentBrokerPrivilegedMacMultiProcessTest, InsertRight) {
erikchen328bf3f2015-10-24 00:46:54185 // Mach-based SharedMemory isn't support on OSX 10.6.
186 if (base::mac::IsOSSnowLeopard())
187 return;
188
erikchenb3481e22015-10-06 19:59:39189 SetUpChild("InsertRightClient");
190 mach_msg_type_number_t original_name_count = GetActiveNameCount();
erikchen7c556432015-11-11 22:07:27191 IPC::AttachmentBrokerPrivilegedMac broker(&port_provider_);
erikchenb3481e22015-10-06 19:59:39192
193 // Create some shared memory.
194 scoped_ptr<base::SharedMemory> shared_memory =
195 CreateAndPopulateSharedMemoryHandle(s_memory_size);
196 ASSERT_TRUE(shared_memory->handle().IsValid());
197
erikchen81543db2015-10-12 17:19:51198 // Insert the memory object into the destination task, via an intermediate
199 // port.
200 IncrementMachRefCount(shared_memory->handle().GetMemoryObject(),
201 MACH_PORT_RIGHT_SEND);
amistrye04cef72016-03-12 04:51:43202 mach_port_name_t inserted_memory_object = base::CreateIntermediateMachPort(
203 client_task_port_.get(),
204 base::mac::ScopedMachSendRight(shared_memory->handle().GetMemoryObject()),
205 nullptr);
erikchenb3481e22015-10-06 19:59:39206 EXPECT_NE(inserted_memory_object,
207 static_cast<mach_port_name_t>(MACH_PORT_NULL));
markda902e182015-10-20 18:36:13208 SendUInt32(client_port_.get(), inserted_memory_object);
erikchenb3481e22015-10-06 19:59:39209
210 // Check that no names have been leaked.
211 shared_memory.reset();
212 EXPECT_EQ(original_name_count, GetActiveNameCount());
213
214 int rv = -1;
215 ASSERT_TRUE(child_process_.WaitForExitWithTimeout(
216 TestTimeouts::action_timeout(), &rv));
217 EXPECT_EQ(0, rv);
218}
219
220MULTIPROCESS_TEST_MAIN(InsertRightClient) {
221 mach_msg_type_number_t original_name_count = 0;
222 base::mac::ScopedMachReceiveRight client_port(
223 CommonChildProcessSetUp(&original_name_count).release());
markda902e182015-10-20 18:36:13224 base::mac::ScopedMachReceiveRight inserted_port(
225 ReceiveUInt32(client_port.get()));
226 base::mac::ScopedMachSendRight memory_object(
227 ReceiveMachPort(inserted_port.get()));
erikchenb3481e22015-10-06 19:59:39228 inserted_port.reset();
229
230 // The server should have inserted a right into this process.
231 EXPECT_EQ(original_name_count + 1, GetActiveNameCount());
232
233 // Map the memory object and check its contents.
234 scoped_ptr<base::SharedMemory> shared_memory(MapMemoryObject(
235 memory_object.release(),
236 AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
237 const char* start = static_cast<const char*>(shared_memory->memory());
238 for (int i = 0;
239 i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
240 DCHECK_EQ(start[i], 'a');
241 }
242
243 // Check that no names have been leaked.
244 shared_memory.reset();
245 EXPECT_EQ(original_name_count, GetActiveNameCount());
246
247 return 0;
248}
249
250// The attachment broker inserts the right for a memory object into the
251// destination task twice.
252TEST_F(AttachmentBrokerPrivilegedMacMultiProcessTest, InsertSameRightTwice) {
erikchen328bf3f2015-10-24 00:46:54253 // Mach-based SharedMemory isn't support on OSX 10.6.
254 if (base::mac::IsOSSnowLeopard())
255 return;
256
erikchenb3481e22015-10-06 19:59:39257 SetUpChild("InsertSameRightTwiceClient");
258 mach_msg_type_number_t original_name_count = GetActiveNameCount();
erikchen7c556432015-11-11 22:07:27259 IPC::AttachmentBrokerPrivilegedMac broker(&port_provider_);
erikchenb3481e22015-10-06 19:59:39260
261 // Create some shared memory.
262 scoped_ptr<base::SharedMemory> shared_memory =
263 CreateAndPopulateSharedMemoryHandle(s_memory_size);
264 ASSERT_TRUE(shared_memory->handle().IsValid());
265
erikchen81543db2015-10-12 17:19:51266 // Insert the memory object into the destination task, via an intermediate
267 // port, twice.
erikchenb3481e22015-10-06 19:59:39268 for (int i = 0; i < 2; ++i) {
erikchen81543db2015-10-12 17:19:51269 IncrementMachRefCount(shared_memory->handle().GetMemoryObject(),
270 MACH_PORT_RIGHT_SEND);
amistrye04cef72016-03-12 04:51:43271 mach_port_name_t inserted_memory_object = base::CreateIntermediateMachPort(
markda902e182015-10-20 18:36:13272 client_task_port_.get(),
273 base::mac::ScopedMachSendRight(
amistrye04cef72016-03-12 04:51:43274 shared_memory->handle().GetMemoryObject()),
275 nullptr);
erikchenb3481e22015-10-06 19:59:39276 EXPECT_NE(inserted_memory_object,
277 static_cast<mach_port_name_t>(MACH_PORT_NULL));
markda902e182015-10-20 18:36:13278 SendUInt32(client_port_.get(), inserted_memory_object);
erikchenb3481e22015-10-06 19:59:39279 }
280
281 // Check that no names have been leaked.
282 shared_memory.reset();
283 EXPECT_EQ(original_name_count, GetActiveNameCount());
284
285 int rv = -1;
286 ASSERT_TRUE(child_process_.WaitForExitWithTimeout(
287 TestTimeouts::action_timeout(), &rv));
288 EXPECT_EQ(0, rv);
289}
290
291MULTIPROCESS_TEST_MAIN(InsertSameRightTwiceClient) {
292 mach_msg_type_number_t original_name_count = 0;
293 base::mac::ScopedMachReceiveRight client_port(
294 CommonChildProcessSetUp(&original_name_count).release());
295
296 // Receive two memory objects.
markda902e182015-10-20 18:36:13297 base::mac::ScopedMachReceiveRight inserted_port(
298 ReceiveUInt32(client_port.get()));
299 base::mac::ScopedMachReceiveRight inserted_port2(
300 ReceiveUInt32(client_port.get()));
301 base::mac::ScopedMachSendRight memory_object(
302 ReceiveMachPort(inserted_port.get()));
erikchenb3481e22015-10-06 19:59:39303 base::mac::ScopedMachSendRight memory_object2(
markda902e182015-10-20 18:36:13304 ReceiveMachPort(inserted_port2.get()));
erikchenb3481e22015-10-06 19:59:39305 inserted_port.reset();
306 inserted_port2.reset();
307
308 // Both rights are for the same Mach port, so only one new name should appear.
309 EXPECT_EQ(original_name_count + 1, GetActiveNameCount());
310
311 // Map both memory objects and check their contents.
312 scoped_ptr<base::SharedMemory> shared_memory(MapMemoryObject(
313 memory_object.release(),
314 AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
315 char* start = static_cast<char*>(shared_memory->memory());
316 for (int i = 0;
317 i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
318 DCHECK_EQ(start[i], 'a');
319 }
320
321 scoped_ptr<base::SharedMemory> shared_memory2(MapMemoryObject(
322 memory_object2.release(),
323 AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
324 char* start2 = static_cast<char*>(shared_memory2->memory());
325 for (int i = 0;
326 i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
327 DCHECK_EQ(start2[i], 'a');
328 }
329
330 // Check that the contents of both regions are shared.
331 start[0] = 'b';
332 DCHECK_EQ(start2[0], 'b');
333
334 // After releasing one shared memory region, the name count shouldn't change,
335 // since another reference exists.
336 shared_memory.reset();
337 EXPECT_EQ(original_name_count + 1, GetActiveNameCount());
338
339 // After releasing the second shared memory region, the name count should be
340 // as if no names were ever inserted
341 shared_memory2.reset();
342 EXPECT_EQ(original_name_count, GetActiveNameCount());
343
344 return 0;
345}
346
347// The attachment broker inserts the rights for two memory objects into the
348// destination task.
349TEST_F(AttachmentBrokerPrivilegedMacMultiProcessTest, InsertTwoRights) {
erikchen328bf3f2015-10-24 00:46:54350 // Mach-based SharedMemory isn't support on OSX 10.6.
351 if (base::mac::IsOSSnowLeopard())
352 return;
353
erikchenb3481e22015-10-06 19:59:39354 SetUpChild("InsertTwoRightsClient");
355 mach_msg_type_number_t original_name_count = GetActiveNameCount();
erikchen7c556432015-11-11 22:07:27356 IPC::AttachmentBrokerPrivilegedMac broker(&port_provider_);
erikchenb3481e22015-10-06 19:59:39357
358 for (int i = 0; i < 2; ++i) {
359 // Create some shared memory.
360 scoped_ptr<base::SharedMemory> shared_memory =
361 CreateAndPopulateSharedMemoryHandle(s_memory_size);
362 ASSERT_TRUE(shared_memory->handle().IsValid());
363
erikchen81543db2015-10-12 17:19:51364 // Insert the memory object into the destination task, via an intermediate
365 // port.
366 IncrementMachRefCount(shared_memory->handle().GetMemoryObject(),
367 MACH_PORT_RIGHT_SEND);
amistrye04cef72016-03-12 04:51:43368 mach_port_name_t inserted_memory_object = base::CreateIntermediateMachPort(
markda902e182015-10-20 18:36:13369 client_task_port_.get(),
370 base::mac::ScopedMachSendRight(
amistrye04cef72016-03-12 04:51:43371 shared_memory->handle().GetMemoryObject()),
372 nullptr);
erikchenb3481e22015-10-06 19:59:39373 EXPECT_NE(inserted_memory_object,
374 static_cast<mach_port_name_t>(MACH_PORT_NULL));
markda902e182015-10-20 18:36:13375 SendUInt32(client_port_.get(), inserted_memory_object);
erikchenb3481e22015-10-06 19:59:39376 }
377
378 // Check that no names have been leaked.
379 EXPECT_EQ(original_name_count, GetActiveNameCount());
380
381 int rv = -1;
382 ASSERT_TRUE(child_process_.WaitForExitWithTimeout(
383 TestTimeouts::action_timeout(), &rv));
384 EXPECT_EQ(0, rv);
385}
386
387MULTIPROCESS_TEST_MAIN(InsertTwoRightsClient) {
388 mach_msg_type_number_t original_name_count = 0;
389 base::mac::ScopedMachReceiveRight client_port(
390 CommonChildProcessSetUp(&original_name_count).release());
391
392 // Receive two memory objects.
markda902e182015-10-20 18:36:13393 base::mac::ScopedMachReceiveRight inserted_port(
394 ReceiveUInt32(client_port.get()));
395 base::mac::ScopedMachReceiveRight inserted_port2(
396 ReceiveUInt32(client_port.get()));
397 base::mac::ScopedMachSendRight memory_object(
398 ReceiveMachPort(inserted_port.get()));
erikchenb3481e22015-10-06 19:59:39399 base::mac::ScopedMachSendRight memory_object2(
markda902e182015-10-20 18:36:13400 ReceiveMachPort(inserted_port2.get()));
erikchenb3481e22015-10-06 19:59:39401 inserted_port.reset();
402 inserted_port2.reset();
403
404 // There should be two new names to reflect the two new shared memory regions.
405 EXPECT_EQ(original_name_count + 2, GetActiveNameCount());
406
407 // Map both memory objects and check their contents.
408 scoped_ptr<base::SharedMemory> shared_memory(MapMemoryObject(
409 memory_object.release(),
410 AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
411 char* start = static_cast<char*>(shared_memory->memory());
412 for (int i = 0;
413 i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
414 DCHECK_EQ(start[i], 'a');
415 }
416
417 scoped_ptr<base::SharedMemory> shared_memory2(MapMemoryObject(
418 memory_object2.release(),
419 AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
420 char* start2 = static_cast<char*>(shared_memory2->memory());
421 for (int i = 0;
422 i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
423 DCHECK_EQ(start2[i], 'a');
424 }
425
426 // Check that the contents of both regions are not shared.
427 start[0] = 'b';
428 DCHECK_EQ(start2[0], 'a');
429
430 // After releasing one shared memory region, the name count should decrement.
431 shared_memory.reset();
432 EXPECT_EQ(original_name_count + 1, GetActiveNameCount());
433 shared_memory2.reset();
434 EXPECT_EQ(original_name_count, GetActiveNameCount());
435
436 return 0;
437}
438
439} // namespace IPC