1 // Copyright 2014 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/ipc_mojo_bootstrap.h"
6 
7 #include <cstdint>
8 #include <memory>
9 #include <utility>
10 
11 #include "base/message_loop/message_loop.h"
12 #include "base/run_loop.h"
13 #include "base/threading/thread_task_runner_handle.h"
14 #include "ipc/ipc.mojom.h"
15 #include "ipc/ipc_test_base.h"
16 #include "mojo/core/test/multiprocess_test_helper.h"
17 #include "mojo/public/cpp/bindings/associated_binding.h"
18 
19 namespace {
20 
21 constexpr int32_t kTestServerPid = 42;
22 constexpr int32_t kTestClientPid = 4242;
23 
24 class Connection {
25  public:
Connection(std::unique_ptr<IPC::MojoBootstrap> bootstrap,int32_t sender_id)26   explicit Connection(std::unique_ptr<IPC::MojoBootstrap> bootstrap,
27                       int32_t sender_id)
28       : bootstrap_(std::move(bootstrap)) {
29     bootstrap_->Connect(&sender_, &receiver_);
30     sender_->SetPeerPid(sender_id);
31   }
32 
TakeReceiver(IPC::mojom::ChannelAssociatedRequest * receiver)33   void TakeReceiver(IPC::mojom::ChannelAssociatedRequest* receiver) {
34     *receiver = std::move(receiver_);
35   }
36 
GetSender()37   IPC::mojom::ChannelAssociatedPtr& GetSender() { return sender_; }
38 
39  private:
40   IPC::mojom::ChannelAssociatedPtr sender_;
41   IPC::mojom::ChannelAssociatedRequest receiver_;
42   std::unique_ptr<IPC::MojoBootstrap> bootstrap_;
43 };
44 
45 class PeerPidReceiver : public IPC::mojom::Channel {
46  public:
47   enum class MessageExpectation {
48     kNotExpected,
49     kExpectedValid,
50     kExpectedInvalid
51   };
52 
PeerPidReceiver(IPC::mojom::ChannelAssociatedRequest request,const base::Closure & on_peer_pid_set,MessageExpectation message_expectation=MessageExpectation::kNotExpected)53   PeerPidReceiver(
54       IPC::mojom::ChannelAssociatedRequest request,
55       const base::Closure& on_peer_pid_set,
56       MessageExpectation message_expectation = MessageExpectation::kNotExpected)
57       : binding_(this, std::move(request)),
58         on_peer_pid_set_(on_peer_pid_set),
59         message_expectation_(message_expectation) {
60     binding_.set_connection_error_handler(disconnect_run_loop_.QuitClosure());
61   }
62 
~PeerPidReceiver()63   ~PeerPidReceiver() override {
64     bool expected_message =
65         message_expectation_ != MessageExpectation::kNotExpected;
66     EXPECT_EQ(expected_message, received_message_);
67   }
68 
69   // mojom::Channel:
SetPeerPid(int32_t pid)70   void SetPeerPid(int32_t pid) override {
71     peer_pid_ = pid;
72     on_peer_pid_set_.Run();
73   }
74 
Receive(IPC::MessageView message_view)75   void Receive(IPC::MessageView message_view) override {
76     ASSERT_NE(MessageExpectation::kNotExpected, message_expectation_);
77     received_message_ = true;
78 
79     IPC::Message message(message_view.data(), message_view.size());
80     bool expected_valid =
81         message_expectation_ == MessageExpectation::kExpectedValid;
82     EXPECT_EQ(expected_valid, message.IsValid());
83   }
84 
GetAssociatedInterface(const std::string & name,IPC::mojom::GenericInterfaceAssociatedRequest request)85   void GetAssociatedInterface(
86       const std::string& name,
87       IPC::mojom::GenericInterfaceAssociatedRequest request) override {}
88 
peer_pid() const89   int32_t peer_pid() const { return peer_pid_; }
90 
RunUntilDisconnect()91   void RunUntilDisconnect() { disconnect_run_loop_.Run(); }
92 
93  private:
94   mojo::AssociatedBinding<IPC::mojom::Channel> binding_;
95   const base::Closure on_peer_pid_set_;
96   MessageExpectation message_expectation_;
97   int32_t peer_pid_ = -1;
98   bool received_message_ = false;
99   base::RunLoop disconnect_run_loop_;
100 
101   DISALLOW_COPY_AND_ASSIGN(PeerPidReceiver);
102 };
103 
104 class IPCMojoBootstrapTest : public testing::Test {
105  protected:
106   mojo::core::test::MultiprocessTestHelper helper_;
107 };
108 
TEST_F(IPCMojoBootstrapTest,Connect)109 TEST_F(IPCMojoBootstrapTest, Connect) {
110   base::MessageLoop message_loop;
111   Connection connection(
112       IPC::MojoBootstrap::Create(
113           helper_.StartChild("IPCMojoBootstrapTestClient"),
114           IPC::Channel::MODE_SERVER, base::ThreadTaskRunnerHandle::Get(),
115           base::ThreadTaskRunnerHandle::Get()),
116       kTestServerPid);
117 
118   IPC::mojom::ChannelAssociatedRequest receiver;
119   connection.TakeReceiver(&receiver);
120 
121   base::RunLoop run_loop;
122   PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure());
123   run_loop.Run();
124 
125   EXPECT_EQ(kTestClientPid, impl.peer_pid());
126 
127   impl.RunUntilDisconnect();
128   EXPECT_TRUE(helper_.WaitForChildTestShutdown());
129 }
130 
131 // A long running process that connects to us.
MULTIPROCESS_TEST_MAIN_WITH_SETUP(IPCMojoBootstrapTestClientTestChildMain,::mojo::core::test::MultiprocessTestHelper::ChildSetup)132 MULTIPROCESS_TEST_MAIN_WITH_SETUP(
133     IPCMojoBootstrapTestClientTestChildMain,
134     ::mojo::core::test::MultiprocessTestHelper::ChildSetup) {
135   base::MessageLoop message_loop;
136   Connection connection(
137       IPC::MojoBootstrap::Create(
138           std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe),
139           IPC::Channel::MODE_CLIENT, base::ThreadTaskRunnerHandle::Get(),
140           base::ThreadTaskRunnerHandle::Get()),
141       kTestClientPid);
142 
143   IPC::mojom::ChannelAssociatedRequest receiver;
144   connection.TakeReceiver(&receiver);
145 
146   base::RunLoop run_loop;
147   PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure());
148   run_loop.Run();
149 
150   EXPECT_EQ(kTestServerPid, impl.peer_pid());
151 
152   return 0;
153 }
154 
TEST_F(IPCMojoBootstrapTest,ReceiveEmptyMessage)155 TEST_F(IPCMojoBootstrapTest, ReceiveEmptyMessage) {
156   base::MessageLoop message_loop;
157   Connection connection(
158       IPC::MojoBootstrap::Create(
159           helper_.StartChild("IPCMojoBootstrapTestEmptyMessage"),
160           IPC::Channel::MODE_SERVER, base::ThreadTaskRunnerHandle::Get(),
161           base::ThreadTaskRunnerHandle::Get()),
162       kTestServerPid);
163 
164   IPC::mojom::ChannelAssociatedRequest receiver;
165   connection.TakeReceiver(&receiver);
166 
167   base::RunLoop run_loop;
168   PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure(),
169                        PeerPidReceiver::MessageExpectation::kExpectedInvalid);
170   run_loop.Run();
171 
172   // Wait for the Channel to be disconnected so we can reasonably assert that
173   // the child's empty message must have been received before we pass the test.
174   impl.RunUntilDisconnect();
175 
176   EXPECT_TRUE(helper_.WaitForChildTestShutdown());
177 }
178 
179 // A long running process that connects to us.
MULTIPROCESS_TEST_MAIN_WITH_SETUP(IPCMojoBootstrapTestEmptyMessageTestChildMain,::mojo::core::test::MultiprocessTestHelper::ChildSetup)180 MULTIPROCESS_TEST_MAIN_WITH_SETUP(
181     IPCMojoBootstrapTestEmptyMessageTestChildMain,
182     ::mojo::core::test::MultiprocessTestHelper::ChildSetup) {
183   base::MessageLoop message_loop;
184   Connection connection(
185       IPC::MojoBootstrap::Create(
186           std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe),
187           IPC::Channel::MODE_CLIENT, base::ThreadTaskRunnerHandle::Get(),
188           base::ThreadTaskRunnerHandle::Get()),
189       kTestClientPid);
190 
191   IPC::mojom::ChannelAssociatedRequest receiver;
192   connection.TakeReceiver(&receiver);
193   auto& sender = connection.GetSender();
194 
195   uint8_t data = 0;
196   sender->Receive(
197       IPC::MessageView(mojo_base::BigBufferView(base::make_span(&data, 0)),
198                        base::nullopt /* handles */));
199 
200   base::RunLoop run_loop;
201   PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure());
202   run_loop.Run();
203 
204   return 0;
205 }
206 
207 }  // namespace
208