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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sandbox/linux/sandbox_linux_test_sources.gypi ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sandbox/linux/services/unix_domain_socket_unittest.cc
diff --git a/sandbox/linux/services/unix_domain_socket_unittest.cc b/sandbox/linux/services/unix_domain_socket_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..976a4ddde005dc290585c9fca7edd43c33ea5d57
--- /dev/null
+++ b/sandbox/linux/services/unix_domain_socket_unittest.cc
@@ -0,0 +1,255 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sched.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "base/process/process_handle.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+// Additional tests for base's UnixDomainSocket to make sure it behaves
+// correctly in the presence of sandboxing functionality (e.g., receiving
+// PIDs across namespaces).
+
+namespace sandbox {
+
+namespace {
+
+const char kHello[] = "hello";
+
+// If the calling process isn't root, then try using unshare(CLONE_NEWUSER)
+// to fake it.
+void FakeRoot() {
+ // If we're already root, then allow test to proceed.
+ if (geteuid() == 0)
+ return;
+
+ // Otherwise hope the kernel supports unprivileged namespaces.
+ if (unshare(CLONE_NEWUSER) == 0)
+ return;
+
+ printf("Permission to use CLONE_NEWPID missing; skipping test.\n");
+ UnitTests::IgnoreThisTest();
+}
+
+void WaitForExit(pid_t pid) {
+ int status;
+ 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.
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(0, WEXITSTATUS(status));
+}
+
+base::ProcessId GetParentProcessId(base::ProcessId pid) {
+ // base::GetParentProcessId() is defined as taking a ProcessHandle instead of
+ // a ProcessId, even though it's a POSIX-only function and IDs and Handles
+ // are both simply pid_t on POSIX... :/
+ base::ProcessHandle handle;
+ CHECK(base::OpenProcessHandle(pid, &handle));
+ base::ProcessId ret = base::GetParentProcessId(pid);
+ base::CloseProcessHandle(handle);
+ return ret;
+}
+
+void SendHello(int fd) {
+ int pipe_fds[2];
+ CHECK_EQ(0, pipe(pipe_fds));
+ base::ScopedFD read_pipe(pipe_fds[0]);
+ base::ScopedFD write_pipe(pipe_fds[1]);
+
+ std::vector<int> send_fds;
+ send_fds.push_back(write_pipe.get());
+ CHECK(UnixDomainSocket::SendMsg(fd, kHello, sizeof(kHello), send_fds));
+
+ write_pipe.reset();
+
+ // Block until receiver closes their end of the pipe.
+ char ch;
+ 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.
+}
+
+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
+ base::ProcessId* sender_pid,
+ base::ScopedFD* write_pipe = NULL) {
+ // Extra receiving buffer space to make sure we really received only
+ // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer.
+ char buf[sizeof(kHello) + 1];
+ std::vector<int> message_fds;
+ ssize_t n = UnixDomainSocket::RecvMsgWithPid(
+ fd, buf, sizeof(buf), &message_fds, sender_pid);
+ CHECK_EQ(sizeof(kHello), static_cast<size_t>(n));
+ CHECK_EQ(0, memcmp(buf, kHello, sizeof(kHello)));
+ CHECK_EQ(1U, message_fds.size());
+ base::ScopedFD message_fd(message_fds[0]);
+ if (write_pipe)
+ write_pipe->swap(message_fd);
+}
+
+// Check that receiving PIDs works across a fork().
+SANDBOX_TEST(UnixDomainSocketTest, Fork) {
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = fork();
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ recv_sock.reset();
+ SendHello(send_sock.get());
+ _exit(0);
+ }
+
+ // Parent process.
+ send_sock.reset();
+
+ base::ProcessId sender_pid;
+ RecvHello(recv_sock.get(), &sender_pid);
+ CHECK_EQ(pid, sender_pid);
+
+ WaitForExit(pid);
jln (very slow on Chromium) 2014/04/25 23:23:31 Thanks for cleaning-up :) (In SANDBOX_TEST it's n
+}
+
+// Similar to Fork above, but forking the child into a new pid namespace.
+SANDBOX_TEST(UnixDomainSocketTest, Namespace) {
+ FakeRoot();
+
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ recv_sock.reset();
+
+ // Check that we think we're pid 1 in our new namespace.
+ CHECK_EQ(1, syscall(__NR_getpid));
+
+ SendHello(send_sock.get());
+ _exit(0);
+ }
+
+ // Parent process.
+ send_sock.reset();
+
+ base::ProcessId sender_pid;
+ RecvHello(recv_sock.get(), &sender_pid);
+ CHECK_EQ(pid, sender_pid);
+
+ WaitForExit(pid);
+}
+
+// Again similar to Fork, but now with nested PID namespaces.
+SANDBOX_TEST(UnixDomainSocketTest, DoubleNamespace) {
+ FakeRoot();
+
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ recv_sock.reset();
+
+ const pid_t pid2 = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+ CHECK_NE(-1, pid2);
+
+ if (pid2 != 0) {
+ // Wait for grand child to run to completion; see comments below.
+ WaitForExit(pid2);
+ }
jln (very slow on Chromium) 2014/04/25 23:23:31 Maybe add a "// Fallthrough." comment?
mdempsky 2014/04/25 23:38:29 Done.
+
+ // Check that we think we're pid 1.
+ CHECK_EQ(1, syscall(__NR_getpid));
+
+ SendHello(send_sock.get());
+ _exit(0);
+ }
+
+ // Parent process.
+ send_sock.reset();
+
+ // We have two messages to receive: first from the grand-child,
+ // then from the child.
+
jln (very slow on Chromium) 2014/04/25 23:23:31 Nit: remove space?
mdempsky 2014/04/25 23:38:29 Done.
+ for (unsigned iteration = 0; iteration < 2; ++iteration) {
+ base::ProcessId sender_pid;
+ base::ScopedFD pipe_fd;
+ RecvHello(recv_sock.get(), &sender_pid, &pipe_fd);
+
+ // We need our child and grand child processes to both be alive for
+ // GetParentProcessId() to return a valid pid, hence the pipe trickery.
+ // (On the first iteration, grand child is blocked reading from the pipe
+ // until we close it, and child is blocked waiting for grand child to exit.)
+
jln (very slow on Chromium) 2014/04/25 23:23:31 Nit: remove space?
mdempsky 2014/04/25 23:38:29 Done.
+ switch (iteration) {
+ case 0: // Grand child's message
+ 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.
+ break;
+ case 1: // Child's message
+ CHECK_EQ(pid, sender_pid);
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ WaitForExit(pid);
+}
+
+// Tests that GetPeerPid() returns 0 if the peer does not exist in caller's
+// namespace.
+SANDBOX_TEST(UnixDomainSocketTest, ImpossiblePid) {
+ FakeRoot();
+
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD send_sock(fds[0]);
+ base::ScopedFD recv_sock(fds[1]);
+
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ send_sock.reset();
+
+ base::ProcessId sender_pid;
+ RecvHello(recv_sock.get(), &sender_pid);
+ CHECK_EQ(0, sender_pid);
+ _exit(0);
+ }
+
+ // Parent process.
+ recv_sock.reset();
+ SendHello(send_sock.get());
+ WaitForExit(pid);
+}
+
+} // namespace
+
+} // namespace sandbox
« 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