OLD | NEW |
---|---|
(Empty) | |
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 "build/build_config.h" | |
6 | |
7 #include <fcntl.h> | |
8 #include <sys/mman.h> | |
9 | |
10 #include "base/command_line.h" | |
11 #include "base/files/file_util.h" | |
12 #include "base/files/scoped_file.h" | |
13 #include "base/files/scoped_temp_dir.h" | |
14 #include "base/memory/scoped_ptr.h" | |
15 #include "base/memory/shared_memory.h" | |
16 #include "ipc/attachment_broker_privileged_mac.h" | |
17 #include "ipc/attachment_broker_unprivileged_mac.h" | |
18 #include "ipc/ipc_listener.h" | |
19 #include "ipc/ipc_message.h" | |
20 #include "ipc/ipc_test_base.h" | |
21 #include "ipc/ipc_test_messages.h" | |
22 #include "ipc/test_util_mac.h" | |
23 | |
24 namespace { | |
25 | |
26 const char kDataBuffer1[] = "This is some test data to write to the file."; | |
27 const char kDataBuffer2[] = "Katy Perry is so cool."; | |
Tom Sepez
2015/10/09 22:38:47
nit: some may frown on this level of colloquialism
erikchen
2015/10/09 23:14:43
I kept the flippancy but removed the proper nouns.
| |
28 const char kDataBuffer3[] = "Taylor Swift is so cool."; | |
29 const char kDataBuffer4[] = "Justin Bieber is so cool."; | |
30 const std::string g_service_switch_name = "service_name"; | |
31 | |
32 enum TestResult { | |
33 RESULT_UNKNOWN, | |
34 RESULT_SUCCESS, | |
35 RESULT_FAILURE, | |
36 }; | |
37 | |
38 base::mac::ScopedMachSendRight GetMachPortFromBrokeredAttachment( | |
39 const scoped_refptr<IPC::BrokerableAttachment>& attachment) { | |
40 if (attachment->GetType() != | |
41 IPC::BrokerableAttachment::TYPE_BROKERABLE_ATTACHMENT) { | |
42 LOG(INFO) << "Attachment type not TYPE_BROKERABLE_ATTACHMENT."; | |
43 return base::mac::ScopedMachSendRight(MACH_PORT_NULL); | |
44 } | |
45 | |
46 if (attachment->GetBrokerableType() != IPC::BrokerableAttachment::MACH_PORT) { | |
47 LOG(INFO) << "Brokerable type not MACH_PORT."; | |
48 return base::mac::ScopedMachSendRight(MACH_PORT_NULL); | |
49 } | |
50 | |
51 IPC::internal::MachPortAttachmentMac* received_mach_port_attachment = | |
52 static_cast<IPC::internal::MachPortAttachmentMac*>(attachment.get()); | |
53 return base::mac::ScopedMachSendRight( | |
54 received_mach_port_attachment->get_mach_port()); | |
55 } | |
56 | |
57 // Makes a Mach port backed SharedMemory region and fills it with |contents|. | |
58 scoped_ptr<base::SharedMemory> MakeSharedMemory(const std::string& contents) { | |
59 base::SharedMemoryHandle shm(contents.size()); | |
60 if (!shm.IsValid()) { | |
61 LOG(ERROR) << "Failed to make SharedMemoryHandle."; | |
62 return nullptr; | |
63 } | |
64 scoped_ptr<base::SharedMemory> shared_memory( | |
65 new base::SharedMemory(shm, false)); | |
66 shared_memory->Map(contents.size()); | |
67 memcpy(shared_memory->memory(), contents.c_str(), contents.size()); | |
68 return shared_memory; | |
69 } | |
70 | |
71 // |message| must be deserializable as a TestSharedMemoryHandleMsg1. | |
72 base::SharedMemoryHandle GetSharedMemoryHandleFromMsg1( | |
73 const IPC::Message& message) { | |
74 // Expect a message with a brokered attachment. | |
75 if (!message.HasBrokerableAttachments()) { | |
76 LOG(ERROR) << "Message missing brokerable attachment."; | |
77 return base::SharedMemoryHandle(); | |
78 } | |
79 | |
80 TestSharedMemoryHandleMsg1::Schema::Param p; | |
81 if (!TestSharedMemoryHandleMsg1::Read(&message, &p)) { | |
82 LOG(ERROR) << "Failed to deserialize message."; | |
83 return base::SharedMemoryHandle(); | |
84 } | |
85 | |
86 return base::get<1>(p); | |
87 } | |
88 | |
89 // |message| must be deserializable as a TestSharedMemoryHandleMsg2. Returns | |
90 // whether deserialization was successful. |handle1| and |handle2| are output | |
91 // variables populated on success. | |
92 bool GetSharedMemoryHandlesFromMsg2(const IPC::Message& message, | |
93 base::SharedMemoryHandle* handle1, | |
94 base::SharedMemoryHandle* handle2) { | |
95 // Expect a message with a brokered attachment. | |
96 if (!message.HasBrokerableAttachments()) { | |
97 LOG(ERROR) << "Message missing brokerable attachment."; | |
98 return false; | |
99 } | |
100 | |
101 TestSharedMemoryHandleMsg2::Schema::Param p; | |
102 if (!TestSharedMemoryHandleMsg2::Read(&message, &p)) { | |
103 LOG(ERROR) << "Failed to deserialize message."; | |
104 return false; | |
105 } | |
106 | |
107 *handle1 = base::get<0>(p); | |
108 *handle2 = base::get<1>(p); | |
109 return true; | |
110 } | |
111 | |
112 // Returns |nullptr| on error. | |
113 scoped_ptr<base::SharedMemory> MapSharedMemoryHandle( | |
114 const base::SharedMemoryHandle& shm) { | |
115 if (!shm.IsValid()) { | |
116 LOG(ERROR) << "Invalid SharedMemoryHandle"; | |
117 return nullptr; | |
118 } | |
119 | |
120 size_t size; | |
121 if (!shm.GetSize(&size)) { | |
122 LOG(ERROR) << "Couldn't get size of SharedMemoryHandle"; | |
123 return nullptr; | |
124 } | |
125 | |
126 scoped_ptr<base::SharedMemory> shared_memory( | |
127 new base::SharedMemory(shm, false)); | |
128 shared_memory->Map(size); | |
129 return shared_memory; | |
130 } | |
131 | |
132 // This method maps the SharedMemoryHandle, checks the contents, and then | |
133 // consumes a reference to the underlying Mach port. | |
134 bool CheckContentsOfSharedMemoryHandle(const base::SharedMemoryHandle& shm, | |
135 const std::string& contents) { | |
136 scoped_ptr<base::SharedMemory> shared_memory(MapSharedMemoryHandle(shm)); | |
137 | |
138 if (memcmp(shared_memory->memory(), contents.c_str(), contents.size()) != 0) { | |
139 LOG(ERROR) << "Shared Memory contents not equivalent"; | |
140 return false; | |
141 } | |
142 return true; | |
143 } | |
144 | |
145 // This method mmaps the FileDescriptor, checks the contents, and then munmaps | |
146 // the FileDescriptor and closes the underlying fd. | |
147 bool CheckContentsOfFileDescriptor(const base::FileDescriptor& file_descriptor, | |
148 const std::string& contents) { | |
149 base::ScopedFD fd_closer(file_descriptor.fd); | |
150 lseek(file_descriptor.fd, 0, SEEK_SET); | |
151 char* buffer = static_cast<char*>(malloc(contents.size())); | |
Tom Sepez
2015/10/09 22:38:47
nit: scoped pointers of some flavor would help her
erikchen
2015/10/09 23:14:43
Done.
| |
152 if (!base::ReadFromFD(file_descriptor.fd, buffer, contents.size())) { | |
153 free(buffer); | |
154 return false; | |
155 } | |
156 | |
157 int result = memcmp(buffer, contents.c_str(), contents.size()); | |
158 free(buffer); | |
159 return result == 0; | |
160 } | |
161 | |
162 // Open |fp| and populate it with |contents|. | |
163 base::FileDescriptor MakeFileDescriptor(const base::FilePath& fp, | |
164 const std::string& contents) { | |
165 int fd = open(fp.value().c_str(), O_RDWR, S_IWUSR | S_IRUSR); | |
166 base::ScopedFD fd_closer(fd); | |
167 if (fd <= 0) { | |
168 LOG(ERROR) << "Error opening file at: " << fp.value(); | |
169 return base::FileDescriptor(); | |
170 } | |
171 | |
172 if (lseek(fd, 0, SEEK_SET) != 0) { | |
173 LOG(ERROR) << "Error changing offset"; | |
174 return base::FileDescriptor(); | |
175 } | |
176 | |
177 if (write(fd, contents.c_str(), contents.size()) != | |
178 static_cast<ssize_t>(contents.size())) { | |
179 LOG(ERROR) << "Error writing to file"; | |
180 return base::FileDescriptor(); | |
181 } | |
182 | |
183 return base::FileDescriptor(fd_closer.release(), true); | |
184 } | |
185 | |
186 // Maps both handles, then checks that their contents matches |contents|. Then | |
187 // checks that changes to one are reflected in the other. Then consumes | |
188 // references to both underlying Mach ports. | |
189 bool CheckContentsOfTwoEquivalentSharedMemoryHandles( | |
190 const base::SharedMemoryHandle& handle1, | |
191 const base::SharedMemoryHandle& handle2, | |
192 const std::string& contents) { | |
193 scoped_ptr<base::SharedMemory> shared_memory1(MapSharedMemoryHandle(handle1)); | |
194 scoped_ptr<base::SharedMemory> shared_memory2(MapSharedMemoryHandle(handle2)); | |
195 | |
196 if (memcmp(shared_memory1->memory(), contents.c_str(), contents.size()) != | |
197 0) { | |
198 LOG(ERROR) << "Incorrect contents in shared_memory1"; | |
199 return false; | |
200 } | |
201 | |
202 if (memcmp(shared_memory1->memory(), shared_memory2->memory(), | |
203 contents.size()) != 0) { | |
204 LOG(ERROR) << "Incorrect contents in shared_memory2"; | |
205 return false; | |
206 } | |
207 | |
208 // Updating shared_memory1 should update shared_memory2. | |
209 char* start = static_cast<char*>(shared_memory1->memory()); | |
Tom Sepez
2015/10/09 22:38:47
You may need volatiles and memory barriers here, i
erikchen
2015/10/09 23:14:43
Given that the store/load are happening on the sam
| |
210 start[0] += 5; | |
211 if (memcmp(shared_memory1->memory(), shared_memory2->memory(), | |
Tom Sepez
2015/10/09 22:38:47
Would like a better test, eg. blat a known string
erikchen
2015/10/09 23:14:43
Done.
| |
212 contents.size()) != 0) { | |
213 LOG(ERROR) << "Incorrect contents in shared_memory2"; | |
214 return false; | |
215 } | |
216 | |
217 return true; | |
218 } | |
219 | |
220 // |message| must be deserializable as a TestSharedMemoryHandleMsg1. Returns | |
221 // whether the contents of the attached shared memory region matches |contents|. | |
222 // Consumes a reference to the underlying Mach port. | |
223 bool CheckContentsOfMessage1(const IPC::Message& message, | |
224 const std::string& contents) { | |
225 base::SharedMemoryHandle shm(GetSharedMemoryHandleFromMsg1(message)); | |
226 return CheckContentsOfSharedMemoryHandle(shm, contents); | |
227 } | |
228 | |
229 // Once the test is finished, send a control message to the parent process with | |
230 // the result. The message may require the runloop to be run before its | |
231 // dispatched. | |
232 void SendControlMessage(IPC::Sender* sender, bool success) { | |
233 IPC::Message* message = new IPC::Message(0, 2, IPC::Message::PRIORITY_NORMAL); | |
234 TestResult result = success ? RESULT_SUCCESS : RESULT_FAILURE; | |
235 message->WriteInt(result); | |
236 sender->Send(message); | |
237 } | |
238 | |
239 // Records the most recently received brokerable attachment's id. | |
240 class AttachmentBrokerObserver : public IPC::AttachmentBroker::Observer { | |
241 public: | |
242 void ReceivedBrokerableAttachmentWithId( | |
243 const IPC::BrokerableAttachment::AttachmentId& id) override { | |
244 id_ = id; | |
245 } | |
246 IPC::BrokerableAttachment::AttachmentId* get_id() { return &id_; } | |
247 | |
248 private: | |
249 IPC::BrokerableAttachment::AttachmentId id_; | |
250 }; | |
251 | |
252 // A broker which always sets the current process as the destination process | |
253 // for attachments. | |
254 class MockBroker : public IPC::AttachmentBrokerUnprivilegedMac { | |
255 public: | |
256 MockBroker() {} | |
257 ~MockBroker() override {} | |
258 bool SendAttachmentToProcess(IPC::BrokerableAttachment* attachment, | |
259 base::ProcessId destination_process) override { | |
260 return IPC::AttachmentBrokerUnprivilegedMac::SendAttachmentToProcess( | |
261 attachment, base::Process::Current().Pid()); | |
262 } | |
263 }; | |
264 | |
265 // Forwards all messages to |listener_|. Quits the message loop after a | |
266 // message is received, or the channel has an error. | |
267 class ProxyListener : public IPC::Listener { | |
268 public: | |
269 ProxyListener() : listener_(nullptr), reason_(MESSAGE_RECEIVED) {} | |
270 ~ProxyListener() override {} | |
271 | |
272 // The reason for exiting the message loop. | |
273 enum Reason { MESSAGE_RECEIVED, CHANNEL_ERROR }; | |
274 | |
275 bool OnMessageReceived(const IPC::Message& message) override { | |
276 bool result = false; | |
277 if (listener_) | |
278 result = listener_->OnMessageReceived(message); | |
279 reason_ = MESSAGE_RECEIVED; | |
280 messages_.push_back(message); | |
281 base::MessageLoop::current()->QuitNow(); | |
282 return result; | |
283 } | |
284 | |
285 void OnChannelError() override { | |
286 reason_ = CHANNEL_ERROR; | |
287 base::MessageLoop::current()->QuitNow(); | |
288 } | |
289 | |
290 void set_listener(IPC::Listener* listener) { listener_ = listener; } | |
291 Reason get_reason() { return reason_; } | |
292 IPC::Message get_first_message() { return messages_[0]; } | |
Tom Sepez
2015/10/09 22:38:47
these two should probably assert !empty.
erikchen
2015/10/09 23:14:43
Done.
| |
293 void pop_first_message() { messages_.erase(messages_.begin()); } | |
294 bool has_message() { return !messages_.empty(); } | |
295 | |
296 private: | |
297 IPC::Listener* listener_; | |
298 Reason reason_; | |
299 std::vector<IPC::Message> messages_; | |
300 }; | |
301 | |
302 // Waits for a result to be sent over the channel. Quits the message loop | |
303 // after a message is received, or the channel has an error. | |
304 class ResultListener : public IPC::Listener { | |
305 public: | |
306 ResultListener() : result_(RESULT_UNKNOWN) {} | |
307 ~ResultListener() override {} | |
308 | |
309 bool OnMessageReceived(const IPC::Message& message) override { | |
310 base::PickleIterator iter(message); | |
311 | |
312 int result; | |
313 EXPECT_TRUE(iter.ReadInt(&result)); | |
314 result_ = static_cast<TestResult>(result); | |
315 return true; | |
316 } | |
317 | |
318 TestResult get_result() { return result_; } | |
319 | |
320 private: | |
321 TestResult result_; | |
322 }; | |
323 | |
324 class MockPortProvider : public base::PortProvider { | |
325 public: | |
326 mach_port_t TaskForPid(base::ProcessHandle process) const override { | |
327 auto it = port_map_.find(process); | |
328 if (it != port_map_.end()) | |
329 return it->second; | |
330 return MACH_PORT_NULL; | |
331 } | |
332 | |
333 void InsertEntry(base::ProcessHandle process, mach_port_t task_port) { | |
334 port_map_[process] = task_port; | |
335 } | |
336 | |
337 private: | |
338 std::map<base::ProcessHandle, mach_port_t> port_map_; | |
339 }; | |
340 | |
341 // End-to-end tests for the attachment brokering process on Mac. | |
342 // The parent process acts as an unprivileged process. The child process acts | |
343 // as the privileged process. | |
344 class IPCAttachmentBrokerMacTest : public IPCTestBase { | |
345 public: | |
346 IPCAttachmentBrokerMacTest() {} | |
347 ~IPCAttachmentBrokerMacTest() override {} | |
348 | |
349 base::CommandLine MakeCmdLine(const std::string& procname) override { | |
350 base::CommandLine command_line = IPCTestBase::MakeCmdLine(procname); | |
351 // Pass the service name to the child process. | |
352 command_line.AppendSwitchASCII(g_service_switch_name, service_name_); | |
353 return command_line; | |
354 } | |
355 | |
356 // Takes ownership of |broker|. Has no effect if called after CommonSetUp(). | |
357 void SetBroker(IPC::AttachmentBrokerUnprivilegedMac* broker) { | |
358 broker_.reset(broker); | |
359 } | |
360 | |
361 // Setup shared between tests. | |
362 void CommonSetUp(const char* name) { | |
363 Init(name); | |
364 service_name_ = IPC::CreateRandomServiceName(); | |
365 server_port_.reset(IPC::BecomeMachServer(service_name_.c_str()).release()); | |
366 | |
367 if (!broker_.get()) | |
368 SetBroker(new IPC::AttachmentBrokerUnprivilegedMac); | |
369 | |
370 broker_->AddObserver(&observer_); | |
371 CreateChannel(&proxy_listener_); | |
372 broker_->DesignateBrokerCommunicationChannel(channel()); | |
373 ASSERT_TRUE(ConnectChannel()); | |
374 ASSERT_TRUE(StartClient()); | |
375 | |
376 client_port_.reset(IPC::ReceiveMachPort(server_port_).release()); | |
377 IPC::SendMachPort(client_port_, mach_task_self(), MACH_MSG_TYPE_COPY_SEND); | |
378 active_names_at_start_ = IPC::GetActiveNameCount(); | |
379 get_proxy_listener()->set_listener(&result_listener_); | |
380 } | |
381 | |
382 void CheckChildResult() { | |
383 ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED, | |
384 get_proxy_listener()->get_reason()); | |
385 ASSERT_EQ(get_result_listener()->get_result(), RESULT_SUCCESS); | |
386 } | |
387 | |
388 void FinalCleanUp() { | |
389 // There should be no leaked names. | |
390 EXPECT_EQ(active_names_at_start_, IPC::GetActiveNameCount()); | |
391 | |
392 // Close the channel so the client's OnChannelError() gets fired. | |
393 channel()->Close(); | |
394 | |
395 EXPECT_TRUE(WaitForClientShutdown()); | |
396 DestroyChannel(); | |
397 broker_.reset(); | |
398 } | |
399 | |
400 // Teardown shared between most tests. | |
401 void CommonTearDown() { | |
402 CheckChildResult(); | |
403 FinalCleanUp(); | |
404 } | |
405 | |
406 // Makes a SharedMemory region, fills it with |contents|, sends the handle | |
407 // over Chrome IPC, and unmaps the region. | |
408 void SendMessage1(const std::string& contents) { | |
409 scoped_ptr<base::SharedMemory> shared_memory(MakeSharedMemory(contents)); | |
410 IPC::Message* message = | |
411 new TestSharedMemoryHandleMsg1(100, shared_memory->handle(), 200); | |
412 sender()->Send(message); | |
413 } | |
414 | |
415 ProxyListener* get_proxy_listener() { return &proxy_listener_; } | |
416 IPC::AttachmentBrokerUnprivilegedMac* get_broker() { return broker_.get(); } | |
417 AttachmentBrokerObserver* get_observer() { return &observer_; } | |
418 ResultListener* get_result_listener() { return &result_listener_; } | |
419 | |
420 private: | |
421 ProxyListener proxy_listener_; | |
422 scoped_ptr<IPC::AttachmentBrokerUnprivilegedMac> broker_; | |
423 AttachmentBrokerObserver observer_; | |
424 | |
425 // A port on which the main process listens for mach messages from the child | |
426 // process. | |
427 base::mac::ScopedMachReceiveRight server_port_; | |
428 | |
429 // A port on which the child process listens for mach messages from the main | |
430 // process. | |
431 base::mac::ScopedMachSendRight client_port_; | |
432 | |
433 // The number of active names immediately after set up. | |
434 mach_msg_type_number_t active_names_at_start_; | |
435 | |
436 std::string service_name_; | |
437 | |
438 ResultListener result_listener_; | |
439 }; | |
440 | |
441 using OnMessageReceivedCallback = void (*)(IPC::Sender* sender, | |
442 const IPC::Message& message); | |
443 | |
444 // These objects are globally accessible, and are expected to outlive all IPC | |
445 // Channels. | |
446 struct ChildProcessGlobals { | |
447 IPC::AttachmentBrokerPrivilegedMac broker; | |
448 MockPortProvider port_provider; | |
449 base::mac::ScopedMachSendRight server_task_port; | |
450 }; | |
451 | |
452 // Sets up the Mach communication ports with the server. Returns a set of | |
453 // globals that must live at least as long as the test. | |
454 scoped_ptr<ChildProcessGlobals> CommonChildProcessSetUp() { | |
455 base::CommandLine cmd_line = *base::CommandLine::ForCurrentProcess(); | |
456 std::string service_name = | |
457 cmd_line.GetSwitchValueASCII(g_service_switch_name); | |
458 base::mac::ScopedMachSendRight server_port( | |
459 IPC::LookupServer(service_name.c_str())); | |
460 base::mac::ScopedMachReceiveRight client_port(IPC::MakeReceivingPort()); | |
461 | |
462 // Send the port that this process is listening on to the server. | |
463 IPC::SendMachPort(server_port, client_port, MACH_MSG_TYPE_MAKE_SEND); | |
464 | |
465 // Receive the task port of the server process. | |
466 base::mac::ScopedMachSendRight server_task_port( | |
467 IPC::ReceiveMachPort(client_port)); | |
468 | |
469 scoped_ptr<ChildProcessGlobals> globals(new ChildProcessGlobals); | |
470 globals->port_provider.InsertEntry(getppid(), server_task_port); | |
471 globals->broker.SetPortProvider(&globals->port_provider); | |
472 globals->server_task_port.reset(server_task_port.release()); | |
473 return globals; | |
474 } | |
475 | |
476 int CommonPrivilegedProcessMain(OnMessageReceivedCallback callback, | |
477 const char* channel_name) { | |
478 LOG(INFO) << "Privileged process start."; | |
479 scoped_ptr<ChildProcessGlobals> globals(CommonChildProcessSetUp()); | |
480 | |
481 mach_msg_type_number_t active_names_at_start = IPC::GetActiveNameCount(); | |
482 | |
483 base::MessageLoopForIO main_message_loop; | |
484 ProxyListener listener; | |
485 | |
486 scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient( | |
487 IPCTestBase::GetChannelName(channel_name), &listener)); | |
488 globals->broker.RegisterCommunicationChannel(channel.get()); | |
489 CHECK(channel->Connect()); | |
490 | |
491 while (true) { | |
492 LOG(INFO) << "Privileged process spinning run loop."; | |
493 base::MessageLoop::current()->Run(); | |
494 ProxyListener::Reason reason = listener.get_reason(); | |
495 if (reason == ProxyListener::CHANNEL_ERROR) | |
496 break; | |
497 | |
498 while (listener.has_message()) { | |
499 LOG(INFO) << "Privileged process running callback."; | |
500 callback(channel.get(), listener.get_first_message()); | |
501 LOG(INFO) << "Privileged process finishing callback."; | |
502 listener.pop_first_message(); | |
503 } | |
504 } | |
505 | |
506 if (active_names_at_start != IPC::GetActiveNameCount()) { | |
507 LOG(INFO) << "Memory leak!."; | |
508 } | |
509 LOG(INFO) << "Privileged process end."; | |
510 return 0; | |
511 } | |
512 | |
513 // An unprivileged process makes a shared memory region, and writes a string to | |
514 // it. The SharedMemoryHandle is sent to the privileged process using Chrome | |
515 // IPC. The privileged process checks that it received the same memory region. | |
516 TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandle) { | |
517 CommonSetUp("SendSharedMemoryHandle"); | |
518 | |
519 SendMessage1(kDataBuffer1); | |
520 base::MessageLoop::current()->Run(); | |
521 CommonTearDown(); | |
522 } | |
523 | |
524 void SendSharedMemoryHandleCallback(IPC::Sender* sender, | |
525 const IPC::Message& message) { | |
526 bool success = CheckContentsOfMessage1(message, kDataBuffer1); | |
527 SendControlMessage(sender, success); | |
528 } | |
529 | |
530 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandle) { | |
531 return CommonPrivilegedProcessMain(&SendSharedMemoryHandleCallback, | |
532 "SendSharedMemoryHandle"); | |
533 } | |
534 | |
535 // Similar to SendSharedMemoryHandle, but sends a very long shared memory | |
536 // region. | |
537 TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandleLong) { | |
538 CommonSetUp("SendSharedMemoryHandleLong"); | |
539 | |
540 std::string buffer(1 << 23, 'a'); | |
541 SendMessage1(buffer); | |
542 base::MessageLoop::current()->Run(); | |
543 CommonTearDown(); | |
544 } | |
545 | |
546 void SendSharedMemoryHandleLongCallback(IPC::Sender* sender, | |
547 const IPC::Message& message) { | |
548 std::string buffer(1 << 23, 'a'); | |
549 bool success = CheckContentsOfMessage1(message, buffer); | |
550 SendControlMessage(sender, success); | |
551 } | |
552 | |
553 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandleLong) { | |
554 return CommonPrivilegedProcessMain(&SendSharedMemoryHandleLongCallback, | |
555 "SendSharedMemoryHandleLong"); | |
556 } | |
557 | |
558 // Similar to SendSharedMemoryHandle, but sends two different shared memory | |
559 // regions in two messages. | |
560 TEST_F(IPCAttachmentBrokerMacTest, SendTwoMessagesDifferentSharedMemoryHandle) { | |
561 CommonSetUp("SendTwoMessagesDifferentSharedMemoryHandle"); | |
562 | |
563 SendMessage1(kDataBuffer1); | |
564 SendMessage1(kDataBuffer2); | |
565 base::MessageLoop::current()->Run(); | |
566 CommonTearDown(); | |
567 } | |
568 | |
569 void SendTwoMessagesDifferentSharedMemoryHandleCallback( | |
570 IPC::Sender* sender, | |
571 const IPC::Message& message) { | |
572 static int count = 0; | |
573 static bool success = true; | |
574 ++count; | |
575 if (count == 1) { | |
576 success &= CheckContentsOfMessage1(message, kDataBuffer1); | |
577 } else if (count == 2) { | |
578 success &= CheckContentsOfMessage1(message, kDataBuffer2); | |
579 SendControlMessage(sender, success); | |
580 } | |
581 } | |
582 | |
583 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendTwoMessagesDifferentSharedMemoryHandle) { | |
584 return CommonPrivilegedProcessMain( | |
585 &SendTwoMessagesDifferentSharedMemoryHandleCallback, | |
586 "SendTwoMessagesDifferentSharedMemoryHandle"); | |
587 } | |
588 | |
589 // Similar to SendSharedMemoryHandle, but sends the same shared memory region in | |
590 // two messages. | |
591 TEST_F(IPCAttachmentBrokerMacTest, SendTwoMessagesSameSharedMemoryHandle) { | |
592 CommonSetUp("SendTwoMessagesSameSharedMemoryHandle"); | |
593 | |
594 { | |
595 scoped_ptr<base::SharedMemory> shared_memory( | |
596 MakeSharedMemory(kDataBuffer1)); | |
597 | |
598 for (int i = 0; i < 2; ++i) { | |
599 IPC::Message* message = | |
600 new TestSharedMemoryHandleMsg1(100, shared_memory->handle(), 200); | |
601 sender()->Send(message); | |
602 } | |
603 } | |
604 | |
605 base::MessageLoop::current()->Run(); | |
606 CommonTearDown(); | |
607 } | |
608 | |
609 void SendTwoMessagesSameSharedMemoryHandleCallback( | |
610 IPC::Sender* sender, | |
611 const IPC::Message& message) { | |
612 static int count = 0; | |
613 static base::SharedMemoryHandle handle1; | |
614 ++count; | |
615 | |
616 if (count == 1) { | |
617 handle1 = GetSharedMemoryHandleFromMsg1(message); | |
618 } else if (count == 2) { | |
619 base::SharedMemoryHandle handle2(GetSharedMemoryHandleFromMsg1(message)); | |
620 | |
621 bool success = CheckContentsOfTwoEquivalentSharedMemoryHandles( | |
622 handle1, handle2, kDataBuffer1); | |
623 SendControlMessage(sender, success); | |
624 } | |
625 } | |
626 | |
627 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendTwoMessagesSameSharedMemoryHandle) { | |
628 return CommonPrivilegedProcessMain( | |
629 &SendTwoMessagesSameSharedMemoryHandleCallback, | |
630 "SendTwoMessagesSameSharedMemoryHandle"); | |
631 } | |
632 | |
633 // Similar to SendSharedMemoryHandle, but sends one message with two different | |
634 // memory regions. | |
635 TEST_F(IPCAttachmentBrokerMacTest, | |
636 SendOneMessageWithTwoDifferentSharedMemoryHandles) { | |
637 CommonSetUp("SendOneMessageWithTwoDifferentSharedMemoryHandles"); | |
638 | |
639 { | |
640 scoped_ptr<base::SharedMemory> shared_memory1( | |
641 MakeSharedMemory(kDataBuffer1)); | |
642 scoped_ptr<base::SharedMemory> shared_memory2( | |
643 MakeSharedMemory(kDataBuffer2)); | |
644 IPC::Message* message = new TestSharedMemoryHandleMsg2( | |
645 shared_memory1->handle(), shared_memory2->handle()); | |
646 sender()->Send(message); | |
647 } | |
648 base::MessageLoop::current()->Run(); | |
649 CommonTearDown(); | |
650 } | |
651 | |
652 void SendOneMessageWithTwoDifferentSharedMemoryHandlesCallback( | |
653 IPC::Sender* sender, | |
654 const IPC::Message& message) { | |
655 base::SharedMemoryHandle handle1; | |
656 base::SharedMemoryHandle handle2; | |
657 if (!GetSharedMemoryHandlesFromMsg2(message, &handle1, &handle2)) { | |
658 LOG(ERROR) << "Failed to deserialize message."; | |
659 SendControlMessage(sender, false); | |
660 return; | |
661 } | |
662 | |
663 bool success = CheckContentsOfSharedMemoryHandle(handle1, kDataBuffer1) && | |
664 CheckContentsOfSharedMemoryHandle(handle2, kDataBuffer2); | |
665 SendControlMessage(sender, success); | |
666 } | |
667 | |
668 MULTIPROCESS_IPC_TEST_CLIENT_MAIN( | |
669 SendOneMessageWithTwoDifferentSharedMemoryHandles) { | |
670 return CommonPrivilegedProcessMain( | |
671 &SendOneMessageWithTwoDifferentSharedMemoryHandlesCallback, | |
672 "SendOneMessageWithTwoDifferentSharedMemoryHandles"); | |
673 } | |
674 | |
675 // Similar to SendSharedMemoryHandle, but sends one message that contains the | |
676 // same memory region twice. | |
677 TEST_F(IPCAttachmentBrokerMacTest, | |
678 SendOneMessageWithTwoSameSharedMemoryHandles) { | |
679 CommonSetUp("SendOneMessageWithTwoSameSharedMemoryHandles"); | |
680 | |
681 { | |
682 scoped_ptr<base::SharedMemory> shared_memory( | |
683 MakeSharedMemory(kDataBuffer1)); | |
684 IPC::Message* message = new TestSharedMemoryHandleMsg2( | |
685 shared_memory->handle(), shared_memory->handle()); | |
686 sender()->Send(message); | |
687 } | |
688 base::MessageLoop::current()->Run(); | |
689 CommonTearDown(); | |
690 } | |
691 | |
692 void SendOneMessageWithTwoSameSharedMemoryHandlesCallback( | |
693 IPC::Sender* sender, | |
694 const IPC::Message& message) { | |
695 base::SharedMemoryHandle handle1; | |
696 base::SharedMemoryHandle handle2; | |
697 if (!GetSharedMemoryHandlesFromMsg2(message, &handle1, &handle2)) { | |
698 LOG(ERROR) << "Failed to deserialize message."; | |
699 SendControlMessage(sender, false); | |
700 return; | |
701 } | |
702 | |
703 bool success = CheckContentsOfTwoEquivalentSharedMemoryHandles( | |
704 handle1, handle2, kDataBuffer1); | |
705 SendControlMessage(sender, success); | |
706 } | |
707 | |
708 MULTIPROCESS_IPC_TEST_CLIENT_MAIN( | |
709 SendOneMessageWithTwoSameSharedMemoryHandles) { | |
710 return CommonPrivilegedProcessMain( | |
711 &SendOneMessageWithTwoSameSharedMemoryHandlesCallback, | |
712 "SendOneMessageWithTwoSameSharedMemoryHandles"); | |
713 } | |
714 | |
715 // Sends one message with two Posix FDs and two Mach ports. | |
716 TEST_F(IPCAttachmentBrokerMacTest, SendPosixFDAndMachPort) { | |
717 base::ScopedTempDir temp_dir; | |
718 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
719 base::FilePath fp1, fp2; | |
720 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &fp1)); | |
721 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &fp2)); | |
722 | |
723 CommonSetUp("SendPosixFDAndMachPort"); | |
724 | |
725 { | |
726 scoped_ptr<base::SharedMemory> shared_memory1( | |
727 MakeSharedMemory(kDataBuffer1)); | |
728 scoped_ptr<base::SharedMemory> shared_memory2( | |
729 MakeSharedMemory(kDataBuffer2)); | |
730 | |
731 base::FileDescriptor file_descriptor1( | |
732 MakeFileDescriptor(fp1, kDataBuffer3)); | |
733 base::FileDescriptor file_descriptor2( | |
734 MakeFileDescriptor(fp2, kDataBuffer4)); | |
735 | |
736 IPC::Message* message = new TestSharedMemoryHandleMsg3( | |
737 file_descriptor1, shared_memory1->handle(), file_descriptor2, | |
738 shared_memory2->handle()); | |
739 sender()->Send(message); | |
740 } | |
741 | |
742 base::MessageLoop::current()->Run(); | |
743 CommonTearDown(); | |
744 } | |
745 | |
746 void SendPosixFDAndMachPortCallback(IPC::Sender* sender, | |
747 const IPC::Message& message) { | |
748 TestSharedMemoryHandleMsg3::Schema::Param p; | |
749 if (!TestSharedMemoryHandleMsg3::Read(&message, &p)) { | |
750 LOG(ERROR) << "Failed to deserialize message."; | |
751 SendControlMessage(sender, false); | |
752 return; | |
753 } | |
754 | |
755 base::SharedMemoryHandle handle1 = base::get<1>(p); | |
756 base::SharedMemoryHandle handle2 = base::get<3>(p); | |
757 bool success1 = CheckContentsOfSharedMemoryHandle(handle1, kDataBuffer1) && | |
758 CheckContentsOfSharedMemoryHandle(handle2, kDataBuffer2); | |
759 if (!success1) | |
760 LOG(ERROR) << "SharedMemoryHandles have wrong contents."; | |
761 | |
762 bool success2 = | |
763 CheckContentsOfFileDescriptor(base::get<0>(p), kDataBuffer3) && | |
764 CheckContentsOfFileDescriptor(base::get<2>(p), kDataBuffer4); | |
765 if (!success2) | |
766 LOG(ERROR) << "FileDescriptors have wrong contents."; | |
767 | |
768 SendControlMessage(sender, success1 && success2); | |
769 } | |
770 | |
771 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendPosixFDAndMachPort) { | |
772 return CommonPrivilegedProcessMain(&SendPosixFDAndMachPortCallback, | |
773 "SendPosixFDAndMachPort"); | |
774 } | |
775 | |
776 // Similar to SendHandle, except the attachment's destination process is this | |
777 // process. This is an unrealistic scenario, but simulates an unprivileged | |
778 // process sending an attachment to another unprivileged process. | |
779 TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandleToSelf) { | |
780 SetBroker(new MockBroker); | |
781 CommonSetUp("SendSharedMemoryHandleToSelf"); | |
782 | |
783 // Technically, the channel is an endpoint, but we need the proxy listener to | |
784 // receive the messages so that it can quit the message loop. | |
785 channel()->SetAttachmentBrokerEndpoint(false); | |
786 get_proxy_listener()->set_listener(get_broker()); | |
787 | |
788 { | |
789 scoped_ptr<base::SharedMemory> shared_memory( | |
790 MakeSharedMemory(kDataBuffer1)); | |
791 mach_port_urefs_t ref_count = IPC::GetMachRefCount( | |
792 shared_memory->handle().GetMemoryObject(), MACH_PORT_RIGHT_SEND); | |
793 | |
794 IPC::Message* message = | |
795 new TestSharedMemoryHandleMsg1(100, shared_memory->handle(), 200); | |
796 sender()->Send(message); | |
797 base::MessageLoop::current()->Run(); | |
798 | |
799 // Get the received attachment. | |
800 IPC::BrokerableAttachment::AttachmentId* id = get_observer()->get_id(); | |
801 scoped_refptr<IPC::BrokerableAttachment> received_attachment; | |
802 get_broker()->GetAttachmentWithId(*id, &received_attachment); | |
803 ASSERT_NE(received_attachment.get(), nullptr); | |
804 | |
805 // Check that it's has the same name, but that the ref count has increased. | |
806 base::mac::ScopedMachSendRight memory_object( | |
807 GetMachPortFromBrokeredAttachment(received_attachment)); | |
808 ASSERT_EQ(memory_object, shared_memory->handle().GetMemoryObject()); | |
809 EXPECT_EQ(ref_count + 1, | |
810 IPC::GetMachRefCount(shared_memory->handle().GetMemoryObject(), | |
811 MACH_PORT_RIGHT_SEND)); | |
812 } | |
813 | |
814 FinalCleanUp(); | |
815 } | |
816 | |
817 void SendSharedMemoryHandleToSelfCallback(IPC::Sender* sender, | |
818 const IPC::Message&) { | |
819 // Do nothing special. The default behavior already runs the | |
820 // AttachmentBrokerPrivilegedMac. | |
821 } | |
822 | |
823 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandleToSelf) { | |
824 return CommonPrivilegedProcessMain(&SendSharedMemoryHandleToSelfCallback, | |
825 "SendSharedMemoryHandleToSelf"); | |
826 } | |
827 | |
828 } // namespace | |
OLD | NEW |