OLD | NEW |
---|---|
(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 | |
OLD | NEW |