1 // Copyright 2016 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 "base/mac/mach_port_broker.h"
6 
7 #include "base/command_line.h"
8 #include "base/synchronization/lock.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "base/test/multiprocess_test.h"
11 #include "base/test/test_timeouts.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "testing/multiprocess_func_list.h"
14 
15 namespace base {
16 
17 namespace {
18 const char kBootstrapPortName[] = "thisisatest";
19 }
20 
21 class MachPortBrokerTest : public testing::Test,
22                            public base::PortProvider::Observer {
23  public:
MachPortBrokerTest()24   MachPortBrokerTest()
25       : broker_(kBootstrapPortName),
26         event_(base::WaitableEvent::ResetPolicy::MANUAL,
27                base::WaitableEvent::InitialState::NOT_SIGNALED),
28         received_process_(kNullProcessHandle) {
29     broker_.AddObserver(this);
30   }
~MachPortBrokerTest()31   ~MachPortBrokerTest() override {
32     broker_.RemoveObserver(this);
33   }
34 
35   // Helper function to acquire/release locks and call |PlaceholderForPid()|.
AddPlaceholderForPid(base::ProcessHandle pid)36   void AddPlaceholderForPid(base::ProcessHandle pid) {
37     base::AutoLock lock(broker_.GetLock());
38     broker_.AddPlaceholderForPid(pid);
39   }
40 
41   // Helper function to acquire/release locks and call |FinalizePid()|.
FinalizePid(base::ProcessHandle pid,mach_port_t task_port)42   void FinalizePid(base::ProcessHandle pid,
43                    mach_port_t task_port) {
44     base::AutoLock lock(broker_.GetLock());
45     broker_.FinalizePid(pid, task_port);
46   }
47 
WaitForTaskPort()48   void WaitForTaskPort() {
49     event_.Wait();
50   }
51 
52   // base::PortProvider::Observer:
OnReceivedTaskPort(ProcessHandle process)53   void OnReceivedTaskPort(ProcessHandle process) override {
54     received_process_ = process;
55     event_.Signal();
56   }
57 
58  protected:
59   MachPortBroker broker_;
60   WaitableEvent event_;
61   ProcessHandle received_process_;
62 };
63 
TEST_F(MachPortBrokerTest,Locks)64 TEST_F(MachPortBrokerTest, Locks) {
65   // Acquire and release the locks.  Nothing bad should happen.
66   base::AutoLock lock(broker_.GetLock());
67 }
68 
TEST_F(MachPortBrokerTest,AddPlaceholderAndFinalize)69 TEST_F(MachPortBrokerTest, AddPlaceholderAndFinalize) {
70   // Add a placeholder for PID 1.
71   AddPlaceholderForPid(1);
72   EXPECT_EQ(0u, broker_.TaskForPid(1));
73 
74   // Finalize PID 1.
75   FinalizePid(1, 100u);
76   EXPECT_EQ(100u, broker_.TaskForPid(1));
77 
78   // Should be no entry for PID 2.
79   EXPECT_EQ(0u, broker_.TaskForPid(2));
80 }
81 
TEST_F(MachPortBrokerTest,FinalizeUnknownPid)82 TEST_F(MachPortBrokerTest, FinalizeUnknownPid) {
83   // Finalizing an entry for an unknown pid should not add it to the map.
84   FinalizePid(1u, 100u);
85   EXPECT_EQ(0u, broker_.TaskForPid(1u));
86 }
87 
MULTIPROCESS_TEST_MAIN(MachPortBrokerTestChild)88 MULTIPROCESS_TEST_MAIN(MachPortBrokerTestChild) {
89   CHECK(base::MachPortBroker::ChildSendTaskPortToParent(kBootstrapPortName));
90   return 0;
91 }
92 
TEST_F(MachPortBrokerTest,ReceivePortFromChild)93 TEST_F(MachPortBrokerTest, ReceivePortFromChild) {
94   ASSERT_TRUE(broker_.Init());
95   CommandLine command_line(
96       base::GetMultiProcessTestChildBaseCommandLine());
97   broker_.GetLock().Acquire();
98   base::Process test_child_process = base::SpawnMultiProcessTestChild(
99       "MachPortBrokerTestChild", command_line, LaunchOptions());
100   broker_.AddPlaceholderForPid(test_child_process.Handle());
101   broker_.GetLock().Release();
102 
103   WaitForTaskPort();
104   EXPECT_EQ(test_child_process.Handle(), received_process_);
105 
106   int rv = -1;
107   ASSERT_TRUE(test_child_process.WaitForExitWithTimeout(
108       TestTimeouts::action_timeout(), &rv));
109   EXPECT_EQ(0, rv);
110 
111   EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL),
112             broker_.TaskForPid(test_child_process.Handle()));
113 }
114 
TEST_F(MachPortBrokerTest,ReceivePortFromChildWithoutAdding)115 TEST_F(MachPortBrokerTest, ReceivePortFromChildWithoutAdding) {
116   ASSERT_TRUE(broker_.Init());
117   CommandLine command_line(
118       base::GetMultiProcessTestChildBaseCommandLine());
119   broker_.GetLock().Acquire();
120   base::Process test_child_process = base::SpawnMultiProcessTestChild(
121       "MachPortBrokerTestChild", command_line, LaunchOptions());
122   broker_.GetLock().Release();
123 
124   int rv = -1;
125   ASSERT_TRUE(test_child_process.WaitForExitWithTimeout(
126       TestTimeouts::action_timeout(), &rv));
127   EXPECT_EQ(0, rv);
128 
129   EXPECT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL),
130             broker_.TaskForPid(test_child_process.Handle()));
131 }
132 
133 }  // namespace base
134