Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(216)

Side by Side Diff: sandbox/linux/services/unix_domain_socket_unittest.cc

Issue 259763002: Add tests to make sure UnixDomainSocket and namespaces play nicely (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cleanup includes for IWYU Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sandbox/linux/sandbox_linux_test_sources.gypi ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 <sched.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <sys/socket.h>
9 #include <sys/syscall.h>
10 #include <sys/wait.h>
11 #include <unistd.h>
12
13 #include <vector>
14
15 #include "base/files/scoped_file.h"
16 #include "base/logging.h"
17 #include "base/posix/unix_domain_socket_linux.h"
18 #include "base/process/process_handle.h"
19 #include "sandbox/linux/tests/unit_tests.h"
20
21 // Additional tests for base's UnixDomainSocket to make sure it behaves
22 // correctly in the presence of sandboxing functionality (e.g., receiving
23 // PIDs across namespaces).
24
25 namespace sandbox {
26
27 namespace {
28
29 const char kHello[] = "hello";
30
31 // If the calling process isn't root, then try using unshare(CLONE_NEWUSER)
32 // to fake it.
33 void FakeRoot() {
34 // If we're already root, then allow test to proceed.
35 if (geteuid() == 0)
36 return;
37
38 // Otherwise hope the kernel supports unprivileged namespaces.
39 if (unshare(CLONE_NEWUSER) == 0)
40 return;
41
42 printf("Permission to use CLONE_NEWPID missing; skipping test.\n");
43 UnitTests::IgnoreThisTest();
44 }
45
46 void WaitForExit(pid_t pid) {
47 int status;
48 CHECK_EQ(pid, waitpid(pid, &status, 0));
jln (very slow on Chromium) 2014/04/25 23:23:31 HANDLE_EINTR
mdempsky 2014/04/25 23:38:29 Done.
49 CHECK(WIFEXITED(status));
50 CHECK_EQ(0, WEXITSTATUS(status));
51 }
52
53 base::ProcessId GetParentProcessId(base::ProcessId pid) {
54 // base::GetParentProcessId() is defined as taking a ProcessHandle instead of
55 // a ProcessId, even though it's a POSIX-only function and IDs and Handles
56 // are both simply pid_t on POSIX... :/
57 base::ProcessHandle handle;
58 CHECK(base::OpenProcessHandle(pid, &handle));
59 base::ProcessId ret = base::GetParentProcessId(pid);
60 base::CloseProcessHandle(handle);
61 return ret;
62 }
63
64 void SendHello(int fd) {
65 int pipe_fds[2];
66 CHECK_EQ(0, pipe(pipe_fds));
67 base::ScopedFD read_pipe(pipe_fds[0]);
68 base::ScopedFD write_pipe(pipe_fds[1]);
69
70 std::vector<int> send_fds;
71 send_fds.push_back(write_pipe.get());
72 CHECK(UnixDomainSocket::SendMsg(fd, kHello, sizeof(kHello), send_fds));
73
74 write_pipe.reset();
75
76 // Block until receiver closes their end of the pipe.
77 char ch;
78 CHECK_EQ(0, read(read_pipe.get(), &ch, 1));
jln (very slow on Chromium) 2014/04/25 23:23:31 HANDLE_EINTR()
mdempsky 2014/04/25 23:38:29 Done.
79 }
80
81 void RecvHello(int fd,
jln (very slow on Chromium) 2014/04/25 23:23:31 Could you document RecvHello() / SendHello() (as i
mdempsky 2014/04/25 23:38:29 Done. PTAL and let me know if the documentation m
jln (very slow on Chromium) 2014/04/25 23:44:00 Perfect, lgtm
82 base::ProcessId* sender_pid,
83 base::ScopedFD* write_pipe = NULL) {
84 // Extra receiving buffer space to make sure we really received only
85 // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer.
86 char buf[sizeof(kHello) + 1];
87 std::vector<int> message_fds;
88 ssize_t n = UnixDomainSocket::RecvMsgWithPid(
89 fd, buf, sizeof(buf), &message_fds, sender_pid);
90 CHECK_EQ(sizeof(kHello), static_cast<size_t>(n));
91 CHECK_EQ(0, memcmp(buf, kHello, sizeof(kHello)));
92 CHECK_EQ(1U, message_fds.size());
93 base::ScopedFD message_fd(message_fds[0]);
94 if (write_pipe)
95 write_pipe->swap(message_fd);
96 }
97
98 // Check that receiving PIDs works across a fork().
99 SANDBOX_TEST(UnixDomainSocketTest, Fork) {
100 int fds[2];
101 CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
102 base::ScopedFD recv_sock(fds[0]);
103 base::ScopedFD send_sock(fds[1]);
104
105 CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
106
107 const pid_t pid = fork();
108 CHECK_NE(-1, pid);
109 if (pid == 0) {
110 // Child process.
111 recv_sock.reset();
112 SendHello(send_sock.get());
113 _exit(0);
114 }
115
116 // Parent process.
117 send_sock.reset();
118
119 base::ProcessId sender_pid;
120 RecvHello(recv_sock.get(), &sender_pid);
121 CHECK_EQ(pid, sender_pid);
122
123 WaitForExit(pid);
jln (very slow on Chromium) 2014/04/25 23:23:31 Thanks for cleaning-up :) (In SANDBOX_TEST it's n
124 }
125
126 // Similar to Fork above, but forking the child into a new pid namespace.
127 SANDBOX_TEST(UnixDomainSocketTest, Namespace) {
128 FakeRoot();
129
130 int fds[2];
131 CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
132 base::ScopedFD recv_sock(fds[0]);
133 base::ScopedFD send_sock(fds[1]);
134
135 CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
136
137 const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
138 CHECK_NE(-1, pid);
139 if (pid == 0) {
140 // Child process.
141 recv_sock.reset();
142
143 // Check that we think we're pid 1 in our new namespace.
144 CHECK_EQ(1, syscall(__NR_getpid));
145
146 SendHello(send_sock.get());
147 _exit(0);
148 }
149
150 // Parent process.
151 send_sock.reset();
152
153 base::ProcessId sender_pid;
154 RecvHello(recv_sock.get(), &sender_pid);
155 CHECK_EQ(pid, sender_pid);
156
157 WaitForExit(pid);
158 }
159
160 // Again similar to Fork, but now with nested PID namespaces.
161 SANDBOX_TEST(UnixDomainSocketTest, DoubleNamespace) {
162 FakeRoot();
163
164 int fds[2];
165 CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
166 base::ScopedFD recv_sock(fds[0]);
167 base::ScopedFD send_sock(fds[1]);
168
169 CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
170
171 const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
172 CHECK_NE(-1, pid);
173 if (pid == 0) {
174 // Child process.
175 recv_sock.reset();
176
177 const pid_t pid2 = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
178 CHECK_NE(-1, pid2);
179
180 if (pid2 != 0) {
181 // Wait for grand child to run to completion; see comments below.
182 WaitForExit(pid2);
183 }
jln (very slow on Chromium) 2014/04/25 23:23:31 Maybe add a "// Fallthrough." comment?
mdempsky 2014/04/25 23:38:29 Done.
184
185 // Check that we think we're pid 1.
186 CHECK_EQ(1, syscall(__NR_getpid));
187
188 SendHello(send_sock.get());
189 _exit(0);
190 }
191
192 // Parent process.
193 send_sock.reset();
194
195 // We have two messages to receive: first from the grand-child,
196 // then from the child.
197
jln (very slow on Chromium) 2014/04/25 23:23:31 Nit: remove space?
mdempsky 2014/04/25 23:38:29 Done.
198 for (unsigned iteration = 0; iteration < 2; ++iteration) {
199 base::ProcessId sender_pid;
200 base::ScopedFD pipe_fd;
201 RecvHello(recv_sock.get(), &sender_pid, &pipe_fd);
202
203 // We need our child and grand child processes to both be alive for
204 // GetParentProcessId() to return a valid pid, hence the pipe trickery.
205 // (On the first iteration, grand child is blocked reading from the pipe
206 // until we close it, and child is blocked waiting for grand child to exit.)
207
jln (very slow on Chromium) 2014/04/25 23:23:31 Nit: remove space?
mdempsky 2014/04/25 23:38:29 Done.
208 switch (iteration) {
209 case 0: // Grand child's message
210 CHECK_EQ(pid, GetParentProcessId(sender_pid));
jln (very slow on Chromium) 2014/04/25 23:23:31 Maybe add a comment: "Check that sender_pid is our
mdempsky 2014/04/25 23:38:29 Done.
211 break;
212 case 1: // Child's message
213 CHECK_EQ(pid, sender_pid);
214 break;
215 default:
216 NOTREACHED();
217 }
218 }
219
220 WaitForExit(pid);
221 }
222
223 // Tests that GetPeerPid() returns 0 if the peer does not exist in caller's
224 // namespace.
225 SANDBOX_TEST(UnixDomainSocketTest, ImpossiblePid) {
226 FakeRoot();
227
228 int fds[2];
229 CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
230 base::ScopedFD send_sock(fds[0]);
231 base::ScopedFD recv_sock(fds[1]);
232
233 CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
234
235 const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
236 CHECK_NE(-1, pid);
237 if (pid == 0) {
238 // Child process.
239 send_sock.reset();
240
241 base::ProcessId sender_pid;
242 RecvHello(recv_sock.get(), &sender_pid);
243 CHECK_EQ(0, sender_pid);
244 _exit(0);
245 }
246
247 // Parent process.
248 recv_sock.reset();
249 SendHello(send_sock.get());
250 WaitForExit(pid);
251 }
252
253 } // namespace
254
255 } // namespace sandbox
OLDNEW
« no previous file with comments | « sandbox/linux/sandbox_linux_test_sources.gypi ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698