1// Copyright (c) 2013 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 <sys/socket.h>
6#include <sys/types.h>
7#include <unistd.h>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/files/file_util.h"
12#include "base/files/scoped_file.h"
13#include "base/memory/scoped_vector.h"
14#include "base/pickle.h"
15#include "base/posix/unix_domain_socket_linux.h"
16#include "base/synchronization/waitable_event.h"
17#include "base/threading/thread.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20namespace base {
21
22namespace {
23
24TEST(UnixDomainSocketTest, SendRecvMsgAbortOnReplyFDClose) {
25  Thread message_thread("UnixDomainSocketTest");
26  ASSERT_TRUE(message_thread.Start());
27
28  int fds[2];
29  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
30  ScopedFD scoped_fd0(fds[0]);
31  ScopedFD scoped_fd1(fds[1]);
32
33  // Have the thread send a synchronous message via the socket.
34  Pickle request;
35  message_thread.message_loop()->PostTask(
36      FROM_HERE,
37      Bind(IgnoreResult(&UnixDomainSocket::SendRecvMsg),
38           fds[1], static_cast<uint8_t*>(NULL), 0U, static_cast<int*>(NULL),
39           request));
40
41  // Receive the message.
42  ScopedVector<base::ScopedFD> message_fds;
43  uint8_t buffer[16];
44  ASSERT_EQ(static_cast<int>(request.size()),
45            UnixDomainSocket::RecvMsg(fds[0], buffer, sizeof(buffer),
46                                      &message_fds));
47  ASSERT_EQ(1U, message_fds.size());
48
49  // Close the reply FD.
50  message_fds.clear();
51
52  // Check that the thread didn't get blocked.
53  WaitableEvent event(false, false);
54  message_thread.message_loop()->PostTask(
55      FROM_HERE,
56      Bind(&WaitableEvent::Signal, Unretained(&event)));
57  ASSERT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(5000)));
58}
59
60TEST(UnixDomainSocketTest, SendRecvMsgAvoidsSIGPIPE) {
61  // Make sure SIGPIPE isn't being ignored.
62  struct sigaction act = {}, oldact;
63  act.sa_handler = SIG_DFL;
64  ASSERT_EQ(0, sigaction(SIGPIPE, &act, &oldact));
65  int fds[2];
66  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
67  ScopedFD scoped_fd1(fds[1]);
68  ASSERT_EQ(0, IGNORE_EINTR(close(fds[0])));
69
70  // Have the thread send a synchronous message via the socket. Unless the
71  // message is sent with MSG_NOSIGNAL, this shall result in SIGPIPE.
72  Pickle request;
73  ASSERT_EQ(-1,
74      UnixDomainSocket::SendRecvMsg(fds[1], static_cast<uint8_t*>(NULL),
75                                    0U, static_cast<int*>(NULL), request));
76  ASSERT_EQ(EPIPE, errno);
77  // Restore the SIGPIPE handler.
78  ASSERT_EQ(0, sigaction(SIGPIPE, &oldact, NULL));
79}
80
81// Simple sanity check within a single process that receiving PIDs works.
82TEST(UnixDomainSocketTest, RecvPid) {
83  int fds[2];
84  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
85  base::ScopedFD recv_sock(fds[0]);
86  base::ScopedFD send_sock(fds[1]);
87
88  ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
89
90  static const char kHello[] = "hello";
91  ASSERT_TRUE(UnixDomainSocket::SendMsg(
92      send_sock.get(), kHello, sizeof(kHello), std::vector<int>()));
93
94  // Extra receiving buffer space to make sure we really received only
95  // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer.
96  char buf[sizeof(kHello) + 1];
97  base::ProcessId sender_pid;
98  ScopedVector<base::ScopedFD> fd_vec;
99  const ssize_t nread = UnixDomainSocket::RecvMsgWithPid(
100      recv_sock.get(), buf, sizeof(buf), &fd_vec, &sender_pid);
101  ASSERT_EQ(sizeof(kHello), static_cast<size_t>(nread));
102  ASSERT_EQ(0, memcmp(buf, kHello, sizeof(kHello)));
103  ASSERT_EQ(0U, fd_vec.size());
104
105  ASSERT_EQ(getpid(), sender_pid);
106}
107
108// Same as above, but send the max number of file descriptors too.
109TEST(UnixDomainSocketTest, RecvPidWithMaxDescriptors) {
110  int fds[2];
111  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
112  base::ScopedFD recv_sock(fds[0]);
113  base::ScopedFD send_sock(fds[1]);
114
115  ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
116
117  static const char kHello[] = "hello";
118  std::vector<int> send_fds(UnixDomainSocket::kMaxFileDescriptors,
119                            send_sock.get());
120  ASSERT_TRUE(UnixDomainSocket::SendMsg(
121      send_sock.get(), kHello, sizeof(kHello), send_fds));
122
123  // Extra receiving buffer space to make sure we really received only
124  // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer.
125  char buf[sizeof(kHello) + 1];
126  base::ProcessId sender_pid;
127  ScopedVector<base::ScopedFD> recv_fds;
128  const ssize_t nread = UnixDomainSocket::RecvMsgWithPid(
129      recv_sock.get(), buf, sizeof(buf), &recv_fds, &sender_pid);
130  ASSERT_EQ(sizeof(kHello), static_cast<size_t>(nread));
131  ASSERT_EQ(0, memcmp(buf, kHello, sizeof(kHello)));
132  ASSERT_EQ(UnixDomainSocket::kMaxFileDescriptors, recv_fds.size());
133
134  ASSERT_EQ(getpid(), sender_pid);
135}
136
137// Check that RecvMsgWithPid doesn't DCHECK fail when reading EOF from a
138// disconnected socket.
139TEST(UnixDomianSocketTest, RecvPidDisconnectedSocket) {
140  int fds[2];
141  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
142  base::ScopedFD recv_sock(fds[0]);
143  base::ScopedFD send_sock(fds[1]);
144
145  ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
146
147  send_sock.reset();
148
149  char ch;
150  base::ProcessId sender_pid;
151  ScopedVector<base::ScopedFD> recv_fds;
152  const ssize_t nread = UnixDomainSocket::RecvMsgWithPid(
153      recv_sock.get(), &ch, sizeof(ch), &recv_fds, &sender_pid);
154  ASSERT_EQ(0, nread);
155  ASSERT_EQ(-1, sender_pid);
156  ASSERT_EQ(0U, recv_fds.size());
157}
158
159}  // namespace
160
161}  // namespace base
162