Index: projects/capsicum-test/contrib/capsicum-test/capmode.cc =================================================================== --- projects/capsicum-test/contrib/capsicum-test/capmode.cc (revision 345216) +++ projects/capsicum-test/contrib/capsicum-test/capmode.cc (revision 345217) @@ -1,651 +1,651 @@ // Test routines to make sure a variety of system calls are or are not // available in capability mode. The goal is not to see if they work, just // whether or not they return the expected ECAPMODE. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "capsicum.h" #include "syscalls.h" #include "capsicum-test.h" // Test fixture that opens (and closes) a bunch of files. class WithFiles : public ::testing::Test { public: WithFiles() : fd_file_(open(TmpFile("cap_capmode"), O_RDWR|O_CREAT, 0644)), fd_close_(open("/dev/null", O_RDWR)), fd_dir_(open(tmpdir.c_str(), O_RDONLY)), fd_socket_(socket(PF_INET, SOCK_DGRAM, 0)), fd_tcp_socket_(socket(PF_INET, SOCK_STREAM, 0)) { EXPECT_OK(fd_file_); EXPECT_OK(fd_close_); EXPECT_OK(fd_dir_); EXPECT_OK(fd_socket_); EXPECT_OK(fd_tcp_socket_); } ~WithFiles() { if (fd_tcp_socket_ >= 0) close(fd_tcp_socket_); if (fd_socket_ >= 0) close(fd_socket_); if (fd_dir_ >= 0) close(fd_dir_); if (fd_close_ >= 0) close(fd_close_); if (fd_file_ >= 0) close(fd_file_); unlink(TmpFile("cap_capmode")); } protected: int fd_file_; int fd_close_; int fd_dir_; int fd_socket_; int fd_tcp_socket_; }; FORK_TEST_F(WithFiles, DisallowedFileSyscalls) { unsigned int mode = -1; EXPECT_OK(cap_getmode(&mode)); EXPECT_EQ(0, (int)mode); EXPECT_OK(cap_enter()); // Enter capability mode. EXPECT_OK(cap_getmode(&mode)); EXPECT_EQ(1, (int)mode); // System calls that are not permitted in capability mode. EXPECT_CAPMODE(access(TmpFile("cap_capmode_access"), F_OK)); EXPECT_CAPMODE(acct(TmpFile("cap_capmode_acct"))); EXPECT_CAPMODE(chdir(TmpFile("cap_capmode_chdir"))); #ifdef HAVE_CHFLAGS EXPECT_CAPMODE(chflags(TmpFile("cap_capmode_chflags"), UF_NODUMP)); #endif EXPECT_CAPMODE(chmod(TmpFile("cap_capmode_chmod"), 0644)); EXPECT_CAPMODE(chown(TmpFile("cap_capmode_chown"), -1, -1)); EXPECT_CAPMODE(chroot(TmpFile("cap_capmode_chroot"))); EXPECT_CAPMODE(creat(TmpFile("cap_capmode_creat"), 0644)); EXPECT_CAPMODE(fchdir(fd_dir_)); #ifdef HAVE_GETFSSTAT struct statfs statfs; EXPECT_CAPMODE(getfsstat(&statfs, sizeof(statfs), MNT_NOWAIT)); #endif EXPECT_CAPMODE(link(TmpFile("foo"), TmpFile("bar"))); struct stat sb; EXPECT_CAPMODE(lstat(TmpFile("cap_capmode_lstat"), &sb)); EXPECT_CAPMODE(mknod(TmpFile("capmode_mknod"), 0644 | S_IFIFO, 0)); EXPECT_CAPMODE(bogus_mount_()); EXPECT_CAPMODE(open("/dev/null", O_RDWR)); char buf[64]; EXPECT_CAPMODE(readlink(TmpFile("cap_capmode_readlink"), buf, sizeof(buf))); #ifdef HAVE_REVOKE EXPECT_CAPMODE(revoke(TmpFile("cap_capmode_revoke"))); #endif EXPECT_CAPMODE(stat(TmpFile("cap_capmode_stat"), &sb)); EXPECT_CAPMODE(symlink(TmpFile("cap_capmode_symlink_from"), TmpFile("cap_capmode_symlink_to"))); EXPECT_CAPMODE(unlink(TmpFile("cap_capmode_unlink"))); EXPECT_CAPMODE(umount2("/not_mounted", 0)); } FORK_TEST_F(WithFiles, DisallowedSocketSyscalls) { EXPECT_OK(cap_enter()); // Enter capability mode. // System calls that are not permitted in capability mode. struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = 0; addr.sin_addr.s_addr = htonl(INADDR_ANY); EXPECT_CAPMODE(bind_(fd_socket_, (sockaddr*)&addr, sizeof(addr))); addr.sin_family = AF_INET; addr.sin_port = 53; addr.sin_addr.s_addr = htonl(0x08080808); EXPECT_CAPMODE(connect_(fd_tcp_socket_, (sockaddr*)&addr, sizeof(addr))); } FORK_TEST_F(WithFiles, AllowedFileSyscalls) { int rc; EXPECT_OK(cap_enter()); // Enter capability mode. EXPECT_OK(close(fd_close_)); fd_close_ = -1; int fd_dup = dup(fd_file_); EXPECT_OK(fd_dup); EXPECT_OK(dup2(fd_file_, fd_dup)); #ifdef HAVE_DUP3 EXPECT_OK(dup3(fd_file_, fd_dup, 0)); #endif if (fd_dup >= 0) close(fd_dup); struct stat sb; EXPECT_OK(fstat(fd_file_, &sb)); EXPECT_OK(lseek(fd_file_, 0, SEEK_SET)); char ch; EXPECT_OK(read(fd_file_, &ch, sizeof(ch))); EXPECT_OK(write(fd_file_, &ch, sizeof(ch))); #ifdef HAVE_CHFLAGS rc = fchflags(fd_file_, UF_NODUMP); if (rc < 0) EXPECT_NE(ECAPMODE, errno); #endif char buf[1024]; rc = getdents_(fd_dir_, (void*)buf, sizeof(buf)); EXPECT_OK(rc); char data[] = "123"; EXPECT_OK(pwrite(fd_file_, data, 1, 0)); EXPECT_OK(pread(fd_file_, data, 1, 0)); struct iovec io; io.iov_base = data; io.iov_len = 2; #if !defined(__i386__) && !defined(__linux__) // TODO(drysdale): reinstate these tests for 32-bit runs when possible // libc bug is fixed. EXPECT_OK(pwritev(fd_file_, &io, 1, 0)); EXPECT_OK(preadv(fd_file_, &io, 1, 0)); #endif EXPECT_OK(writev(fd_file_, &io, 1)); EXPECT_OK(readv(fd_file_, &io, 1)); #ifdef HAVE_SYNCFS EXPECT_OK(syncfs(fd_file_)); #endif #ifdef HAVE_SYNC_FILE_RANGE EXPECT_OK(sync_file_range(fd_file_, 0, 1, 0)); #endif #ifdef HAVE_READAHEAD if (!tmpdir_on_tmpfs) { // tmpfs doesn't support readahead(2) EXPECT_OK(readahead(fd_file_, 0, 1)); } #endif } FORK_TEST_F(WithFiles, AllowedSocketSyscalls) { EXPECT_OK(cap_enter()); // Enter capability mode. // recvfrom() either returns -1 with EAGAIN, or 0. int rc = recvfrom(fd_socket_, NULL, 0, MSG_DONTWAIT, NULL, NULL); if (rc < 0) EXPECT_EQ(EAGAIN, errno); char ch; EXPECT_OK(write(fd_file_, &ch, sizeof(ch))); // These calls will fail for lack of e.g. a proper name to send to, // but they are allowed in capability mode, so errno != ECAPMODE. EXPECT_FAIL_NOT_CAPMODE(accept(fd_socket_, NULL, NULL)); EXPECT_FAIL_NOT_CAPMODE(getpeername(fd_socket_, NULL, NULL)); EXPECT_FAIL_NOT_CAPMODE(getsockname(fd_socket_, NULL, NULL)); EXPECT_FAIL_NOT_CAPMODE(recvmsg(fd_socket_, NULL, 0)); EXPECT_FAIL_NOT_CAPMODE(sendmsg(fd_socket_, NULL, 0)); EXPECT_FAIL_NOT_CAPMODE(sendto(fd_socket_, NULL, 0, 0, NULL, 0)); off_t offset = 0; EXPECT_FAIL_NOT_CAPMODE(sendfile_(fd_socket_, fd_file_, &offset, 1)); // The socket/socketpair syscalls are allowed, but they don't give // anything externally useful (can't call bind/connect on them). int fd_socket2 = socket(PF_INET, SOCK_DGRAM, 0); EXPECT_OK(fd_socket2); if (fd_socket2 >= 0) close(fd_socket2); int fd_pair[2] = {-1, -1}; EXPECT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, fd_pair)); if (fd_pair[0] >= 0) close(fd_pair[0]); if (fd_pair[1] >= 0) close(fd_pair[1]); } #ifdef HAVE_SEND_RECV_MMSG FORK_TEST(Capmode, AllowedMmsgSendRecv) { int fd_socket = socket(PF_INET, SOCK_DGRAM, 0); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(0); addr.sin_addr.s_addr = htonl(INADDR_ANY); EXPECT_OK(bind(fd_socket, (sockaddr*)&addr, sizeof(addr))); EXPECT_OK(cap_enter()); // Enter capability mode. char buffer[256] = {0}; struct iovec iov; iov.iov_base = buffer; iov.iov_len = sizeof(buffer); struct mmsghdr mm; memset(&mm, 0, sizeof(mm)); mm.msg_hdr.msg_iov = &iov; mm.msg_hdr.msg_iovlen = 1; struct timespec ts; ts.tv_sec = 1; ts.tv_nsec = 100; EXPECT_FAIL_NOT_CAPMODE(recvmmsg(fd_socket, &mm, 1, MSG_DONTWAIT, &ts)); EXPECT_FAIL_NOT_CAPMODE(sendmmsg(fd_socket, &mm, 1, 0)); close(fd_socket); } #endif FORK_TEST(Capmode, AllowedIdentifierSyscalls) { // Record some identifiers gid_t my_gid = getgid(); pid_t my_pid = getpid(); pid_t my_ppid = getppid(); uid_t my_uid = getuid(); pid_t my_sid = getsid(my_pid); EXPECT_OK(cap_enter()); // Enter capability mode. EXPECT_EQ(my_gid, getegid_()); EXPECT_EQ(my_uid, geteuid_()); EXPECT_EQ(my_gid, getgid_()); EXPECT_EQ(my_pid, getpid()); EXPECT_EQ(my_ppid, getppid()); EXPECT_EQ(my_uid, getuid_()); EXPECT_EQ(my_sid, getsid(my_pid)); gid_t grps[128]; EXPECT_OK(getgroups_(128, grps)); uid_t ruid; uid_t euid; uid_t suid; EXPECT_OK(getresuid(&ruid, &euid, &suid)); gid_t rgid; gid_t egid; gid_t sgid; EXPECT_OK(getresgid(&rgid, &egid, &sgid)); #ifdef HAVE_GETLOGIN EXPECT_TRUE(getlogin() != NULL); #endif // Set various identifiers (to their existing values). EXPECT_OK(setgid(my_gid)); #ifdef HAVE_SETFSGID EXPECT_OK(setfsgid(my_gid)); #endif EXPECT_OK(setuid(my_uid)); #ifdef HAVE_SETFSUID EXPECT_OK(setfsuid(my_uid)); #endif EXPECT_OK(setregid(my_gid, my_gid)); EXPECT_OK(setresgid(my_gid, my_gid, my_gid)); EXPECT_OK(setreuid(my_uid, my_uid)); EXPECT_OK(setresuid(my_uid, my_uid, my_uid)); EXPECT_OK(setsid()); } FORK_TEST(Capmode, AllowedSchedSyscalls) { EXPECT_OK(cap_enter()); // Enter capability mode. int policy = sched_getscheduler(0); EXPECT_OK(policy); struct sched_param sp; EXPECT_OK(sched_getparam(0, &sp)); if (policy >= 0 && (!SCHED_SETSCHEDULER_REQUIRES_ROOT || getuid() == 0)) { EXPECT_OK(sched_setscheduler(0, policy, &sp)); } EXPECT_OK(sched_setparam(0, &sp)); EXPECT_OK(sched_get_priority_max(policy)); EXPECT_OK(sched_get_priority_min(policy)); struct timespec ts; EXPECT_OK(sched_rr_get_interval(0, &ts)); EXPECT_OK(sched_yield()); } FORK_TEST(Capmode, AllowedTimerSyscalls) { EXPECT_OK(cap_enter()); // Enter capability mode. struct timespec ts; EXPECT_OK(clock_getres(CLOCK_REALTIME, &ts)); EXPECT_OK(clock_gettime(CLOCK_REALTIME, &ts)); struct itimerval itv; EXPECT_OK(getitimer(ITIMER_REAL, &itv)); EXPECT_OK(setitimer(ITIMER_REAL, &itv, NULL)); struct timeval tv; struct timezone tz; EXPECT_OK(gettimeofday(&tv, &tz)); ts.tv_sec = 0; ts.tv_nsec = 1; EXPECT_OK(nanosleep(&ts, NULL)); } FORK_TEST(Capmode, AllowedProfilSyscall) { EXPECT_OK(cap_enter()); // Enter capability mode. char sbuf[32]; EXPECT_OK(profil((profil_arg1_t*)sbuf, sizeof(sbuf), 0, 1)); } FORK_TEST(Capmode, AllowedResourceSyscalls) { EXPECT_OK(cap_enter()); // Enter capability mode. errno = 0; int rc = getpriority(PRIO_PROCESS, 0); EXPECT_EQ(0, errno); EXPECT_OK(setpriority(PRIO_PROCESS, 0, rc)); struct rlimit rlim; EXPECT_OK(getrlimit_(RLIMIT_CORE, &rlim)); EXPECT_OK(setrlimit(RLIMIT_CORE, &rlim)); struct rusage ruse; EXPECT_OK(getrusage(RUSAGE_SELF, &ruse)); } FORK_TEST(CapMode, AllowedMmapSyscalls) { // mmap() some memory. size_t mem_size = getpagesize(); void *mem = mmap(NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); EXPECT_TRUE(mem != NULL); EXPECT_OK(cap_enter()); // Enter capability mode. EXPECT_OK(msync(mem, mem_size, MS_ASYNC)); EXPECT_OK(madvise(mem, mem_size, MADV_NORMAL)); unsigned char vec[2]; EXPECT_OK(mincore_(mem, mem_size, vec)); EXPECT_OK(mprotect(mem, mem_size, PROT_READ|PROT_WRITE)); if (!MLOCK_REQUIRES_ROOT || getuid() == 0) { EXPECT_OK(mlock(mem, mem_size)); EXPECT_OK(munlock(mem, mem_size)); int rc = mlockall(MCL_CURRENT); if (rc != 0) { // mlockall may well fail with ENOMEM for non-root users, as the // default RLIMIT_MEMLOCK value isn't that big. EXPECT_NE(ECAPMODE, errno); } EXPECT_OK(munlockall()); } // Unmap the memory. EXPECT_OK(munmap(mem, mem_size)); } FORK_TEST(Capmode, AllowedPipeSyscalls) { EXPECT_OK(cap_enter()); // Enter capability mode int fd2[2]; int rc = pipe(fd2); EXPECT_EQ(0, rc); #ifdef HAVE_VMSPLICE char buf[11] = "0123456789"; struct iovec iov; iov.iov_base = buf; iov.iov_len = sizeof(buf); EXPECT_FAIL_NOT_CAPMODE(vmsplice(fd2[0], &iov, 1, SPLICE_F_NONBLOCK)); #endif if (rc == 0) { close(fd2[0]); close(fd2[1]); }; #ifdef HAVE_PIPE2 rc = pipe2(fd2, 0); EXPECT_EQ(0, rc); if (rc == 0) { close(fd2[0]); close(fd2[1]); }; #endif } TEST(Capmode, AllowedAtSyscalls) { int rc = mkdir(TmpFile("cap_at_syscalls"), 0755); EXPECT_OK(rc); if (rc < 0 && errno != EEXIST) return; int dfd = open(TmpFile("cap_at_syscalls"), O_RDONLY); EXPECT_OK(dfd); int file = openat(dfd, "testfile", O_RDONLY|O_CREAT, 0644); EXPECT_OK(file); EXPECT_OK(close(file)); pid_t child = fork(); if (child == 0) { // Child: enter cap mode and run tests EXPECT_OK(cap_enter()); // Enter capability mode struct stat fs; EXPECT_OK(fstatat(dfd, "testfile", &fs, 0)); EXPECT_OK(mkdirat(dfd, "subdir", 0600)); EXPECT_OK(fchmodat(dfd, "subdir", 0644, 0)); EXPECT_OK(faccessat(dfd, "subdir", F_OK, 0)); EXPECT_OK(renameat(dfd, "subdir", dfd, "subdir2")); EXPECT_OK(renameat(dfd, "subdir2", dfd, "subdir")); struct timeval tv[2]; struct timezone tz; EXPECT_OK(gettimeofday(&tv[0], &tz)); EXPECT_OK(gettimeofday(&tv[1], &tz)); EXPECT_OK(futimesat(dfd, "testfile", tv)); EXPECT_OK(fchownat(dfd, "testfile", fs.st_uid, fs.st_gid, 0)); EXPECT_OK(linkat(dfd, "testfile", dfd, "linky", 0)); EXPECT_OK(symlinkat("testfile", dfd, "symlink")); char buffer[256]; EXPECT_OK(readlinkat(dfd, "symlink", buffer, sizeof(buffer))); EXPECT_OK(unlinkat(dfd, "linky", 0)); EXPECT_OK(unlinkat(dfd, "subdir", AT_REMOVEDIR)); // Check that invalid requests get a non-Capsicum errno. errno = 0; rc = readlinkat(-1, "symlink", buffer, sizeof(buffer)); EXPECT_GE(0, rc); EXPECT_NE(ECAPMODE, errno); exit(HasFailure()); } // Wait for the child. int status; EXPECT_EQ(child, waitpid(child, &status, 0)); rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1; EXPECT_EQ(0, rc); // Tidy up. close(dfd); rmdir(TmpFile("cap_at_syscalls/subdir")); unlink(TmpFile("cap_at_syscalls/symlink")); unlink(TmpFile("cap_at_syscalls/linky")); unlink(TmpFile("cap_at_syscalls/testfile")); rmdir(TmpFile("cap_at_syscalls")); } TEST(Capmode, AllowedAtSyscallsCwd) { int rc = mkdir(TmpFile("cap_at_syscalls_cwd"), 0755); EXPECT_OK(rc); if (rc < 0 && errno != EEXIST) return; int dfd = open(TmpFile("cap_at_syscalls_cwd"), O_RDONLY); EXPECT_OK(dfd); int file = openat(dfd, "testfile", O_RDONLY|O_CREAT, 0644); EXPECT_OK(file); EXPECT_OK(close(file)); pid_t child = fork(); if (child == 0) { // Child: move into temp dir, enter cap mode and run tests EXPECT_OK(fchdir(dfd)); EXPECT_OK(cap_enter()); // Enter capability mode // Test that *at(AT_FDCWD, path,...) is policed with ECAPMODE. EXPECT_CAPMODE(openat(AT_FDCWD, "testfile", O_RDONLY)); struct stat fs; EXPECT_CAPMODE(fstatat(AT_FDCWD, "testfile", &fs, 0)); EXPECT_CAPMODE(mkdirat(AT_FDCWD, "subdir", 0600)); EXPECT_CAPMODE(fchmodat(AT_FDCWD, "subdir", 0644, 0)); EXPECT_CAPMODE(faccessat(AT_FDCWD, "subdir", F_OK, 0)); EXPECT_CAPMODE(renameat(AT_FDCWD, "subdir", AT_FDCWD, "subdir2")); EXPECT_CAPMODE(renameat(AT_FDCWD, "subdir2", AT_FDCWD, "subdir")); struct timeval tv[2]; struct timezone tz; EXPECT_OK(gettimeofday(&tv[0], &tz)); EXPECT_OK(gettimeofday(&tv[1], &tz)); EXPECT_CAPMODE(futimesat(AT_FDCWD, "testfile", tv)); EXPECT_CAPMODE(fchownat(AT_FDCWD, "testfile", fs.st_uid, fs.st_gid, 0)); EXPECT_CAPMODE(linkat(AT_FDCWD, "testfile", AT_FDCWD, "linky", 0)); EXPECT_CAPMODE(symlinkat("testfile", AT_FDCWD, "symlink")); char buffer[256]; EXPECT_CAPMODE(readlinkat(AT_FDCWD, "symlink", buffer, sizeof(buffer))); EXPECT_CAPMODE(unlinkat(AT_FDCWD, "linky", 0)); exit(HasFailure()); } // Wait for the child. int status; EXPECT_EQ(child, waitpid(child, &status, 0)); rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1; EXPECT_EQ(0, rc); // Tidy up. close(dfd); rmdir(TmpFile("cap_at_syscalls_cwd/subdir")); unlink(TmpFile("cap_at_syscalls_cwd/symlink")); unlink(TmpFile("cap_at_syscalls_cwd/linky")); unlink(TmpFile("cap_at_syscalls_cwd/testfile")); rmdir(TmpFile("cap_at_syscalls_cwd")); } TEST(Capmode, Abort) { // Check that abort(3) works even in capability mode. pid_t child = fork(); if (child == 0) { // Child: enter capability mode and call abort(3). // Triggers something like kill(getpid(), SIGABRT). cap_enter(); // Enter capability mode. abort(); exit(99); } int status; EXPECT_EQ(child, waitpid(child, &status, 0)); EXPECT_TRUE(WIFSIGNALED(status)) << " status = " << std::hex << status; EXPECT_EQ(SIGABRT, WTERMSIG(status)) << " status = " << std::hex << status; } FORK_TEST_F(WithFiles, AllowedMiscSyscalls) { umask(022); mode_t um_before = umask(022); EXPECT_OK(cap_enter()); // Enter capability mode. mode_t um = umask(022); EXPECT_NE(-ECAPMODE, (int)um); EXPECT_EQ(um_before, um); stack_t ss; EXPECT_OK(sigaltstack(NULL, &ss)); // Finally, tests for system calls that don't fit the pattern very well. pid_t pid = fork(); EXPECT_OK(pid); if (pid == 0) { // Child: almost immediately exit. sleep(1); exit(0); } else if (pid > 0) { errno = 0; EXPECT_CAPMODE(ptrace_(PTRACE_PEEKDATA_, pid, &pid, NULL)); EXPECT_CAPMODE(waitpid(pid, NULL, 0)); } // No error return from sync(2) to test, but check errno remains unset. errno = 0; sync(); EXPECT_EQ(0, errno); // TODO(FreeBSD): ktrace #ifdef HAVE_SYSARCH // sysarch() is, by definition, architecture-dependent #if defined (__amd64__) || defined (__i386__) long sysarch_arg = 0; EXPECT_CAPMODE(sysarch(I386_SET_IOPERM, &sysarch_arg)); #else // TOOD(jra): write a test for arm FAIL("capmode:no sysarch() test for current architecture"); #endif #endif } void *thread_fn(void *p) { int delay = *(int *)p; sleep(delay); EXPECT_OK(getpid_()); EXPECT_CAPMODE(open("/dev/null", O_RDWR)); return NULL; } // Check that restrictions are the same in subprocesses and threads FORK_TEST(Capmode, NewThread) { // Fire off a new thread before entering capability mode pthread_t early_thread; int one = 1; // second EXPECT_OK(pthread_create(&early_thread, NULL, thread_fn, &one)); // Fire off a new process before entering capability mode. int early_child = fork(); EXPECT_OK(early_child); if (early_child == 0) { // Child: wait and then confirm this process is unaffect by capability mode in the parent. sleep(1); int fd = open("/dev/null", O_RDWR); EXPECT_OK(fd); close(fd); exit(0); } EXPECT_OK(cap_enter()); // Enter capability mode. // Do an allowed syscall. EXPECT_OK(getpid_()); int child = fork(); EXPECT_OK(child); if (child == 0) { // Child: do an allowed and a disallowed syscall. EXPECT_OK(getpid_()); EXPECT_CAPMODE(open("/dev/null", O_RDWR)); exit(0); } // Don't (can't) wait for either child. // Wait for the early-started thread. EXPECT_OK(pthread_join(early_thread, NULL)); // Fire off a new thread. pthread_t child_thread; int zero = 0; // seconds EXPECT_OK(pthread_create(&child_thread, NULL, thread_fn, &zero)); EXPECT_OK(pthread_join(child_thread, NULL)); // Fork a subprocess which fires off a new thread. child = fork(); EXPECT_OK(child); if (child == 0) { pthread_t child_thread2; EXPECT_OK(pthread_create(&child_thread2, NULL, thread_fn, &zero)); EXPECT_OK(pthread_join(child_thread2, NULL)); exit(0); } // Sleep for a bit to allow the subprocess to finish. sleep(2); } static int had_signal = 0; -static void handle_signal(int x) { had_signal = 1; } +static void handle_signal(int) { had_signal = 1; } FORK_TEST(Capmode, SelfKill) { pid_t me = getpid(); sighandler_t original = signal(SIGUSR1, handle_signal); pid_t child = fork(); if (child == 0) { // Child: sleep and exit sleep(1); exit(0); } EXPECT_OK(cap_enter()); // Enter capability mode. // Can only kill(2) to own pid. EXPECT_CAPMODE(kill(child, SIGUSR1)); EXPECT_OK(kill(me, SIGUSR1)); EXPECT_EQ(1, had_signal); signal(SIGUSR1, original); } Index: projects/capsicum-test/contrib/capsicum-test/procdesc.cc =================================================================== --- projects/capsicum-test/contrib/capsicum-test/procdesc.cc (revision 345216) +++ projects/capsicum-test/contrib/capsicum-test/procdesc.cc (revision 345217) @@ -1,977 +1,977 @@ // Tests for the process descriptor API for Linux. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "capsicum.h" #include "syscalls.h" #include "capsicum-test.h" #ifndef __WALL // Linux requires __WALL in order for waitpid(specific_pid,...) to // see and reap any specific pid. Define this to nothing for platforms // (FreeBSD) where it doesn't exist, to reduce macroing. #define __WALL 0 #endif // TODO(drysdale): it would be nice to use proper synchronization between // processes, rather than synchronization-via-sleep; faster too. //------------------------------------------------ // Utilities for the tests. static pid_t pdwait4_(int pd, int *status, int options, struct rusage *ru) { #ifdef HAVE_PDWAIT4 return pdwait4(pd, status, options, ru); #else // Simulate pdwait4() with wait4(pdgetpid()); this won't work in capability mode. pid_t pid = -1; int rc = pdgetpid(pd, &pid); if (rc < 0) { return rc; } options |= __WALL; return wait4(pid, status, options, ru); #endif } static void print_rusage(FILE *f, struct rusage *ru) { fprintf(f, " User CPU time=%ld.%06ld\n", (long)ru->ru_utime.tv_sec, (long)ru->ru_utime.tv_usec); fprintf(f, " System CPU time=%ld.%06ld\n", (long)ru->ru_stime.tv_sec, (long)ru->ru_stime.tv_usec); fprintf(f, " Max RSS=%ld\n", ru->ru_maxrss); } static void print_stat(FILE *f, const struct stat *stat) { fprintf(f, "{ .st_dev=%ld, st_ino=%ld, st_mode=%04o, st_nlink=%ld, st_uid=%d, st_gid=%d,\n" " .st_rdev=%ld, .st_size=%ld, st_blksize=%ld, .st_block=%ld,\n " #ifdef HAVE_STAT_BIRTHTIME ".st_birthtime=%ld, " #endif ".st_atime=%ld, .st_mtime=%ld, .st_ctime=%ld}\n", (long)stat->st_dev, (long)stat->st_ino, stat->st_mode, (long)stat->st_nlink, stat->st_uid, stat->st_gid, (long)stat->st_rdev, (long)stat->st_size, (long)stat->st_blksize, (long)stat->st_blocks, #ifdef HAVE_STAT_BIRTHTIME (long)stat->st_birthtime, #endif (long)stat->st_atime, (long)stat->st_mtime, (long)stat->st_ctime); } static std::map had_signal; static void handle_signal(int x) { had_signal[x] = true; } // Check that the given child process terminates as expected. void CheckChildFinished(pid_t pid, bool signaled=false) { // Wait for the child to finish. int rc; int status = 0; do { rc = waitpid(pid, &status, __WALL); if (rc < 0) { fprintf(stderr, "Warning: waitpid error %s (%d)\n", strerror(errno), errno); ADD_FAILURE() << "Failed to wait for child"; break; } else if (rc == pid) { break; } } while (true); EXPECT_EQ(pid, rc); if (rc == pid) { if (signaled) { EXPECT_TRUE(WIFSIGNALED(status)); } else { EXPECT_TRUE(WIFEXITED(status)) << std::hex << status; EXPECT_EQ(0, WEXITSTATUS(status)); } } } //------------------------------------------------ // Basic tests of process descriptor functionality TEST(Pdfork, Simple) { int pd = -1; pid_t parent = getpid_(); int pid = pdfork(&pd, 0); EXPECT_OK(pid); if (pid == 0) { // Child: check pid values. EXPECT_EQ(-1, pd); EXPECT_NE(parent, getpid_()); EXPECT_EQ(parent, getppid()); sleep(1); exit(0); } usleep(100); // ensure the child has a chance to run EXPECT_NE(-1, pd); EXPECT_PID_ALIVE(pid); int pid_got; EXPECT_OK(pdgetpid(pd, &pid_got)); EXPECT_EQ(pid, pid_got); // Wait long enough for the child to exit(). sleep(2); EXPECT_PID_ZOMBIE(pid); // Wait for the the child. int status; struct rusage ru; memset(&ru, 0, sizeof(ru)); int waitrc = pdwait4_(pd, &status, 0, &ru); EXPECT_EQ(pid, waitrc); if (verbose) { fprintf(stderr, "For pd %d pid %d:\n", pd, pid); print_rusage(stderr, &ru); } EXPECT_PID_GONE(pid); // Can only pdwait4(pd) once (as initial call reaps zombie). memset(&ru, 0, sizeof(ru)); EXPECT_EQ(-1, pdwait4_(pd, &status, 0, &ru)); EXPECT_EQ(ECHILD, errno); EXPECT_OK(close(pd)); } TEST(Pdfork, InvalidFlag) { int pd = -1; int pid = pdfork(&pd, PD_DAEMON<<5); if (pid == 0) { exit(1); } EXPECT_EQ(-1, pid); EXPECT_EQ(EINVAL, errno); if (pid > 0) waitpid(pid, NULL, __WALL); } TEST(Pdfork, TimeCheck) { time_t now = time(NULL); // seconds since epoch EXPECT_NE(-1, now); if (verbose) fprintf(stderr, "Calling pdfork around %ld\n", (long)(long)now); int pd = -1; pid_t pid = pdfork(&pd, 0); EXPECT_OK(pid); if (pid == 0) { // Child: check we didn't get a valid process descriptor then exit. EXPECT_EQ(-1, pdgetpid(pd, &pid)); EXPECT_EQ(EBADF, errno); exit(HasFailure()); } #ifdef HAVE_PROCDESC_FSTAT // Parent process. Ensure that [acm]times have been set correctly. struct stat stat; memset(&stat, 0, sizeof(stat)); EXPECT_OK(fstat(pd, &stat)); if (verbose) print_stat(stderr, &stat); #ifdef HAVE_STAT_BIRTHTIME EXPECT_GE(now, stat.st_birthtime); EXPECT_EQ(stat.st_birthtime, stat.st_atime); #endif EXPECT_LT((now - stat.st_atime), 2); EXPECT_EQ(stat.st_atime, stat.st_ctime); EXPECT_EQ(stat.st_ctime, stat.st_mtime); #endif // Wait for the child to finish. pid_t pd_pid = -1; EXPECT_OK(pdgetpid(pd, &pd_pid)); EXPECT_EQ(pid, pd_pid); CheckChildFinished(pid); } TEST(Pdfork, UseDescriptor) { int pd = -1; pid_t pid = pdfork(&pd, 0); EXPECT_OK(pid); if (pid == 0) { // Child: immediately exit exit(0); } CheckChildFinished(pid); } TEST(Pdfork, NonProcessDescriptor) { int fd = open("/etc/passwd", O_RDONLY); EXPECT_OK(fd); // pd*() operations should fail on a non-process descriptor. EXPECT_EQ(-1, pdkill(fd, SIGUSR1)); int status; EXPECT_EQ(-1, pdwait4_(fd, &status, 0, NULL)); pid_t pid; EXPECT_EQ(-1, pdgetpid(fd, &pid)); close(fd); } -static void *SubThreadMain(void *data) { +static void *SubThreadMain(void *) { while (true) { if (verbose) fprintf(stderr, " subthread: \"I aten't dead\"\n"); usleep(100000); } return NULL; } -static void *ThreadMain(void *data) { +static void *ThreadMain(void *) { int pd; pid_t child = pdfork(&pd, 0); if (child == 0) { // Child: start a subthread then loop pthread_t child_subthread; EXPECT_OK(pthread_create(&child_subthread, NULL, SubThreadMain, NULL)); while (true) { if (verbose) fprintf(stderr, " pdforked process %d: \"I aten't dead\"\n", getpid()); usleep(100000); } exit(0); } if (verbose) fprintf(stderr, " thread generated pd %d\n", pd); sleep(2); // Pass the process descriptor back to the main thread. return reinterpret_cast(pd); } TEST(Pdfork, FromThread) { // Fire off a new thread to do all of the creation work. pthread_t child_thread; EXPECT_OK(pthread_create(&child_thread, NULL, ThreadMain, NULL)); void *data; EXPECT_OK(pthread_join(child_thread, &data)); int pd = reinterpret_cast(data); if (verbose) fprintf(stderr, "retrieved pd %d from terminated thread\n", pd); // Kill and reap. pid_t pid; EXPECT_OK(pdgetpid(pd, &pid)); EXPECT_OK(pdkill(pd, SIGKILL)); int status; EXPECT_EQ(pid, pdwait4_(pd, &status, 0, NULL)); EXPECT_TRUE(WIFSIGNALED(status)); } //------------------------------------------------ // More complicated tests. // Test fixture that pdfork()s off a child process, which terminates // when it receives anything on a pipe. class PipePdforkBase : public ::testing::Test { public: PipePdforkBase(int pdfork_flags) : pd_(-1), pid_(-1) { had_signal.clear(); int pipes[2]; EXPECT_OK(pipe(pipes)); pipe_ = pipes[1]; int parent = getpid_(); if (verbose) fprintf(stderr, "[%d] about to pdfork()\n", getpid_()); int rc = pdfork(&pd_, pdfork_flags); EXPECT_OK(rc); if (rc == 0) { // Child process: blocking-read an int from the pipe then exit with that value. EXPECT_NE(parent, getpid_()); EXPECT_EQ(parent, getppid()); if (verbose) fprintf(stderr, " [%d] child of %d waiting for value on pipe\n", getpid_(), getppid()); read(pipes[0], &rc, sizeof(rc)); if (verbose) fprintf(stderr, " [%d] got value %d on pipe, exiting\n", getpid_(), rc); exit(rc); } pid_ = rc; usleep(100); // ensure the child has a chance to run } ~PipePdforkBase() { // Terminate by any means necessary. if (pd_ > 0) { pdkill(pd_, SIGKILL); close(pd_); } if (pid_ > 0) { kill(pid_, SIGKILL); waitpid(pid_, NULL, __WALL|WNOHANG); } // Check signal expectations. EXPECT_FALSE(had_signal[SIGCHLD]); } int TerminateChild() { // Tell the child to exit. int zero = 0; if (verbose) fprintf(stderr, "[%d] write 0 to pipe\n", getpid_()); return write(pipe_, &zero, sizeof(zero)); } protected: int pd_; int pipe_; pid_t pid_; }; class PipePdfork : public PipePdforkBase { public: PipePdfork() : PipePdforkBase(0) {} }; class PipePdforkDaemon : public PipePdforkBase { public: PipePdforkDaemon() : PipePdforkBase(PD_DAEMON) {} }; // Can we poll a process descriptor? TEST_F(PipePdfork, Poll) { // Poll the process descriptor, nothing happening. struct pollfd fdp; fdp.fd = pd_; fdp.events = POLLIN | POLLERR | POLLHUP; fdp.revents = 0; EXPECT_EQ(0, poll(&fdp, 1, 0)); TerminateChild(); // Poll again, should have activity on the process descriptor. EXPECT_EQ(1, poll(&fdp, 1, 2000)); EXPECT_TRUE(fdp.revents & POLLHUP); // Poll a third time, still have POLLHUP. fdp.revents = 0; EXPECT_EQ(1, poll(&fdp, 1, 0)); EXPECT_TRUE(fdp.revents & POLLHUP); } // Can multiple processes poll on the same descriptor? TEST_F(PipePdfork, PollMultiple) { int child = fork(); EXPECT_OK(child); if (child == 0) { // Child: wait to give time for setup, then write to the pipe (which will // induce exit of the pdfork()ed process) and exit. sleep(1); TerminateChild(); exit(0); } usleep(100); // ensure the child has a chance to run // Fork again int doppel = fork(); EXPECT_OK(doppel); // We now have: // pid A: main process, here // |--pid B: pdfork()ed process, blocked on read() // |--pid C: fork()ed process, in sleep(1) above // +--pid D: doppel process, here // Both A and D execute the following code. // First, check no activity on the process descriptor yet. struct pollfd fdp; fdp.fd = pd_; fdp.events = POLLIN | POLLERR | POLLHUP; fdp.revents = 0; EXPECT_EQ(0, poll(&fdp, 1, 0)); // Now, wait (indefinitely) for activity on the process descriptor. // We expect: // - pid C will finish its sleep, write to the pipe and exit // - pid B will unblock from read(), and exit // - this will generate an event on the process descriptor... // - ...in both process A and process D. EXPECT_EQ(1, poll(&fdp, 1, 2000)); EXPECT_TRUE(fdp.revents & POLLHUP); if (doppel == 0) { // Child: process D exits. exit(0); } else { // Parent: wait on process D. int rc = 0; waitpid(doppel, &rc, __WALL); EXPECT_TRUE(WIFEXITED(rc)); EXPECT_EQ(0, WEXITSTATUS(rc)); // Also wait on process B. CheckChildFinished(child); } } // Check that exit status/rusage for a dead pdfork()ed child can be retrieved // via any process descriptor, multiple times. TEST_F(PipePdfork, MultipleRetrieveExitStatus) { EXPECT_PID_ALIVE(pid_); int pd_copy = dup(pd_); EXPECT_LT(0, TerminateChild()); int status; struct rusage ru; memset(&ru, 0, sizeof(ru)); int waitrc = pdwait4_(pd_copy, &status, 0, &ru); EXPECT_EQ(pid_, waitrc); if (verbose) { fprintf(stderr, "For pd %d -> pid %d:\n", pd_, pid_); print_rusage(stderr, &ru); } EXPECT_PID_GONE(pid_); #ifdef NOTYET // Child has been reaped, so original process descriptor dangles but // still has access to rusage information. memset(&ru, 0, sizeof(ru)); EXPECT_EQ(0, pdwait4_(pd_, &status, 0, &ru)); #endif close(pd_copy); } TEST_F(PipePdfork, ChildExit) { EXPECT_PID_ALIVE(pid_); EXPECT_LT(0, TerminateChild()); EXPECT_PID_DEAD(pid_); int status; int rc = pdwait4_(pd_, &status, 0, NULL); EXPECT_OK(rc); EXPECT_EQ(pid_, rc); pid_ = 0; } #ifdef HAVE_PROC_FDINFO TEST_F(PipePdfork, FdInfo) { char buffer[1024]; sprintf(buffer, "/proc/%d/fdinfo/%d", getpid_(), pd_); int procfd = open(buffer, O_RDONLY); EXPECT_OK(procfd); EXPECT_OK(read(procfd, buffer, sizeof(buffer))); // The fdinfo should include the file pos of the underlying file EXPECT_NE((char*)NULL, strstr(buffer, "pos:\t0")) << buffer; // ...and the underlying pid char pidline[256]; sprintf(pidline, "pid:\t%d", pid_); EXPECT_NE((char*)NULL, strstr(buffer, pidline)) << buffer; close(procfd); } #endif // Closing a normal process descriptor terminates the underlying process. TEST_F(PipePdfork, Close) { sighandler_t original = signal(SIGCHLD, handle_signal); EXPECT_PID_ALIVE(pid_); int status; EXPECT_EQ(0, waitpid(pid_, &status, __WALL|WNOHANG)); EXPECT_OK(close(pd_)); pd_ = -1; EXPECT_FALSE(had_signal[SIGCHLD]); EXPECT_PID_DEAD(pid_); #ifdef __FreeBSD__ EXPECT_EQ(-1, waitpid(pid_, NULL, __WALL)); EXPECT_EQ(errno, ECHILD); #else // Having closed the process descriptor means that pdwait4(pd) now doesn't work. int rc = pdwait4_(pd_, &status, 0, NULL); EXPECT_EQ(-1, rc); EXPECT_EQ(EBADF, errno); // Closing all process descriptors means the the child can only be reaped via pid. EXPECT_EQ(pid_, waitpid(pid_, &status, __WALL|WNOHANG)); #endif signal(SIGCHLD, original); } TEST_F(PipePdfork, CloseLast) { sighandler_t original = signal(SIGCHLD, handle_signal); // Child should only die when last process descriptor is closed. EXPECT_PID_ALIVE(pid_); int pd_other = dup(pd_); EXPECT_OK(close(pd_)); pd_ = -1; EXPECT_PID_ALIVE(pid_); int status; EXPECT_EQ(0, waitpid(pid_, &status, __WALL|WNOHANG)); // Can no longer pdwait4() the closed process descriptor... EXPECT_EQ(-1, pdwait4_(pd_, &status, WNOHANG, NULL)); EXPECT_EQ(EBADF, errno); // ...but can pdwait4() the still-open process descriptor. errno = 0; EXPECT_EQ(0, pdwait4_(pd_other, &status, WNOHANG, NULL)); EXPECT_EQ(0, errno); EXPECT_OK(close(pd_other)); EXPECT_PID_DEAD(pid_); EXPECT_FALSE(had_signal[SIGCHLD]); signal(SIGCHLD, original); } FORK_TEST(Pdfork, OtherUser) { REQUIRE_ROOT(); int pd; pid_t pid = pdfork(&pd, 0); EXPECT_OK(pid); if (pid == 0) { // Child process: loop forever. while (true) usleep(100000); } usleep(100); // Now that the second process has been pdfork()ed, change euid. setuid(other_uid); if (verbose) fprintf(stderr, "uid=%d euid=%d\n", getuid(), geteuid()); // Fail to kill child with normal PID operation. EXPECT_EQ(-1, kill(pid, SIGKILL)); EXPECT_EQ(EPERM, errno); EXPECT_PID_ALIVE(pid); // Succeed with pdkill though. EXPECT_OK(pdkill(pd, SIGKILL)); EXPECT_PID_ZOMBIE(pid); int status; int rc = pdwait4_(pd, &status, WNOHANG, NULL); EXPECT_OK(rc); EXPECT_EQ(pid, rc); EXPECT_TRUE(WIFSIGNALED(status)); } TEST_F(PipePdfork, WaitPidThenPd) { TerminateChild(); int status; // If we waitpid(pid) first... int rc = waitpid(pid_, &status, __WALL); EXPECT_OK(rc); EXPECT_EQ(pid_, rc); #ifdef NOTYET // ...the zombie is reaped but we can still subsequently pdwait4(pd). EXPECT_EQ(0, pdwait4_(pd_, &status, 0, NULL)); #endif } TEST_F(PipePdfork, WaitPdThenPid) { TerminateChild(); int status; // If we pdwait4(pd) first... int rc = pdwait4_(pd_, &status, 0, NULL); EXPECT_OK(rc); EXPECT_EQ(pid_, rc); // ...the zombie is reaped and cannot subsequently waitpid(pid). EXPECT_EQ(-1, waitpid(pid_, &status, __WALL)); EXPECT_EQ(ECHILD, errno); } // Setting PD_DAEMON prevents close() from killing the child. TEST_F(PipePdforkDaemon, Close) { EXPECT_OK(close(pd_)); pd_ = -1; EXPECT_PID_ALIVE(pid_); // Can still explicitly kill it via the pid. if (pid_ > 0) { EXPECT_OK(kill(pid_, SIGKILL)); EXPECT_PID_DEAD(pid_); } } static void TestPdkill(pid_t pid, int pd) { EXPECT_PID_ALIVE(pid); // SIGCONT is ignored by default. EXPECT_OK(pdkill(pd, SIGCONT)); EXPECT_PID_ALIVE(pid); // SIGINT isn't EXPECT_OK(pdkill(pd, SIGINT)); EXPECT_PID_DEAD(pid); // pdkill() on zombie is no-op. errno = 0; EXPECT_EQ(0, pdkill(pd, SIGINT)); EXPECT_EQ(0, errno); // pdkill() on reaped process gives -ESRCH. CheckChildFinished(pid, true); EXPECT_EQ(-1, pdkill(pd, SIGINT)); EXPECT_EQ(ESRCH, errno); } TEST_F(PipePdfork, Pdkill) { TestPdkill(pid_, pd_); } TEST_F(PipePdforkDaemon, Pdkill) { TestPdkill(pid_, pd_); } TEST(Pdfork, PdkillOtherSignal) { int pd = -1; int pid = pdfork(&pd, 0); EXPECT_OK(pid); if (pid == 0) { // Child: watch for SIGUSR1 forever. had_signal.clear(); signal(SIGUSR1, handle_signal); while (!had_signal[SIGUSR1]) { usleep(100000); } exit(123); } sleep(1); // Send an invalid signal. EXPECT_EQ(-1, pdkill(pd, 0xFFFF)); EXPECT_EQ(EINVAL, errno); // Send an expected SIGUSR1 to the pdfork()ed child. EXPECT_PID_ALIVE(pid); pdkill(pd, SIGUSR1); EXPECT_PID_DEAD(pid); // Child's exit status confirms whether it received the signal. int status; int rc = waitpid(pid, &status, __WALL); EXPECT_OK(rc); EXPECT_EQ(pid, rc); EXPECT_TRUE(WIFEXITED(status)) << "0x" << std::hex << rc; EXPECT_EQ(123, WEXITSTATUS(status)); } pid_t PdforkParentDeath(int pdfork_flags) { // Set up: // pid A: main process, here // +--pid B: fork()ed process, sleep(4)s then exits // +--pid C: pdfork()ed process, looping forever int sock_fds[2]; EXPECT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds)); if (verbose) fprintf(stderr, "[%d] parent about to fork()...\n", getpid_()); pid_t child = fork(); EXPECT_OK(child); if (child == 0) { int pd; if (verbose) fprintf(stderr, " [%d] child about to pdfork()...\n", getpid_()); pid_t grandchild = pdfork(&pd, pdfork_flags); if (grandchild == 0) { while (true) { if (verbose) fprintf(stderr, " [%d] grandchild: \"I aten't dead\"\n", getpid_()); sleep(1); } } if (verbose) fprintf(stderr, " [%d] pdfork()ed grandchild %d, sending ID to parent\n", getpid_(), grandchild); // send grandchild pid to parent write(sock_fds[1], &grandchild, sizeof(grandchild)); sleep(4); if (verbose) fprintf(stderr, " [%d] child terminating\n", getpid_()); exit(0); } if (verbose) fprintf(stderr, "[%d] fork()ed child is %d\n", getpid_(), child); pid_t grandchild; read(sock_fds[0], &grandchild, sizeof(grandchild)); if (verbose) fprintf(stderr, "[%d] receive grandchild id %d\n", getpid_(), grandchild); EXPECT_PID_ALIVE(child); EXPECT_PID_ALIVE(grandchild); sleep(6); // Child dies, closing its process descriptor for the grandchild. EXPECT_PID_DEAD(child); CheckChildFinished(child); return grandchild; } TEST(Pdfork, Bagpuss) { // "And of course when Bagpuss goes to sleep, all his friends go to sleep too" pid_t grandchild = PdforkParentDeath(0); // By default: child death => closed process descriptor => grandchild death. EXPECT_PID_DEAD(grandchild); } TEST(Pdfork, BagpussDaemon) { pid_t grandchild = PdforkParentDeath(PD_DAEMON); // With PD_DAEMON: child death => closed process descriptor => no effect on grandchild. EXPECT_PID_ALIVE(grandchild); if (grandchild > 0) { EXPECT_OK(kill(grandchild, SIGKILL)); } } // The exit of a pdfork()ed process should not generate SIGCHLD. TEST_F(PipePdfork, NoSigchld) { had_signal.clear(); sighandler_t original = signal(SIGCHLD, handle_signal); TerminateChild(); int rc = 0; // Can waitpid() for the specific pid of the pdfork()ed child. EXPECT_EQ(pid_, waitpid(pid_, &rc, __WALL)); EXPECT_TRUE(WIFEXITED(rc)) << "0x" << std::hex << rc; EXPECT_FALSE(had_signal[SIGCHLD]); signal(SIGCHLD, original); } // The exit of a pdfork()ed process whose process descriptors have // all been closed should generate SIGCHLD. The child process needs // PD_DAEMON to survive the closure of the process descriptors. TEST_F(PipePdforkDaemon, NoPDSigchld) { had_signal.clear(); sighandler_t original = signal(SIGCHLD, handle_signal); EXPECT_OK(close(pd_)); TerminateChild(); #ifdef __FreeBSD__ EXPECT_EQ(-1, waitpid(pid_, NULL, __WALL)); EXPECT_EQ(errno, ECHILD); #else int rc = 0; // Can waitpid() for the specific pid of the pdfork()ed child. EXPECT_EQ(pid_, waitpid(pid_, &rc, __WALL)); EXPECT_TRUE(WIFEXITED(rc)) << "0x" << std::hex << rc; #endif EXPECT_FALSE(had_signal[SIGCHLD]); signal(SIGCHLD, original); } #ifdef HAVE_PROCDESC_FSTAT TEST_F(PipePdfork, ModeBits) { // Owner rwx bits indicate liveness of child struct stat stat; memset(&stat, 0, sizeof(stat)); EXPECT_OK(fstat(pd_, &stat)); if (verbose) print_stat(stderr, &stat); EXPECT_EQ(S_IRWXU, (long)(stat.st_mode & S_IRWXU)); TerminateChild(); usleep(100000); memset(&stat, 0, sizeof(stat)); EXPECT_OK(fstat(pd_, &stat)); if (verbose) print_stat(stderr, &stat); EXPECT_EQ(0, (int)(stat.st_mode & S_IRWXU)); } #endif TEST_F(PipePdfork, WildcardWait) { // TODO(FreeBSD): make wildcard wait ignore pdfork()ed children // https://bugs.freebsd.org/201054 TerminateChild(); sleep(1); // Ensure child is truly dead. // Wildcard waitpid(-1) should not see the pdfork()ed child because // there is still a process descriptor for it. int rc; EXPECT_EQ(-1, waitpid(-1, &rc, WNOHANG)); EXPECT_EQ(ECHILD, errno); EXPECT_OK(close(pd_)); pd_ = -1; } FORK_TEST(Pdfork, Pdkill) { had_signal.clear(); int pd; pid_t pid = pdfork(&pd, 0); EXPECT_OK(pid); if (pid == 0) { // Child: set a SIGINT handler and sleep. had_signal.clear(); signal(SIGINT, handle_signal); if (verbose) fprintf(stderr, "[%d] child about to sleep(10)\n", getpid_()); int left = sleep(10); if (verbose) fprintf(stderr, "[%d] child slept, %d sec left, had[SIGINT]=%d\n", getpid_(), left, had_signal[SIGINT]); // Expect this sleep to be interrupted by the signal (and so left > 0). exit(left == 0); } // Parent: get child's PID. pid_t pd_pid; EXPECT_OK(pdgetpid(pd, &pd_pid)); EXPECT_EQ(pid, pd_pid); // Interrupt the child after a second. sleep(1); EXPECT_OK(pdkill(pd, SIGINT)); // Make sure the child finished properly (caught signal then exited). CheckChildFinished(pid); } FORK_TEST(Pdfork, PdkillSignal) { int pd; pid_t pid = pdfork(&pd, 0); EXPECT_OK(pid); if (pid == 0) { // Child: sleep. No SIGINT handler. if (verbose) fprintf(stderr, "[%d] child about to sleep(10)\n", getpid_()); int left = sleep(10); if (verbose) fprintf(stderr, "[%d] child slept, %d sec left\n", getpid_(), left); exit(99); } // Kill the child (as it doesn't handle SIGINT). sleep(1); EXPECT_OK(pdkill(pd, SIGINT)); // Make sure the child finished properly (terminated by signal). CheckChildFinished(pid, true); } //------------------------------------------------ // Test interactions with other parts of Capsicum: // - capability mode // - capabilities FORK_TEST(Pdfork, DaemonUnrestricted) { EXPECT_OK(cap_enter()); int fd; // Capability mode leaves pdfork() available, with and without flag. int rc; rc = pdfork(&fd, PD_DAEMON); EXPECT_OK(rc); if (rc == 0) { // Child: immediately terminate. exit(0); } rc = pdfork(&fd, 0); EXPECT_OK(rc); if (rc == 0) { // Child: immediately terminate. exit(0); } } TEST(Pdfork, MissingRights) { pid_t parent = getpid_(); int pd = -1; pid_t pid = pdfork(&pd, 0); EXPECT_OK(pid); if (pid == 0) { // Child: loop forever. EXPECT_NE(parent, getpid_()); while (true) sleep(1); } // Create two capabilities from the process descriptor. cap_rights_t r_ro; cap_rights_init(&r_ro, CAP_READ, CAP_LOOKUP); int cap_incapable = dup(pd); EXPECT_OK(cap_incapable); EXPECT_OK(cap_rights_limit(cap_incapable, &r_ro)); cap_rights_t r_pdall; cap_rights_init(&r_pdall, CAP_PDGETPID, CAP_PDWAIT, CAP_PDKILL); int cap_capable = dup(pd); EXPECT_OK(cap_capable); EXPECT_OK(cap_rights_limit(cap_capable, &r_pdall)); pid_t other_pid; EXPECT_NOTCAPABLE(pdgetpid(cap_incapable, &other_pid)); EXPECT_NOTCAPABLE(pdkill(cap_incapable, SIGINT)); int status; EXPECT_NOTCAPABLE(pdwait4_(cap_incapable, &status, 0, NULL)); EXPECT_OK(pdgetpid(cap_capable, &other_pid)); EXPECT_EQ(pid, other_pid); EXPECT_OK(pdkill(cap_capable, SIGINT)); int rc = pdwait4_(pd, &status, 0, NULL); EXPECT_OK(rc); EXPECT_EQ(pid, rc); } //------------------------------------------------ // Passing process descriptors between processes. TEST_F(PipePdfork, PassProcessDescriptor) { int sock_fds[2]; EXPECT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds)); struct msghdr mh; mh.msg_name = NULL; // No address needed mh.msg_namelen = 0; char buffer1[1024]; struct iovec iov[1]; iov[0].iov_base = buffer1; iov[0].iov_len = sizeof(buffer1); mh.msg_iov = iov; mh.msg_iovlen = 1; char buffer2[1024]; mh.msg_control = buffer2; mh.msg_controllen = sizeof(buffer2); struct cmsghdr *cmptr; if (verbose) fprintf(stderr, "[%d] about to fork()\n", getpid_()); pid_t child2 = fork(); if (child2 == 0) { // Child: close our copy of the original process descriptor. close(pd_); // Child: wait to receive process descriptor over socket if (verbose) fprintf(stderr, " [%d] child of %d waiting for process descriptor on socket\n", getpid_(), getppid()); int rc = recvmsg(sock_fds[0], &mh, 0); EXPECT_OK(rc); EXPECT_LE(CMSG_LEN(sizeof(int)), mh.msg_controllen); cmptr = CMSG_FIRSTHDR(&mh); int pd = *(int*)CMSG_DATA(cmptr); EXPECT_EQ(CMSG_LEN(sizeof(int)), cmptr->cmsg_len); cmptr = CMSG_NXTHDR(&mh, cmptr); EXPECT_TRUE(cmptr == NULL); if (verbose) fprintf(stderr, " [%d] got process descriptor %d on socket\n", getpid_(), pd); // Child: confirm we can do pd*() operations on the process descriptor pid_t other; EXPECT_OK(pdgetpid(pd, &other)); if (verbose) fprintf(stderr, " [%d] process descriptor %d is pid %d\n", getpid_(), pd, other); sleep(2); if (verbose) fprintf(stderr, " [%d] close process descriptor %d\n", getpid_(), pd); close(pd); // Last process descriptor closed, expect death EXPECT_PID_DEAD(other); exit(HasFailure()); } usleep(1000); // Ensure subprocess runs // Send the process descriptor over the pipe to the sub-process mh.msg_controllen = CMSG_LEN(sizeof(int)); cmptr = CMSG_FIRSTHDR(&mh); cmptr->cmsg_level = SOL_SOCKET; cmptr->cmsg_type = SCM_RIGHTS; cmptr->cmsg_len = CMSG_LEN(sizeof(int)); *(int *)CMSG_DATA(cmptr) = pd_; buffer1[0] = 0; iov[0].iov_len = 1; sleep(1); if (verbose) fprintf(stderr, "[%d] send process descriptor %d on socket\n", getpid_(), pd_); int rc = sendmsg(sock_fds[1], &mh, 0); EXPECT_OK(rc); if (verbose) fprintf(stderr, "[%d] close process descriptor %d\n", getpid_(), pd_); close(pd_); // Not last open process descriptor // wait for child2 int status; EXPECT_EQ(child2, waitpid(child2, &status, __WALL)); rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1; EXPECT_EQ(0, rc); // confirm death all round EXPECT_PID_DEAD(child2); EXPECT_PID_DEAD(pid_); }