#include <sys/types.h>
#include <sys/resource.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <iomanip>
#include <map>
#include "capsicum.h"
#include "syscalls.h"
#include "capsicum-test.h"
#ifndef __WALL
#define __WALL 0
#endif
static pid_t pdwait4_(int pd, int *status, int options, struct rusage *ru) {
#ifdef HAVE_PDWAIT4
return pdwait4(pd, status, options, ru);
#else
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 volatile sig_atomic_t had_signal[NSIG];
void clear_had_signals() {
memset(const_cast<sig_atomic_t *>(had_signal), 0, sizeof(had_signal));
}
static void handle_signal(int x) {
had_signal[x] = true;
}
void CheckChildFinished(pid_t pid, bool signaled=false) {
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));
}
}
}
TEST(Pdfork, Simple) {
int pd = -1;
int pipefds[2];
pid_t parent = getpid_();
EXPECT_OK(pipe(pipefds));
int pid = pdfork(&pd, 0);
EXPECT_OK(pid);
if (pid == 0) {
EXPECT_EQ(-1, pd);
EXPECT_NE(parent, getpid_());
EXPECT_EQ(parent, getppid());
close(pipefds[0]);
SEND_INT_MESSAGE(pipefds[1], MSG_CHILD_STARTED);
if (verbose) fprintf(stderr, "Child waiting for exit message\n");
AWAIT_INT_MESSAGE(pipefds[1], MSG_PARENT_REQUEST_CHILD_EXIT);
exit(testing::Test::HasFailure());
}
close(pipefds[1]);
AWAIT_INT_MESSAGE(pipefds[0], MSG_CHILD_STARTED);
EXPECT_NE(-1, pd);
EXPECT_PID_ALIVE(pid);
int pid_got;
EXPECT_OK(pdgetpid(pd, &pid_got));
EXPECT_EQ(pid, pid_got);
SEND_INT_MESSAGE(pipefds[0], MSG_PARENT_REQUEST_CHILD_EXIT);
EXPECT_PID_ZOMBIE(pid);
close(pipefds[0]);
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);
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);
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) {
EXPECT_EQ(-1, pdgetpid(pd, &pid));
EXPECT_EQ(EBADF, errno);
exit(HasFailure());
}
#ifdef HAVE_PROCDESC_FSTAT
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
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) {
exit(0);
}
CheckChildFinished(pid);
}
TEST(Pdfork, NonProcessDescriptor) {
int fd = open("/etc/passwd", O_RDONLY);
EXPECT_OK(fd);
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 *arg) {
if (verbose) fprintf(stderr, " subthread started: pipe=%p\n", arg);
SEND_INT_MESSAGE((int)(intptr_t)arg, MSG_CHILD_STARTED);
while (true) {
if (verbose) fprintf(stderr, " subthread: \"I aten't dead\"\n");
usleep(100000);
}
return NULL;
}
static void *ThreadMain(void *) {
int pd;
int pipefds[2];
EXPECT_EQ(0, pipe(pipefds));
pid_t child = pdfork(&pd, 0);
if (child == 0) {
close(pipefds[0]);
pthread_t child_subthread;
int thread_pipefds[2];
EXPECT_EQ(0, pipe(thread_pipefds));
EXPECT_OK(pthread_create(&child_subthread, NULL, SubThreadMain,
(void *)(intptr_t)thread_pipefds[0]));
if (verbose) {
fprintf(stderr, " pdforked process %d: waiting for subthread.\n",
getpid());
}
AWAIT_INT_MESSAGE(thread_pipefds[1], MSG_CHILD_STARTED);
close(thread_pipefds[0]);
close(thread_pipefds[1]);
if (verbose) fprintf(stderr, " pdforked process %d: subthread started\n", getpid());
SEND_INT_MESSAGE(pipefds[1], MSG_CHILD_STARTED);
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);
close(pipefds[1]);
AWAIT_INT_MESSAGE(pipefds[0], MSG_CHILD_STARTED);
if (verbose) fprintf(stderr, "[%d] got child startup message\n", getpid_());
return reinterpret_cast<void *>(pd);
}
TEST(Pdfork, FromThread) {
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<intptr_t>(data);
if (verbose) fprintf(stderr, "retrieved pd %d from terminated thread\n", pd);
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));
}
class PipePdforkBase : public ::testing::Test {
public:
PipePdforkBase(int pdfork_flags) : pd_(-1), pid_(-1) {
clear_had_signals();
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) {
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);
}
~PipePdforkBase() {
if (pd_ > 0) {
pdkill(pd_, SIGKILL);
close(pd_);
}
if (pid_ > 0) {
kill(pid_, SIGKILL);
waitpid(pid_, NULL, __WALL|WNOHANG);
}
EXPECT_FALSE(had_signal[SIGCHLD]);
}
int TerminateChild() {
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) {}
};
TEST_F(PipePdfork, Poll) {
struct pollfd fdp;
fdp.fd = pd_;
fdp.events = POLLIN | POLLERR | POLLHUP;
fdp.revents = 0;
EXPECT_EQ(0, poll(&fdp, 1, 0));
TerminateChild();
EXPECT_EQ(1, poll(&fdp, 1, 2000));
EXPECT_TRUE(fdp.revents & POLLHUP);
fdp.revents = 0;
EXPECT_EQ(1, poll(&fdp, 1, 0));
EXPECT_TRUE(fdp.revents & POLLHUP);
}
TEST_F(PipePdfork, PollMultiple) {
int pipefds[2];
EXPECT_EQ(0, pipe(pipefds));
int child = fork();
EXPECT_OK(child);
if (child == 0) {
close(pipefds[0]);
SEND_INT_MESSAGE(pipefds[1], MSG_CHILD_STARTED);
if (verbose) fprintf(stderr, "[%d] waiting for read 1\n", getpid_());
AWAIT_INT_MESSAGE(pipefds[1], MSG_PARENT_REQUEST_CHILD_EXIT);
if (verbose) fprintf(stderr, "[%d] waiting for read 2\n", getpid_());
AWAIT_INT_MESSAGE(pipefds[1], MSG_PARENT_REQUEST_CHILD_EXIT);
TerminateChild();
if (verbose) fprintf(stderr, "[%d] about to exit\n", getpid_());
exit(testing::Test::HasFailure());
}
close(pipefds[1]);
AWAIT_INT_MESSAGE(pipefds[0], MSG_CHILD_STARTED);
if (verbose) fprintf(stderr, "[%d] got child startup message\n", getpid_());
int doppel = fork();
EXPECT_OK(doppel);
struct pollfd fdp;
fdp.fd = pd_;
fdp.events = POLLIN | POLLERR | POLLHUP;
fdp.revents = 0;
EXPECT_EQ(0, poll(&fdp, 1, 0));
if (verbose) fprintf(stderr, "[%d] telling child to exit\n", getpid_());
SEND_INT_MESSAGE(pipefds[0], MSG_PARENT_REQUEST_CHILD_EXIT);
close(pipefds[0]);
if (verbose) fprintf(stderr, "[%d] waiting for child to exit\n", getpid_());
EXPECT_EQ(1, poll(&fdp, 1, 2000));
EXPECT_TRUE(fdp.revents & POLLHUP);
if (doppel == 0) {
exit(0);
} else {
int rc = 0;
waitpid(doppel, &rc, __WALL);
EXPECT_TRUE(WIFEXITED(rc));
EXPECT_EQ(0, WEXITSTATUS(rc));
CheckChildFinished(child);
}
}
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
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)));
EXPECT_NE((char*)NULL, strstr(buffer, "pos:\t0")) << buffer;
char pidline[256];
sprintf(pidline, "pid:\t%d", pid_);
EXPECT_NE((char*)NULL, strstr(buffer, pidline)) << buffer;
close(procfd);
}
#endif
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
int rc = pdwait4_(pd_, &status, 0, NULL);
EXPECT_EQ(-1, rc);
EXPECT_EQ(EBADF, errno);
EXPECT_EQ(pid_, waitpid(pid_, &status, __WALL|WNOHANG));
#endif
signal(SIGCHLD, original);
}
TEST_F(PipePdfork, CloseLast) {
sighandler_t original = signal(SIGCHLD, handle_signal);
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));
EXPECT_EQ(-1, pdwait4_(pd_, &status, WNOHANG, NULL));
EXPECT_EQ(EBADF, errno);
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, OtherUserIfRoot) {
GTEST_SKIP_IF_NOT_ROOT();
int pd;
int status;
pid_t pid = pdfork(&pd, 0);
EXPECT_OK(pid);
if (pid == 0) {
while (true) usleep(100000);
}
usleep(100);
ASSERT_NE(0u, other_uid) << "other_uid not initialized correctly, "
"please pass the -u <uid> flag.";
EXPECT_EQ(0, setuid(other_uid));
EXPECT_EQ(other_uid, getuid());
if (verbose) fprintf(stderr, "uid=%d euid=%d\n", getuid(), geteuid());
EXPECT_EQ(-1, kill(pid, SIGKILL));
EXPECT_EQ(EPERM, errno);
EXPECT_PID_ALIVE(pid);
#ifdef __FreeBSD__
EXPECT_SYSCALL_FAIL(EBADF, pdkill(pid, SIGKILL));
EXPECT_PID_ALIVE(pid);
EXPECT_OK(close(pd));
EXPECT_PID_GONE(pid);
EXPECT_SYSCALL_FAIL(EBADF, pdwait4_(pd, &status, WNOHANG, NULL));
#else
EXPECT_OK(pdkill(pd, SIGKILL));
EXPECT_PID_ZOMBIE(pid);
int rc = pdwait4_(pd, &status, WNOHANG, NULL);
EXPECT_OK(rc);
EXPECT_EQ(pid, rc);
EXPECT_TRUE(WIFSIGNALED(status));
#endif
}
TEST_F(PipePdfork, WaitPidThenPd) {
TerminateChild();
int status;
int rc = waitpid(pid_, &status, __WALL);
EXPECT_OK(rc);
EXPECT_EQ(pid_, rc);
#ifdef NOTYET
EXPECT_EQ(0, pdwait4_(pd_, &status, 0, NULL));
#endif
}
TEST_F(PipePdfork, WaitPdThenPid) {
TerminateChild();
int status;
int rc = pdwait4_(pd_, &status, 0, NULL);
EXPECT_OK(rc);
EXPECT_EQ(pid_, rc);
EXPECT_EQ(-1, waitpid(pid_, &status, __WALL));
EXPECT_EQ(ECHILD, errno);
}
TEST_F(PipePdforkDaemon, Close) {
EXPECT_OK(close(pd_));
pd_ = -1;
EXPECT_PID_ALIVE(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);
EXPECT_OK(pdkill(pd, SIGCONT));
EXPECT_PID_ALIVE(pid);
EXPECT_OK(pdkill(pd, SIGINT));
EXPECT_PID_DEAD(pid);
errno = 0;
EXPECT_EQ(0, pdkill(pd, SIGINT));
EXPECT_EQ(0, errno);
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 pipefds[2];
EXPECT_EQ(0, pipe(pipefds));
int pid = pdfork(&pd, 0);
EXPECT_OK(pid);
if (pid == 0) {
close(pipefds[0]);
clear_had_signals();
signal(SIGUSR1, handle_signal);
SEND_INT_MESSAGE(pipefds[1], MSG_CHILD_STARTED);
while (!had_signal[SIGUSR1]) {
usleep(100000);
}
exit(123);
}
close(pipefds[1]);
AWAIT_INT_MESSAGE(pipefds[0], MSG_CHILD_STARTED);
close(pipefds[0]);
EXPECT_EQ(-1, pdkill(pd, 0xFFFF));
EXPECT_EQ(EINVAL, errno);
EXPECT_PID_ALIVE(pid);
pdkill(pd, SIGUSR1);
EXPECT_PID_DEAD(pid);
int status;
int rc = waitpid(pid, &status, __WALL);
EXPECT_OK(rc);
EXPECT_EQ(pid, rc);
EXPECT_TRUE(WIFEXITED(status)) << "status: 0x" << std::hex << status;
EXPECT_EQ(123, WEXITSTATUS(status));
}
pid_t PdforkParentDeath(int pdfork_flags) {
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_());
int pipefds[2];
EXPECT_OK(pipe(pipefds));
pid_t grandchild = pdfork(&pd, pdfork_flags);
if (grandchild == 0) {
close(pipefds[0]);
pid_t grandchildPid = getpid_();
EXPECT_EQ(sizeof(grandchildPid), (size_t)write(pipefds[1], &grandchildPid, sizeof(grandchildPid)));
while (true) {
if (verbose) fprintf(stderr, " [%d] grandchild: \"I aten't dead\"\n", grandchildPid);
sleep(1);
}
}
close(pipefds[1]);
if (verbose) fprintf(stderr, " [%d] pdfork()ed grandchild %d, sending ID to parent\n", getpid_(), grandchild);
pid_t grandchild2;
EXPECT_EQ(sizeof(grandchild2), (size_t)read(pipefds[0], &grandchild2, sizeof(grandchild2)));
EXPECT_EQ(grandchild, grandchild2) << "received invalid grandchild pid";
if (verbose) fprintf(stderr, " [%d] grandchild %d has started successfully\n", getpid_(), grandchild);
close(pipefds[0]);
EXPECT_EQ(sizeof(grandchild), (size_t)write(sock_fds[1], &grandchild, sizeof(grandchild)));
if (verbose) fprintf(stderr, " [%d] sent grandchild pid %d to parent\n", getpid_(), grandchild);
AWAIT_INT_MESSAGE(sock_fds[1], MSG_PARENT_REQUEST_CHILD_EXIT);
if (verbose) fprintf(stderr, " [%d] parent acknowledged grandchild pid %d\n", getpid_(), grandchild);
if (verbose) fprintf(stderr, " [%d] child terminating\n", getpid_());
exit(testing::Test::HasFailure());
}
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] received grandchild id %d\n", getpid_(), grandchild);
EXPECT_PID_ALIVE(child);
EXPECT_PID_ALIVE(grandchild);
if (verbose) fprintf(stderr, "[%d] telling child %d to exit\n", getpid_(), child);
SEND_INT_MESSAGE(sock_fds[0], MSG_PARENT_REQUEST_CHILD_EXIT);
EXPECT_PID_DEAD(child);
CheckChildFinished(child);
return grandchild;
}
TEST(Pdfork, Bagpuss) {
pid_t grandchild = PdforkParentDeath(0);
EXPECT_PID_DEAD(grandchild);
}
TEST(Pdfork, BagpussDaemon) {
pid_t grandchild = PdforkParentDeath(PD_DAEMON);
EXPECT_PID_ALIVE(grandchild);
if (grandchild > 0) {
EXPECT_OK(kill(grandchild, SIGKILL));
}
}
TEST_F(PipePdfork, NoSigchld) {
clear_had_signals();
sighandler_t original = signal(SIGCHLD, handle_signal);
TerminateChild();
int rc = 0;
EXPECT_EQ(pid_, waitpid(pid_, &rc, __WALL));
EXPECT_TRUE(WIFEXITED(rc)) << "0x" << std::hex << rc;
EXPECT_FALSE(had_signal[SIGCHLD]);
signal(SIGCHLD, original);
}
TEST_F(PipePdforkDaemon, NoPDSigchld) {
clear_had_signals();
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;
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) {
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) {
TerminateChild();
EXPECT_PID_ZOMBIE(pid_);
int rc;
EXPECT_EQ(-1, waitpid(-1, &rc, WNOHANG));
EXPECT_EQ(ECHILD, errno);
EXPECT_OK(close(pd_));
pd_ = -1;
}
FORK_TEST(Pdfork, Pdkill) {
clear_had_signals();
int pd;
int pipefds[2];
EXPECT_OK(pipe(pipefds));
pid_t pid = pdfork(&pd, 0);
EXPECT_OK(pid);
if (pid == 0) {
close(pipefds[0]);
clear_had_signals();
signal(SIGINT, handle_signal);
if (verbose) fprintf(stderr, "[%d] child started\n", getpid_());
SEND_INT_MESSAGE(pipefds[1], MSG_CHILD_STARTED);
if (verbose) fprintf(stderr, "[%d] child about to sleep(10)\n", getpid_());
for (int i = 0; i < 50 && !had_signal[SIGINT]; i++) {
usleep(100000);
}
if (verbose) fprintf(stderr, "[%d] child slept, had[SIGINT]=%d\n",
getpid_(), (int)had_signal[SIGINT]);
exit(had_signal[SIGINT] ? 0 : 99);
}
pid_t pd_pid;
EXPECT_OK(pdgetpid(pd, &pd_pid));
EXPECT_EQ(pid, pd_pid);
close(pipefds[1]);
if (verbose) fprintf(stderr, "[%d] waiting for child\n", getpid_());
AWAIT_INT_MESSAGE(pipefds[0], MSG_CHILD_STARTED);
EXPECT_OK(pdkill(pd, SIGINT));
if (verbose) fprintf(stderr, "[%d] sent SIGINT\n", getpid_());
CheckChildFinished(pid);
}
FORK_TEST(Pdfork, PdkillSignal) {
int pd;
int pipefds[2];
EXPECT_OK(pipe(pipefds));
pid_t pid = pdfork(&pd, 0);
EXPECT_OK(pid);
if (pid == 0) {
close(pipefds[0]);
if (verbose) fprintf(stderr, "[%d] child started\n", getpid_());
SEND_INT_MESSAGE(pipefds[1], MSG_CHILD_STARTED);
if (verbose) fprintf(stderr, "[%d] child about to read()\n", getpid_());
AWAIT_INT_MESSAGE(pipefds[1], MSG_PARENT_REQUEST_CHILD_EXIT);
fprintf(stderr, "[%d] child read() returned unexpectedly\n", getpid_());
exit(99);
}
if (verbose) fprintf(stderr, "[%d] waiting for child\n", getpid_());
close(pipefds[1]);
AWAIT_INT_MESSAGE(pipefds[0], MSG_CHILD_STARTED);
if (verbose) fprintf(stderr, "[%d] sending SIGINT\n", getpid_());
EXPECT_OK(pdkill(pd, SIGINT));
CheckChildFinished(pid, true);
}
FORK_TEST(Pdfork, DaemonUnrestricted) {
EXPECT_OK(cap_enter());
int fd;
int rc;
rc = pdfork(&fd, PD_DAEMON);
EXPECT_OK(rc);
if (rc == 0) {
exit(0);
}
rc = pdfork(&fd, 0);
EXPECT_OK(rc);
if (rc == 0) {
exit(0);
}
}
TEST(Pdfork, MissingRights) {
pid_t parent = getpid_();
int pd = -1;
pid_t pid = pdfork(&pd, 0);
EXPECT_OK(pid);
if (pid == 0) {
EXPECT_NE(parent, getpid_());
while (true) sleep(1);
}
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);
}
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;
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) {
close(pd_);
SEND_INT_MESSAGE(sock_fds[0], MSG_CHILD_STARTED);
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);
SEND_INT_MESSAGE(sock_fds[0], MSG_CHILD_FD_RECEIVED);
pid_t other;
EXPECT_OK(pdgetpid(pd, &other));
if (verbose) fprintf(stderr, " [%d] process descriptor %d is pid %d\n", getpid_(), pd, other);
AWAIT_INT_MESSAGE(sock_fds[0], MSG_PARENT_CLOSED_FD);
if (verbose) fprintf(stderr, " [%d] close process descriptor %d\n", getpid_(), pd);
close(pd);
EXPECT_PID_DEAD(other);
exit(HasFailure());
}
AWAIT_INT_MESSAGE(sock_fds[1], MSG_CHILD_STARTED);
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;
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);
AWAIT_INT_MESSAGE(sock_fds[1], MSG_CHILD_FD_RECEIVED);
if (verbose) fprintf(stderr, "[%d] close process descriptor %d\n", getpid_(), pd_);
close(pd_);
SEND_INT_MESSAGE(sock_fds[1], MSG_PARENT_CLOSED_FD);
int status;
EXPECT_EQ(child2, waitpid(child2, &status, __WALL));
rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
EXPECT_EQ(0, rc);
EXPECT_PID_DEAD(child2);
EXPECT_PID_DEAD(pid_);
}