blob: ec18ea90410259abacb369ac9e7c5f92346f566e [file] [log] [blame]
Primiano Tuccie73ac932017-11-08 18:11:17 +00001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "ipc/src/unix_socket.h"
18
19#include <sys/mman.h>
20
21#include <list>
22
Primiano Tuccie73ac932017-11-08 18:11:17 +000023#include "gmock/gmock.h"
24#include "gtest/gtest.h"
Oystein Eftevaagdd727e42017-12-05 08:49:55 -080025#include "perfetto_base/build_config.h"
26#include "perfetto_base/logging.h"
27#include "perfetto_base/test/test_task_runner.h"
28#include "perfetto_base/utils.h"
Primiano Tuccie73ac932017-11-08 18:11:17 +000029
30namespace perfetto {
31namespace ipc {
32namespace {
33
34using ::testing::_;
35using ::testing::Invoke;
36using ::testing::Mock;
37
38// Mac OS X doesn't support abstract (i.e. unnamed) sockets.
39#if BUILDFLAG(OS_MACOSX)
40static const char kSocketName[] = "/tmp/test_socket";
41void UnlinkSocket() {
42 unlink(kSocketName);
43}
44#else
45static const char kSocketName[] = "@test_socket";
46void UnlinkSocket() {}
47#endif
48
49class MockEventListener : public UnixSocket::EventListener {
50 public:
51 MOCK_METHOD2(OnNewIncomingConnection, void(UnixSocket*, UnixSocket*));
52 MOCK_METHOD2(OnConnect, void(UnixSocket*, bool));
53 MOCK_METHOD1(OnDisconnect, void(UnixSocket*));
54 MOCK_METHOD1(OnDataAvailable, void(UnixSocket*));
55
56 // GMock doesn't support mocking methods with non-copiable args.
57 void OnNewIncomingConnection(
58 UnixSocket* self,
59 std::unique_ptr<UnixSocket> new_connection) override {
60 incoming_connections_.emplace_back(std::move(new_connection));
61 OnNewIncomingConnection(self, incoming_connections_.back().get());
62 }
63
64 std::unique_ptr<UnixSocket> GetIncomingConnection() {
65 if (incoming_connections_.empty())
66 return nullptr;
67 std::unique_ptr<UnixSocket> sock = std::move(incoming_connections_.front());
68 incoming_connections_.pop_front();
69 return sock;
70 }
71
72 private:
73 std::list<std::unique_ptr<UnixSocket>> incoming_connections_;
74};
75
76class UnixSocketTest : public ::testing::Test {
77 protected:
78 void SetUp() override { UnlinkSocket(); }
79 void TearDown() override { UnlinkSocket(); }
80
81 base::TestTaskRunner task_runner_;
82 MockEventListener event_listener_;
83};
84
85TEST_F(UnixSocketTest, ConnectionFailureIfUnreachable) {
86 auto cli = UnixSocket::Connect(kSocketName, &event_listener_, &task_runner_);
87 ASSERT_FALSE(cli->is_connected());
88 auto checkpoint = task_runner_.CreateCheckpoint("failure");
89 EXPECT_CALL(event_listener_, OnConnect(cli.get(), false))
90 .WillOnce(Invoke([checkpoint](UnixSocket*, bool) { checkpoint(); }));
91 task_runner_.RunUntilCheckpoint("failure");
92}
93
94// Both server and client should see an OnDisconnect() if the server drops
95// incoming connections immediately as they are created.
96TEST_F(UnixSocketTest, ConnectionImmediatelyDroppedByServer) {
97 auto srv = UnixSocket::Listen(kSocketName, &event_listener_, &task_runner_);
98 ASSERT_TRUE(srv->is_listening());
99
100 // The server will immediately shutdown the connection upon
101 // OnNewIncomingConnection().
102 auto srv_did_shutdown = task_runner_.CreateCheckpoint("srv_did_shutdown");
103 EXPECT_CALL(event_listener_, OnNewIncomingConnection(srv.get(), _))
104 .WillOnce(
105 Invoke([this, srv_did_shutdown](UnixSocket*, UnixSocket* new_conn) {
106 EXPECT_CALL(event_listener_, OnDisconnect(new_conn));
107 new_conn->Shutdown();
108 srv_did_shutdown();
109 }));
110
111 auto checkpoint = task_runner_.CreateCheckpoint("cli_connected");
112 auto cli = UnixSocket::Connect(kSocketName, &event_listener_, &task_runner_);
113 EXPECT_CALL(event_listener_, OnConnect(cli.get(), true))
114 .WillOnce(Invoke([checkpoint](UnixSocket*, bool) { checkpoint(); }));
115 task_runner_.RunUntilCheckpoint("cli_connected");
116 task_runner_.RunUntilCheckpoint("srv_did_shutdown");
117
118 // Trying to send something will trigger the disconnection notification.
119 auto cli_disconnected = task_runner_.CreateCheckpoint("cli_disconnected");
120 EXPECT_CALL(event_listener_, OnDisconnect(cli.get()))
121 .WillOnce(
122 Invoke([cli_disconnected](UnixSocket*) { cli_disconnected(); }));
123 EXPECT_FALSE(cli->Send("whatever"));
124 task_runner_.RunUntilCheckpoint("cli_disconnected");
125}
126
127TEST_F(UnixSocketTest, ClientAndServerExchangeData) {
128 auto srv = UnixSocket::Listen(kSocketName, &event_listener_, &task_runner_);
129 ASSERT_TRUE(srv->is_listening());
130
131 auto cli = UnixSocket::Connect(kSocketName, &event_listener_, &task_runner_);
132 EXPECT_CALL(event_listener_, OnConnect(cli.get(), true));
133 auto cli_connected = task_runner_.CreateCheckpoint("cli_connected");
134 auto srv_disconnected = task_runner_.CreateCheckpoint("srv_disconnected");
135 EXPECT_CALL(event_listener_, OnNewIncomingConnection(srv.get(), _))
136 .WillOnce(Invoke([this, cli_connected, srv_disconnected](
137 UnixSocket*, UnixSocket* srv_conn) {
138 EXPECT_CALL(event_listener_, OnDisconnect(srv_conn))
139 .WillOnce(Invoke(
140 [srv_disconnected](UnixSocket*) { srv_disconnected(); }));
141 cli_connected();
142 }));
143 task_runner_.RunUntilCheckpoint("cli_connected");
144
145 auto srv_conn = event_listener_.GetIncomingConnection();
146 ASSERT_TRUE(srv_conn);
147 ASSERT_TRUE(cli->is_connected());
148
149 auto cli_did_recv = task_runner_.CreateCheckpoint("cli_did_recv");
150 EXPECT_CALL(event_listener_, OnDataAvailable(cli.get()))
151 .WillOnce(Invoke([cli_did_recv](UnixSocket* s) {
152 ASSERT_EQ("srv>cli", s->ReceiveString());
153 cli_did_recv();
154 }));
155
156 auto srv_did_recv = task_runner_.CreateCheckpoint("srv_did_recv");
157 EXPECT_CALL(event_listener_, OnDataAvailable(srv_conn.get()))
158 .WillOnce(Invoke([srv_did_recv](UnixSocket* s) {
159 ASSERT_EQ("cli>srv", s->ReceiveString());
160 srv_did_recv();
161 }));
162 ASSERT_TRUE(cli->Send("cli>srv"));
163 ASSERT_TRUE(srv_conn->Send("srv>cli"));
164 task_runner_.RunUntilCheckpoint("cli_did_recv");
165 task_runner_.RunUntilCheckpoint("srv_did_recv");
166
167 // Check that Send/Receive() fails gracefully once the socket is closed.
168 auto cli_disconnected = task_runner_.CreateCheckpoint("cli_disconnected");
169 EXPECT_CALL(event_listener_, OnDisconnect(cli.get()))
170 .WillOnce(
171 Invoke([cli_disconnected](UnixSocket*) { cli_disconnected(); }));
172 cli->Shutdown();
173 char msg[4];
174 ASSERT_EQ(0u, cli->Receive(&msg, sizeof(msg)));
175 ASSERT_EQ("", cli->ReceiveString());
176 ASSERT_EQ(0u, srv_conn->Receive(&msg, sizeof(msg)));
177 ASSERT_EQ("", srv_conn->ReceiveString());
178 ASSERT_FALSE(cli->Send("foo"));
179 ASSERT_FALSE(srv_conn->Send("bar"));
180 srv->Shutdown();
181 task_runner_.RunUntilCheckpoint("cli_disconnected");
182 task_runner_.RunUntilCheckpoint("srv_disconnected");
183}
184
185// Mostly a stress tests. Connects kNumClients clients to the same server and
186// tests that all can exchange data and can see the expected sequence of events.
187TEST_F(UnixSocketTest, SeveralClients) {
188 auto srv = UnixSocket::Listen(kSocketName, &event_listener_, &task_runner_);
189 ASSERT_TRUE(srv->is_listening());
190 constexpr size_t kNumClients = 32;
191 std::unique_ptr<UnixSocket> cli[kNumClients];
192
193 EXPECT_CALL(event_listener_, OnNewIncomingConnection(srv.get(), _))
194 .Times(kNumClients)
195 .WillRepeatedly(Invoke([this](UnixSocket*, UnixSocket* s) {
196 EXPECT_CALL(event_listener_, OnDataAvailable(s))
197 .WillOnce(Invoke([](UnixSocket* t) {
198 ASSERT_EQ("PING", t->ReceiveString());
199 ASSERT_TRUE(t->Send("PONG"));
200 }));
201 }));
202
203 for (size_t i = 0; i < kNumClients; i++) {
204 cli[i] = UnixSocket::Connect(kSocketName, &event_listener_, &task_runner_);
205 EXPECT_CALL(event_listener_, OnConnect(cli[i].get(), true))
206 .WillOnce(Invoke([](UnixSocket* s, bool success) {
207 ASSERT_TRUE(success);
208 ASSERT_TRUE(s->Send("PING"));
209 }));
210
211 auto checkpoint = task_runner_.CreateCheckpoint(std::to_string(i));
212 EXPECT_CALL(event_listener_, OnDataAvailable(cli[i].get()))
213 .WillOnce(Invoke([checkpoint](UnixSocket* s) {
214 ASSERT_EQ("PONG", s->ReceiveString());
215 checkpoint();
216 }));
217 }
218
219 for (size_t i = 0; i < kNumClients; i++) {
220 task_runner_.RunUntilCheckpoint(std::to_string(i));
221 ASSERT_TRUE(Mock::VerifyAndClearExpectations(cli[i].get()));
222 }
223}
224
225// Creates two processes. The server process creates a file and passes it over
226// the socket to the client. Both processes mmap the file in shared mode and
227// check that they see the same contents.
228TEST_F(UnixSocketTest, SharedMemory) {
229 int pipes[2];
230 ASSERT_EQ(0, pipe(pipes));
231
232 pid_t pid = fork();
233 ASSERT_GE(pid, 0);
234 constexpr size_t kTmpSize = 4096;
235
236 if (pid == 0) {
237 // Child process.
238 FILE* tmp = tmpfile();
239 ASSERT_NE(nullptr, tmp);
240 int tmp_fd = fileno(tmp);
241 ASSERT_FALSE(ftruncate(tmp_fd, kTmpSize));
242 char* mem = reinterpret_cast<char*>(
243 mmap(nullptr, kTmpSize, PROT_READ | PROT_WRITE, MAP_SHARED, tmp_fd, 0));
244 ASSERT_NE(nullptr, mem);
245 memcpy(mem, "shm rocks", 10);
246
247 auto srv = UnixSocket::Listen(kSocketName, &event_listener_, &task_runner_);
248 ASSERT_TRUE(srv->is_listening());
249 // Signal the other process that it can connect.
250 ASSERT_EQ(1, PERFETTO_EINTR(write(pipes[1], ".", 1)));
251 auto checkpoint = task_runner_.CreateCheckpoint("change_seen_by_server");
252 EXPECT_CALL(event_listener_, OnNewIncomingConnection(srv.get(), _))
253 .WillOnce(Invoke(
254 [this, tmp_fd, checkpoint, mem](UnixSocket*, UnixSocket* new_conn) {
Oystein Eftevaagdd727e42017-12-05 08:49:55 -0800255 ASSERT_EQ(geteuid(), static_cast<uint32_t>(new_conn->peer_uid()));
Primiano Tuccie73ac932017-11-08 18:11:17 +0000256 ASSERT_TRUE(new_conn->Send("txfd", 5, tmp_fd));
257 // Wait for the client to change this again.
258 EXPECT_CALL(event_listener_, OnDataAvailable(new_conn))
259 .WillOnce(Invoke([checkpoint, mem](UnixSocket* s) {
260 ASSERT_EQ("change notify", s->ReceiveString());
261 ASSERT_STREQ("rock more", mem);
262 checkpoint();
263 }));
264 }));
265 task_runner_.RunUntilCheckpoint("change_seen_by_server");
266 ASSERT_TRUE(Mock::VerifyAndClearExpectations(&event_listener_));
267 _exit(0);
268 } else {
269 char sync_cmd = '\0';
270 ASSERT_EQ(1, PERFETTO_EINTR(read(pipes[0], &sync_cmd, 1)));
271 ASSERT_EQ('.', sync_cmd);
272 auto cli =
273 UnixSocket::Connect(kSocketName, &event_listener_, &task_runner_);
274 EXPECT_CALL(event_listener_, OnConnect(cli.get(), true));
275 auto checkpoint = task_runner_.CreateCheckpoint("change_seen_by_client");
276 EXPECT_CALL(event_listener_, OnDataAvailable(cli.get()))
277 .WillOnce(Invoke([checkpoint](UnixSocket* s) {
278 char msg[32];
279 base::ScopedFile fd;
280 ASSERT_EQ(5u, s->Receive(msg, sizeof(msg), &fd));
281 ASSERT_STREQ("txfd", msg);
282 ASSERT_TRUE(fd);
283 char* mem = reinterpret_cast<char*>(mmap(
284 nullptr, kTmpSize, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0));
285 ASSERT_NE(nullptr, mem);
286 mem[9] = '\0'; // Just to get a clean error in case of test failure.
287 ASSERT_STREQ("shm rocks", mem);
288
289 // Now change the shared memory and ping the other process.
290 memcpy(mem, "rock more", 10);
291 ASSERT_TRUE(s->Send("change notify"));
292 checkpoint();
293 }));
294 task_runner_.RunUntilCheckpoint("change_seen_by_client");
295 int st = 0;
296 PERFETTO_EINTR(waitpid(pid, &st, 0));
297 ASSERT_FALSE(WIFSIGNALED(st)) << "Server died with signal " << WTERMSIG(st);
298 EXPECT_TRUE(WIFEXITED(st));
299 ASSERT_EQ(0, WEXITSTATUS(st));
300 }
301}
302
303constexpr size_t kAtomicWrites_FrameSize = 1123;
304bool AtomicWrites_SendAttempt(UnixSocket* s,
305 base::TaskRunner* task_runner,
306 int num_frame) {
307 char buf[kAtomicWrites_FrameSize];
308 memset(buf, static_cast<char>(num_frame), sizeof(buf));
309 if (s->Send(buf, sizeof(buf)))
310 return true;
311 task_runner->PostTask(
312 std::bind(&AtomicWrites_SendAttempt, s, task_runner, num_frame));
313 return false;
314}
315
316// Creates a client-server pair. The client sends continuously data to the
317// server. Upon each Send() attempt, the client sends a buffer which is memset()
318// with a unique number (0 to kNumFrames). We are deliberately trying to fill
319// the socket output buffer, so we expect some of these Send()s to fail.
320// The client is extremely aggressive and, when a Send() fails, just keeps
321// re-posting it with the same unique number. The server verifies that we
322// receive one and exactly one of each buffers, without any gaps or truncation.
323TEST_F(UnixSocketTest, SendIsAtomic) {
324 static constexpr int kNumFrames = 127;
325
326 auto srv = UnixSocket::Listen(kSocketName, &event_listener_, &task_runner_);
327 ASSERT_TRUE(srv->is_listening());
328
329 auto cli = UnixSocket::Connect(kSocketName, &event_listener_, &task_runner_);
330
331 auto all_frames_done = task_runner_.CreateCheckpoint("all_frames_done");
332 std::set<int> received_iterations;
333 EXPECT_CALL(event_listener_, OnNewIncomingConnection(srv.get(), _))
334 .WillOnce(Invoke([this, &received_iterations, all_frames_done](
335 UnixSocket*, UnixSocket* srv_conn) {
336 EXPECT_CALL(event_listener_, OnDataAvailable(srv_conn))
337 .WillRepeatedly(
338 Invoke([&received_iterations, all_frames_done](UnixSocket* s) {
339 char buf[kAtomicWrites_FrameSize];
340 size_t res = s->Receive(buf, sizeof(buf));
341 if (res == 0)
342 return; // Spurious select(), could happen.
343 ASSERT_EQ(kAtomicWrites_FrameSize, res);
344 // Check that we didn't get two truncated frames.
345 for (size_t i = 0; i < sizeof(buf); i++)
346 ASSERT_EQ(buf[0], buf[i]);
347 ASSERT_EQ(0u, received_iterations.count(buf[0]));
348 received_iterations.insert(buf[0]);
349 if (received_iterations.size() == kNumFrames)
350 all_frames_done();
351 }));
352 }));
353
354 auto cli_connected = task_runner_.CreateCheckpoint("cli_connected");
355 EXPECT_CALL(event_listener_, OnConnect(cli.get(), true))
356 .WillOnce(
357 Invoke([cli_connected](UnixSocket*, bool) { cli_connected(); }));
358 task_runner_.RunUntilCheckpoint("cli_connected");
359 ASSERT_TRUE(cli->is_connected());
Oystein Eftevaagdd727e42017-12-05 08:49:55 -0800360 ASSERT_EQ(geteuid(), static_cast<uint32_t>(cli->peer_uid()));
Primiano Tuccie73ac932017-11-08 18:11:17 +0000361
362 bool did_requeue = false;
363 for (int i = 0; i < kNumFrames; i++)
364 did_requeue |= !AtomicWrites_SendAttempt(cli.get(), &task_runner_, i);
365
366 // We expect that at least one of the kNumFrames didn't fit in the socket
367 // buffer and was re-posted, otherwise this entire test would be pointless.
368 ASSERT_TRUE(did_requeue);
369
370 task_runner_.RunUntilCheckpoint("all_frames_done");
371}
372
Primiano Tuccic2025af2017-11-20 12:47:49 +0000373// Checks that the peer_uid() is retained after the client disconnects. The IPC
374// layer needs to rely on this to validate messages received immediately before
375// a client disconnects.
376TEST_F(UnixSocketTest, PeerUidRetainedAfterDisconnect) {
377 auto srv = UnixSocket::Listen(kSocketName, &event_listener_, &task_runner_);
378 ASSERT_TRUE(srv->is_listening());
379 UnixSocket* srv_client_conn = nullptr;
380 auto srv_connected = task_runner_.CreateCheckpoint("srv_connected");
381 EXPECT_CALL(event_listener_, OnNewIncomingConnection(srv.get(), _))
382 .WillOnce(Invoke(
383 [&srv_client_conn, srv_connected](UnixSocket*, UnixSocket* srv_conn) {
384 srv_client_conn = srv_conn;
Oystein Eftevaagdd727e42017-12-05 08:49:55 -0800385 EXPECT_EQ(geteuid(), static_cast<uint32_t>(srv_conn->peer_uid()));
Primiano Tuccic2025af2017-11-20 12:47:49 +0000386 srv_connected();
387 }));
388 auto cli_connected = task_runner_.CreateCheckpoint("cli_connected");
389 auto cli = UnixSocket::Connect(kSocketName, &event_listener_, &task_runner_);
390 EXPECT_CALL(event_listener_, OnConnect(cli.get(), true))
391 .WillOnce(
392 Invoke([cli_connected](UnixSocket*, bool) { cli_connected(); }));
393
394 task_runner_.RunUntilCheckpoint("cli_connected");
395 task_runner_.RunUntilCheckpoint("srv_connected");
396 ASSERT_NE(nullptr, srv_client_conn);
397 ASSERT_TRUE(srv_client_conn->is_connected());
398
399 auto cli_disconnected = task_runner_.CreateCheckpoint("cli_disconnected");
400 EXPECT_CALL(event_listener_, OnDisconnect(srv_client_conn))
401 .WillOnce(
402 Invoke([cli_disconnected](UnixSocket*) { cli_disconnected(); }));
403
404 // TODO(primiano): when the a peer disconnects, the other end receives a
405 // spurious OnDataAvailable() that needs to be acked with a Receive() to read
406 // the EOF. See b/69536434.
407 EXPECT_CALL(event_listener_, OnDataAvailable(srv_client_conn))
408 .WillOnce(Invoke([](UnixSocket* sock) { sock->ReceiveString(); }));
409
410 cli.reset();
411 task_runner_.RunUntilCheckpoint("cli_disconnected");
412 ASSERT_FALSE(srv_client_conn->is_connected());
Oystein Eftevaagdd727e42017-12-05 08:49:55 -0800413 EXPECT_EQ(geteuid(), static_cast<uint32_t>(srv_client_conn->peer_uid()));
Primiano Tuccic2025af2017-11-20 12:47:49 +0000414}
415
Primiano Tuccie73ac932017-11-08 18:11:17 +0000416// TODO(primiano): add a test to check that in the case of a peer sending a fd
417// and the other end just doing a recv (without taking it), the fd is closed and
418// not left around.
419
420// TODO(primiano); add a test to check that a socket can be reused after
421// Shutdown(),
422
423// TODO(primiano): add a test to check that OnDisconnect() is called in all
424// possible cases.
425
426// TODO(primiano): add tests that destroy the socket in all possible stages and
427// verify that no spurious EventListener callback is received.
428
429} // namespace
430} // namespace ipc
431} // namespace perfetto