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/memory/scoped_vector.h" 18#include "base/posix/eintr_wrapper.h" 19#include "base/posix/unix_domain_socket_linux.h" 20#include "base/process/process_handle.h" 21#include "sandbox/linux/tests/unit_tests.h" 22 23// Additional tests for base's UnixDomainSocket to make sure it behaves 24// correctly in the presence of sandboxing functionality (e.g., receiving 25// PIDs across namespaces). 26 27namespace sandbox { 28 29namespace { 30 31const char kHello[] = "hello"; 32 33// If the calling process isn't root, then try using unshare(CLONE_NEWUSER) 34// to fake it. 35void FakeRoot() { 36 // If we're already root, then allow test to proceed. 37 if (geteuid() == 0) 38 return; 39 40 // Otherwise hope the kernel supports unprivileged namespaces. 41 if (unshare(CLONE_NEWUSER) == 0) 42 return; 43 44 printf("Permission to use CLONE_NEWPID missing; skipping test.\n"); 45 UnitTests::IgnoreThisTest(); 46} 47 48void WaitForExit(pid_t pid) { 49 int status; 50 CHECK_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0))); 51 CHECK(WIFEXITED(status)); 52 CHECK_EQ(0, WEXITSTATUS(status)); 53} 54 55base::ProcessId GetParentProcessId(base::ProcessId pid) { 56 // base::GetParentProcessId() is defined as taking a ProcessHandle instead of 57 // a ProcessId, even though it's a POSIX-only function and IDs and Handles 58 // are both simply pid_t on POSIX... :/ 59 base::ProcessHandle handle; 60 CHECK(base::OpenProcessHandle(pid, &handle)); 61 base::ProcessId ret = base::GetParentProcessId(pid); 62 base::CloseProcessHandle(handle); 63 return ret; 64} 65 66// SendHello sends a "hello" to socket fd, and then blocks until the recipient 67// acknowledges it by calling RecvHello. 68void SendHello(int fd) { 69 int pipe_fds[2]; 70 CHECK_EQ(0, pipe(pipe_fds)); 71 base::ScopedFD read_pipe(pipe_fds[0]); 72 base::ScopedFD write_pipe(pipe_fds[1]); 73 74 std::vector<int> send_fds; 75 send_fds.push_back(write_pipe.get()); 76 CHECK(UnixDomainSocket::SendMsg(fd, kHello, sizeof(kHello), send_fds)); 77 78 write_pipe.reset(); 79 80 // Block until receiver closes their end of the pipe. 81 char ch; 82 CHECK_EQ(0, HANDLE_EINTR(read(read_pipe.get(), &ch, 1))); 83} 84 85// RecvHello receives and acknowledges a "hello" on socket fd, and returns the 86// process ID of the sender in sender_pid. Optionally, write_pipe can be used 87// to return a file descriptor, and the acknowledgement will be delayed until 88// the descriptor is closed. 89// (Implementation details: SendHello allocates a new pipe, sends us the writing 90// end alongside the "hello" message, and then blocks until we close the writing 91// end of the pipe.) 92void RecvHello(int fd, 93 base::ProcessId* sender_pid, 94 base::ScopedFD* write_pipe = NULL) { 95 // Extra receiving buffer space to make sure we really received only 96 // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer. 97 char buf[sizeof(kHello) + 1]; 98 ScopedVector<base::ScopedFD> message_fds; 99 ssize_t n = UnixDomainSocket::RecvMsgWithPid( 100 fd, buf, sizeof(buf), &message_fds, sender_pid); 101 CHECK_EQ(sizeof(kHello), static_cast<size_t>(n)); 102 CHECK_EQ(0, memcmp(buf, kHello, sizeof(kHello))); 103 CHECK_EQ(1U, message_fds.size()); 104 if (write_pipe) 105 write_pipe->swap(*message_fds[0]); 106} 107 108// Check that receiving PIDs works across a fork(). 109SANDBOX_TEST(UnixDomainSocketTest, Fork) { 110 int fds[2]; 111 CHECK_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 CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get())); 116 117 const pid_t pid = fork(); 118 CHECK_NE(-1, pid); 119 if (pid == 0) { 120 // Child process. 121 recv_sock.reset(); 122 SendHello(send_sock.get()); 123 _exit(0); 124 } 125 126 // Parent process. 127 send_sock.reset(); 128 129 base::ProcessId sender_pid; 130 RecvHello(recv_sock.get(), &sender_pid); 131 CHECK_EQ(pid, sender_pid); 132 133 WaitForExit(pid); 134} 135 136// Similar to Fork above, but forking the child into a new pid namespace. 137SANDBOX_TEST(UnixDomainSocketTest, Namespace) { 138 FakeRoot(); 139 140 int fds[2]; 141 CHECK_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 CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get())); 146 147 const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0); 148 CHECK_NE(-1, pid); 149 if (pid == 0) { 150 // Child process. 151 recv_sock.reset(); 152 153 // Check that we think we're pid 1 in our new namespace. 154 CHECK_EQ(1, syscall(__NR_getpid)); 155 156 SendHello(send_sock.get()); 157 _exit(0); 158 } 159 160 // Parent process. 161 send_sock.reset(); 162 163 base::ProcessId sender_pid; 164 RecvHello(recv_sock.get(), &sender_pid); 165 CHECK_EQ(pid, sender_pid); 166 167 WaitForExit(pid); 168} 169 170// Again similar to Fork, but now with nested PID namespaces. 171SANDBOX_TEST(UnixDomainSocketTest, DoubleNamespace) { 172 FakeRoot(); 173 174 int fds[2]; 175 CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); 176 base::ScopedFD recv_sock(fds[0]); 177 base::ScopedFD send_sock(fds[1]); 178 179 CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get())); 180 181 const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0); 182 CHECK_NE(-1, pid); 183 if (pid == 0) { 184 // Child process. 185 recv_sock.reset(); 186 187 const pid_t pid2 = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0); 188 CHECK_NE(-1, pid2); 189 190 if (pid2 != 0) { 191 // Wait for grandchild to run to completion; see comments below. 192 WaitForExit(pid2); 193 194 // Fallthrough once grandchild has sent its hello and exited. 195 } 196 197 // Check that we think we're pid 1. 198 CHECK_EQ(1, syscall(__NR_getpid)); 199 200 SendHello(send_sock.get()); 201 _exit(0); 202 } 203 204 // Parent process. 205 send_sock.reset(); 206 207 // We have two messages to receive: first from the grand-child, 208 // then from the child. 209 for (unsigned iteration = 0; iteration < 2; ++iteration) { 210 base::ProcessId sender_pid; 211 base::ScopedFD pipe_fd; 212 RecvHello(recv_sock.get(), &sender_pid, &pipe_fd); 213 214 // We need our child and grandchild processes to both be alive for 215 // GetParentProcessId() to return a valid pid, hence the pipe trickery. 216 // (On the first iteration, grandchild is blocked reading from the pipe 217 // until we close it, and child is blocked waiting for grandchild to exit.) 218 switch (iteration) { 219 case 0: // Grandchild's message 220 // Check that sender_pid refers to our grandchild by checking that pid 221 // (our child) is its parent. 222 CHECK_EQ(pid, GetParentProcessId(sender_pid)); 223 break; 224 case 1: // Child's message 225 CHECK_EQ(pid, sender_pid); 226 break; 227 default: 228 NOTREACHED(); 229 } 230 } 231 232 WaitForExit(pid); 233} 234 235// Tests that GetPeerPid() returns 0 if the peer does not exist in caller's 236// namespace. 237SANDBOX_TEST(UnixDomainSocketTest, ImpossiblePid) { 238 FakeRoot(); 239 240 int fds[2]; 241 CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); 242 base::ScopedFD send_sock(fds[0]); 243 base::ScopedFD recv_sock(fds[1]); 244 245 CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get())); 246 247 const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0); 248 CHECK_NE(-1, pid); 249 if (pid == 0) { 250 // Child process. 251 send_sock.reset(); 252 253 base::ProcessId sender_pid; 254 RecvHello(recv_sock.get(), &sender_pid); 255 CHECK_EQ(0, sender_pid); 256 _exit(0); 257 } 258 259 // Parent process. 260 recv_sock.reset(); 261 SendHello(send_sock.get()); 262 WaitForExit(pid); 263} 264 265} // namespace 266 267} // namespace sandbox 268