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