Update capsicum-test to git commit f4d97414d48b8f8356b971ab9f45dc5c70d53c40
This commit is contained in:
parent
b6973c8f4a
commit
c59f30a57b
@ -4,6 +4,10 @@ OS:=$(shell uname)
|
||||
ARCH?=64
|
||||
ARCHFLAG=-m$(ARCH)
|
||||
|
||||
ifeq ($(OS),FreeBSD)
|
||||
EXTRA_LIBS=-lprocstat
|
||||
endif
|
||||
|
||||
ifeq ($(OS),Linux)
|
||||
PROCESSOR:=$(shell uname -p)
|
||||
|
||||
|
@ -486,6 +486,7 @@ FORK_TEST_ON(Capability, Mmap, TmpFile("cap_mmap_operations")) {
|
||||
// Given a file descriptor, create a capability with specific rights and
|
||||
// make sure only those rights work.
|
||||
#define TRY_FILE_OPS(fd, ...) do { \
|
||||
SCOPED_TRACE(#__VA_ARGS__); \
|
||||
cap_rights_t rights; \
|
||||
cap_rights_init(&rights, __VA_ARGS__); \
|
||||
TryFileOps((fd), rights); \
|
||||
@ -1019,6 +1020,8 @@ FORK_TEST_ON(Capability, SocketTransfer, TmpFile("cap_fd_transfer")) {
|
||||
if (child == 0) {
|
||||
// Child: enter cap mode
|
||||
EXPECT_OK(cap_enter());
|
||||
// Child: send startup notification
|
||||
SEND_INT_MESSAGE(sock_fds[0], MSG_CHILD_STARTED);
|
||||
|
||||
// Child: wait to receive FD over socket
|
||||
int rc = recvmsg(sock_fds[0], &mh, 0);
|
||||
@ -1036,10 +1039,12 @@ FORK_TEST_ON(Capability, SocketTransfer, TmpFile("cap_fd_transfer")) {
|
||||
EXPECT_RIGHTS_EQ(&r_rs, &rights);
|
||||
TryReadWrite(cap_fd);
|
||||
|
||||
// Child: acknowledge that we have received and tested the file descriptor
|
||||
SEND_INT_MESSAGE(sock_fds[0], MSG_CHILD_FD_RECEIVED);
|
||||
|
||||
// Child: wait for a normal read
|
||||
int val;
|
||||
read(sock_fds[0], &val, sizeof(val));
|
||||
exit(0);
|
||||
AWAIT_INT_MESSAGE(sock_fds[0], MSG_PARENT_REQUEST_CHILD_EXIT);
|
||||
exit(testing::Test::HasFailure());
|
||||
}
|
||||
|
||||
int fd = open(TmpFile("cap_fd_transfer"), O_RDWR | O_CREAT, 0644);
|
||||
@ -1054,6 +1059,9 @@ FORK_TEST_ON(Capability, SocketTransfer, TmpFile("cap_fd_transfer")) {
|
||||
// Confirm we can do the right operations on the capability
|
||||
TryReadWrite(cap_fd);
|
||||
|
||||
// Wait for child to start up:
|
||||
AWAIT_INT_MESSAGE(sock_fds[1], MSG_CHILD_STARTED);
|
||||
|
||||
// Send the file descriptor over the pipe to the sub-process
|
||||
mh.msg_controllen = CMSG_LEN(sizeof(int));
|
||||
cmptr = CMSG_FIRSTHDR(&mh);
|
||||
@ -1063,13 +1071,14 @@ FORK_TEST_ON(Capability, SocketTransfer, TmpFile("cap_fd_transfer")) {
|
||||
*(int *)CMSG_DATA(cmptr) = cap_fd;
|
||||
buffer1[0] = 0;
|
||||
iov[0].iov_len = 1;
|
||||
sleep(3);
|
||||
int rc = sendmsg(sock_fds[1], &mh, 0);
|
||||
EXPECT_OK(rc);
|
||||
|
||||
sleep(1); // Ensure subprocess runs
|
||||
int zero = 0;
|
||||
write(sock_fds[1], &zero, sizeof(zero));
|
||||
// Check that the child received the message
|
||||
AWAIT_INT_MESSAGE(sock_fds[1], MSG_CHILD_FD_RECEIVED);
|
||||
|
||||
// Tell the child to exit
|
||||
SEND_INT_MESSAGE(sock_fds[1], MSG_PARENT_REQUEST_CHILD_EXIT);
|
||||
}
|
||||
|
||||
TEST(Capability, SyscallAt) {
|
||||
|
126
capmode.cc
126
capmode.cc
@ -528,6 +528,8 @@ TEST(Capmode, Abort) {
|
||||
FORK_TEST_F(WithFiles, AllowedMiscSyscalls) {
|
||||
umask(022);
|
||||
mode_t um_before = umask(022);
|
||||
int pipefds[2];
|
||||
EXPECT_OK(pipe(pipefds));
|
||||
EXPECT_OK(cap_enter()); // Enter capability mode.
|
||||
|
||||
mode_t um = umask(022);
|
||||
@ -540,13 +542,19 @@ FORK_TEST_F(WithFiles, AllowedMiscSyscalls) {
|
||||
pid_t pid = fork();
|
||||
EXPECT_OK(pid);
|
||||
if (pid == 0) {
|
||||
// Child: almost immediately exit.
|
||||
sleep(1);
|
||||
// Child: wait for an exit message from parent (so we can test waitpid).
|
||||
EXPECT_OK(close(pipefds[0]));
|
||||
SEND_INT_MESSAGE(pipefds[1], MSG_CHILD_STARTED);
|
||||
AWAIT_INT_MESSAGE(pipefds[1], MSG_PARENT_REQUEST_CHILD_EXIT);
|
||||
exit(0);
|
||||
} else if (pid > 0) {
|
||||
EXPECT_OK(close(pipefds[1]));
|
||||
AWAIT_INT_MESSAGE(pipefds[0], MSG_CHILD_STARTED);
|
||||
errno = 0;
|
||||
EXPECT_CAPMODE(ptrace_(PTRACE_PEEKDATA_, pid, &pid, NULL));
|
||||
EXPECT_CAPMODE(waitpid(pid, NULL, 0));
|
||||
EXPECT_CAPMODE(waitpid(pid, NULL, WNOHANG));
|
||||
SEND_INT_MESSAGE(pipefds[0], MSG_PARENT_REQUEST_CHILD_EXIT);
|
||||
if (verbose) fprintf(stderr, " child finished\n");
|
||||
}
|
||||
|
||||
// No error return from sync(2) to test, but check errno remains unset.
|
||||
@ -568,68 +576,136 @@ FORK_TEST_F(WithFiles, AllowedMiscSyscalls) {
|
||||
}
|
||||
|
||||
void *thread_fn(void *p) {
|
||||
int delay = *(int *)p;
|
||||
sleep(delay);
|
||||
int fd = (int)(intptr_t)p;
|
||||
if (verbose) fprintf(stderr, " thread waiting to run\n");
|
||||
AWAIT_INT_MESSAGE(fd, MSG_PARENT_CHILD_SHOULD_RUN);
|
||||
EXPECT_OK(getpid_());
|
||||
EXPECT_CAPMODE(open("/dev/null", O_RDWR));
|
||||
return NULL;
|
||||
// Return whether there have been any failures to the main thread.
|
||||
void *rval = (void *)(intptr_t)testing::Test::HasFailure();
|
||||
if (verbose) fprintf(stderr, " thread finished: %p\n", rval);
|
||||
return rval;
|
||||
}
|
||||
|
||||
// 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));
|
||||
void *thread_rval;
|
||||
// Create two pipes, one for synchronization with the threads, the other to
|
||||
// synchronize with the children (since we can't use waitpid after cap_enter).
|
||||
// Note: Could use pdfork+pdwait instead, but that is tested in procdesc.cc.
|
||||
int thread_pipe[2];
|
||||
EXPECT_OK(pipe(thread_pipe));
|
||||
int proc_pipe[2];
|
||||
EXPECT_OK(pipe(proc_pipe));
|
||||
EXPECT_OK(pthread_create(&early_thread, NULL, thread_fn,
|
||||
(void *)(intptr_t)thread_pipe[1]));
|
||||
|
||||
// Fire off a new process before entering capability mode.
|
||||
if (verbose) fprintf(stderr, " starting second child (non-capability mode)\n");
|
||||
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);
|
||||
if (verbose) fprintf(stderr, " first child started\n");
|
||||
EXPECT_OK(close(proc_pipe[0]));
|
||||
// Child: wait and then confirm this process is unaffected by capability mode in the parent.
|
||||
AWAIT_INT_MESSAGE(proc_pipe[1], MSG_PARENT_CHILD_SHOULD_RUN);
|
||||
int fd = open("/dev/null", O_RDWR);
|
||||
EXPECT_OK(fd);
|
||||
close(fd);
|
||||
exit(0);
|
||||
// Notify the parent of success/failure.
|
||||
int rval = (int)testing::Test::HasFailure();
|
||||
SEND_INT_MESSAGE(proc_pipe[1], rval);
|
||||
if (verbose) fprintf(stderr, " first child finished: %d\n", rval);
|
||||
exit(rval);
|
||||
}
|
||||
|
||||
EXPECT_OK(cap_enter()); // Enter capability mode.
|
||||
// At this point the current process has both a child process and a
|
||||
// child thread that were created before entering capability mode.
|
||||
// - The child process is unaffected by capability mode.
|
||||
// - The child thread is affected by capability mode.
|
||||
SEND_INT_MESSAGE(proc_pipe[0], MSG_PARENT_CHILD_SHOULD_RUN);
|
||||
|
||||
// Do an allowed syscall.
|
||||
EXPECT_OK(getpid_());
|
||||
// Wait for the first child to exit (should get a zero exit code message).
|
||||
AWAIT_INT_MESSAGE(proc_pipe[0], 0);
|
||||
|
||||
// The child processes/threads return HasFailure(), so we depend on no prior errors.
|
||||
ASSERT_FALSE(testing::Test::HasFailure())
|
||||
<< "Cannot continue test with pre-existing failures.";
|
||||
// Now that we're in capability mode, if we create a second child process
|
||||
// it will be affected by capability mode.
|
||||
if (verbose) fprintf(stderr, " starting second child (in capability mode)\n");
|
||||
int child = fork();
|
||||
EXPECT_OK(child);
|
||||
if (child == 0) {
|
||||
if (verbose) fprintf(stderr, " second child started\n");
|
||||
EXPECT_OK(close(proc_pipe[0]));
|
||||
// Child: do an allowed and a disallowed syscall.
|
||||
EXPECT_OK(getpid_());
|
||||
EXPECT_CAPMODE(open("/dev/null", O_RDWR));
|
||||
exit(0);
|
||||
// Notify the parent of success/failure.
|
||||
int rval = (int)testing::Test::HasFailure();
|
||||
SEND_INT_MESSAGE(proc_pipe[1], rval);
|
||||
if (verbose) fprintf(stderr, " second child finished: %d\n", rval);
|
||||
exit(rval);
|
||||
}
|
||||
// Don't (can't) wait for either child.
|
||||
|
||||
// Now tell the early_started thread that it can run. We expect it to also
|
||||
// be affected by capability mode since it's per-process not per-thread.
|
||||
// Note: it is important that we don't allow the thread to run before fork(),
|
||||
// since that could result in fork() being called while the thread holds one
|
||||
// of the gtest-internal mutexes, so the child process deadlocks.
|
||||
SEND_INT_MESSAGE(thread_pipe[0], MSG_PARENT_CHILD_SHOULD_RUN);
|
||||
// Wait for the early-started thread.
|
||||
EXPECT_OK(pthread_join(early_thread, NULL));
|
||||
EXPECT_OK(pthread_join(early_thread, &thread_rval));
|
||||
EXPECT_FALSE((bool)(intptr_t)thread_rval) << "thread returned failure";
|
||||
|
||||
// Fire off a new thread.
|
||||
// Wait for the second child to exit (should get a zero exit code message).
|
||||
AWAIT_INT_MESSAGE(proc_pipe[0], 0);
|
||||
|
||||
// Fire off a new (second) child thread, which is also affected by capability mode.
|
||||
ASSERT_FALSE(testing::Test::HasFailure())
|
||||
<< "Cannot continue test with pre-existing failures.";
|
||||
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));
|
||||
EXPECT_OK(pthread_create(&child_thread, NULL, thread_fn,
|
||||
(void *)(intptr_t)thread_pipe[1]));
|
||||
SEND_INT_MESSAGE(thread_pipe[0], MSG_PARENT_CHILD_SHOULD_RUN);
|
||||
EXPECT_OK(pthread_join(child_thread, &thread_rval));
|
||||
EXPECT_FALSE((bool)(intptr_t)thread_rval) << "thread returned failure";
|
||||
|
||||
// Fork a subprocess which fires off a new thread.
|
||||
ASSERT_FALSE(testing::Test::HasFailure())
|
||||
<< "Cannot continue test with pre-existing failures.";
|
||||
if (verbose) fprintf(stderr, " starting third child (in capability mode)\n");
|
||||
child = fork();
|
||||
EXPECT_OK(child);
|
||||
if (child == 0) {
|
||||
if (verbose) fprintf(stderr, " third child started\n");
|
||||
EXPECT_OK(close(proc_pipe[0]));
|
||||
pthread_t child_thread2;
|
||||
EXPECT_OK(pthread_create(&child_thread2, NULL, thread_fn, &zero));
|
||||
EXPECT_OK(pthread_join(child_thread2, NULL));
|
||||
exit(0);
|
||||
EXPECT_OK(pthread_create(&child_thread2, NULL, thread_fn,
|
||||
(void *)(intptr_t)thread_pipe[1]));
|
||||
SEND_INT_MESSAGE(thread_pipe[0], MSG_PARENT_CHILD_SHOULD_RUN);
|
||||
EXPECT_OK(pthread_join(child_thread2, &thread_rval));
|
||||
EXPECT_FALSE((bool)(intptr_t)thread_rval) << "thread returned failure";
|
||||
// Notify the parent of success/failure.
|
||||
int rval = (int)testing::Test::HasFailure();
|
||||
SEND_INT_MESSAGE(proc_pipe[1], rval);
|
||||
if (verbose) fprintf(stderr, " third child finished: %d\n", rval);
|
||||
exit(rval);
|
||||
}
|
||||
// Sleep for a bit to allow the subprocess to finish.
|
||||
sleep(2);
|
||||
// Wait for the third child to exit (should get a zero exit code message).
|
||||
AWAIT_INT_MESSAGE(proc_pipe[0], 0);
|
||||
close(proc_pipe[0]);
|
||||
close(proc_pipe[1]);
|
||||
close(thread_pipe[0]);
|
||||
close(thread_pipe[1]);
|
||||
}
|
||||
|
||||
static int had_signal = 0;
|
||||
static volatile sig_atomic_t had_signal = 0;
|
||||
static void handle_signal(int) { had_signal = 1; }
|
||||
|
||||
FORK_TEST(Capmode, SelfKill) {
|
||||
|
@ -1,5 +1,15 @@
|
||||
#include "capsicum-test.h"
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/param.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
#include <libprocstat.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
@ -48,31 +58,59 @@ char ProcessState(int pid) {
|
||||
return '?';
|
||||
#endif
|
||||
#ifdef __FreeBSD__
|
||||
char buffer[1024];
|
||||
snprintf(buffer, sizeof(buffer), "ps -p %d -o state | grep -v STAT", pid);
|
||||
sig_t original = signal(SIGCHLD, SIG_IGN);
|
||||
FILE* cmd = popen(buffer, "r");
|
||||
usleep(50000); // allow any pending SIGCHLD signals to arrive
|
||||
signal(SIGCHLD, original);
|
||||
int result = fgetc(cmd);
|
||||
fclose(cmd);
|
||||
// Map FreeBSD codes to Linux codes.
|
||||
switch (result) {
|
||||
case EOF:
|
||||
return '\0';
|
||||
case 'D': // disk wait
|
||||
case 'R': // runnable
|
||||
case 'S': // sleeping
|
||||
case 'T': // stopped
|
||||
case 'Z': // zombie
|
||||
return result;
|
||||
case 'W': // idle interrupt thread
|
||||
return 'S';
|
||||
case 'I': // idle
|
||||
return 'S';
|
||||
case 'L': // waiting to acquire lock
|
||||
default:
|
||||
return '?';
|
||||
// First check if the process exists/we have permission to see it. This
|
||||
// Avoids warning messages being printed to stderr by libprocstat.
|
||||
size_t len = 0;
|
||||
int name[4];
|
||||
name[0] = CTL_KERN;
|
||||
name[1] = KERN_PROC;
|
||||
name[2] = KERN_PROC_PID;
|
||||
name[3] = pid;
|
||||
if (sysctl(name, nitems(name), NULL, &len, NULL, 0) < 0 && errno == ESRCH) {
|
||||
if (verbose) fprintf(stderr, "Process %d does not exist\n", pid);
|
||||
return '\0'; // No such process.
|
||||
}
|
||||
unsigned int count = 0;
|
||||
struct procstat *prstat = procstat_open_sysctl();
|
||||
EXPECT_NE(NULL, prstat) << "procstat_open_sysctl failed.";
|
||||
errno = 0;
|
||||
struct kinfo_proc *p = procstat_getprocs(prstat, KERN_PROC_PID, pid, &count);
|
||||
if (p == NULL || count == 0) {
|
||||
if (verbose) fprintf(stderr, "procstat_getprocs failed with %p/%d: %s\n", p, count, strerror(errno));
|
||||
procstat_close(prstat);
|
||||
return '\0';
|
||||
}
|
||||
char result = '\0';
|
||||
// See state() in bin/ps/print.c
|
||||
switch (p->ki_stat) {
|
||||
case SSTOP:
|
||||
result = 'T';
|
||||
break;
|
||||
case SSLEEP:
|
||||
if (p->ki_tdflags & TDF_SINTR) /* interruptable (long) */
|
||||
result = 'S';
|
||||
else
|
||||
result = 'D';
|
||||
break;
|
||||
case SRUN:
|
||||
case SIDL:
|
||||
result = 'R';
|
||||
break;
|
||||
case SWAIT:
|
||||
case SLOCK:
|
||||
// We treat SWAIT/SLOCK as 'S' here (instead of 'W'/'L').
|
||||
result = 'S';
|
||||
break;
|
||||
case SZOMB:
|
||||
result = 'Z';
|
||||
break;
|
||||
default:
|
||||
result = '?';
|
||||
break;
|
||||
}
|
||||
procstat_freeprocs(prstat, p);
|
||||
procstat_close(prstat);
|
||||
if (verbose) fprintf(stderr, "Process %d in state '%c'\n", pid, result);
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
@ -140,15 +140,17 @@ const char *TmpFile(const char *pathname);
|
||||
// Expect a syscall to fail with the given error.
|
||||
#define EXPECT_SYSCALL_FAIL(E, C) \
|
||||
do { \
|
||||
SCOPED_TRACE(#C); \
|
||||
EXPECT_GT(0, C); \
|
||||
EXPECT_EQ(E, errno); \
|
||||
EXPECT_EQ(E, errno) << "expected '" << strerror(E) \
|
||||
<< "' but got '" << strerror(errno) << "'"; \
|
||||
} while (0)
|
||||
|
||||
// Expect a syscall to fail with anything other than the given error.
|
||||
#define EXPECT_SYSCALL_FAIL_NOT(E, C) \
|
||||
do { \
|
||||
EXPECT_GT(0, C); \
|
||||
EXPECT_NE(E, errno); \
|
||||
EXPECT_NE(E, errno) << strerror(E); \
|
||||
} while (0)
|
||||
|
||||
// Expect a void syscall to fail with anything other than the given error.
|
||||
@ -163,7 +165,8 @@ const char *TmpFile(const char *pathname);
|
||||
// code is OS-specific.
|
||||
#ifdef O_BENEATH
|
||||
#define EXPECT_OPENAT_FAIL_TRAVERSAL(fd, path, flags) \
|
||||
do { \
|
||||
do { \
|
||||
SCOPED_TRACE(GTEST_STRINGIFY_(openat((fd), (path), (flags)))); \
|
||||
const int result = openat((fd), (path), (flags)); \
|
||||
if (((flags) & O_BENEATH) == O_BENEATH) { \
|
||||
EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_O_BENEATH, result); \
|
||||
@ -175,6 +178,7 @@ const char *TmpFile(const char *pathname);
|
||||
#else
|
||||
#define EXPECT_OPENAT_FAIL_TRAVERSAL(fd, path, flags) \
|
||||
do { \
|
||||
SCOPED_TRACE(GTEST_STRINGIFY_(openat((fd), (path), (flags)))); \
|
||||
const int result = openat((fd), (path), (flags)); \
|
||||
EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, result); \
|
||||
if (result >= 0) { close(result); } \
|
||||
@ -200,7 +204,8 @@ const char *TmpFile(const char *pathname);
|
||||
int rc = C; \
|
||||
EXPECT_GT(0, rc); \
|
||||
EXPECT_TRUE(errno == ECAPMODE || errno == ENOTCAPABLE) \
|
||||
<< #C << " did not fail with ECAPMODE/ENOTCAPABLE but " << errno; \
|
||||
<< #C << " did not fail with ECAPMODE/ENOTCAPABLE but " << errno \
|
||||
<< "(" << strerror(errno) << ")"; \
|
||||
} while (0)
|
||||
|
||||
// Ensure that 'rights' are a subset of 'max'.
|
||||
@ -244,6 +249,29 @@ char ProcessState(int pid);
|
||||
#define EXPECT_PID_ZOMBIE(pid) EXPECT_PID_REACHES_STATES(pid, 'Z', 'Z');
|
||||
#define EXPECT_PID_GONE(pid) EXPECT_PID_REACHES_STATES(pid, '\0', '\0');
|
||||
|
||||
enum {
|
||||
// Magic numbers for messages sent by child processes.
|
||||
MSG_CHILD_STARTED = 1234,
|
||||
MSG_CHILD_FD_RECEIVED = 4321,
|
||||
// Magic numbers for messages sent by parent processes.
|
||||
MSG_PARENT_REQUEST_CHILD_EXIT = 9999,
|
||||
MSG_PARENT_CLOSED_FD = 10000,
|
||||
MSG_PARENT_CHILD_SHOULD_RUN = 10001,
|
||||
};
|
||||
|
||||
#define SEND_INT_MESSAGE(fd, message) \
|
||||
do { \
|
||||
int _msg = message; \
|
||||
EXPECT_EQ(sizeof(_msg), (size_t)write(fd, &_msg, sizeof(_msg))); \
|
||||
} while (0)
|
||||
|
||||
#define AWAIT_INT_MESSAGE(fd, expected) \
|
||||
do { \
|
||||
int _msg = 0; \
|
||||
EXPECT_EQ(sizeof(_msg), (size_t)read(fd, &_msg, sizeof(_msg))); \
|
||||
EXPECT_EQ(expected, _msg); \
|
||||
} while (0)
|
||||
|
||||
// Mark a test that can only be run as root.
|
||||
#define GTEST_SKIP_IF_NOT_ROOT() \
|
||||
if (getuid() != 0) { GTEST_SKIP() << "requires root"; }
|
||||
|
2
makefile
2
makefile
@ -8,7 +8,7 @@ CXXFLAGS+=$(ARCHFLAG) -Wall -g $(GTEST_INCS) $(GTEST_FLAGS) --std=c++11
|
||||
CFLAGS+=$(ARCHFLAG) -Wall -g
|
||||
|
||||
capsicum-test: $(OBJECTS) libgtest.a $(LOCAL_LIBS)
|
||||
$(CXX) $(CXXFLAGS) -g -o $@ $(OBJECTS) libgtest.a -lpthread -lrt $(LIBSCTP) $(LIBCAPRIGHTS)
|
||||
$(CXX) $(CXXFLAGS) -g -o $@ $(OBJECTS) libgtest.a -lpthread -lrt $(LIBSCTP) $(LIBCAPRIGHTS) $(EXTRA_LIBS)
|
||||
|
||||
# Small statically-linked program for fexecve tests
|
||||
# (needs to be statically linked so that execve()ing it
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
// Check an open call works and close the resulting fd.
|
||||
#define EXPECT_OPEN_OK(f) do { \
|
||||
SCOPED_TRACE(#f); \
|
||||
int _fd = f; \
|
||||
EXPECT_OK(_fd); \
|
||||
close(_fd); \
|
||||
|
241
procdesc.cc
241
procdesc.cc
@ -27,10 +27,6 @@
|
||||
#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.
|
||||
|
||||
@ -73,7 +69,10 @@ static void print_stat(FILE *f, const struct stat *stat) {
|
||||
(long)stat->st_atime, (long)stat->st_mtime, (long)stat->st_ctime);
|
||||
}
|
||||
|
||||
static std::map<int,bool> had_signal;
|
||||
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;
|
||||
}
|
||||
@ -109,7 +108,9 @@ void CheckChildFinished(pid_t pid, bool signaled=false) {
|
||||
|
||||
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) {
|
||||
@ -117,19 +118,29 @@ TEST(Pdfork, Simple) {
|
||||
EXPECT_EQ(-1, pd);
|
||||
EXPECT_NE(parent, getpid_());
|
||||
EXPECT_EQ(parent, getppid());
|
||||
sleep(1);
|
||||
exit(0);
|
||||
close(pipefds[0]);
|
||||
SEND_INT_MESSAGE(pipefds[1], MSG_CHILD_STARTED);
|
||||
if (verbose) fprintf(stderr, "Child waiting for exit message\n");
|
||||
// Terminate once the parent has completed the checks
|
||||
AWAIT_INT_MESSAGE(pipefds[1], MSG_PARENT_REQUEST_CHILD_EXIT);
|
||||
exit(testing::Test::HasFailure());
|
||||
}
|
||||
usleep(100); // ensure the child has a chance to run
|
||||
close(pipefds[1]);
|
||||
// Ensure the child has started.
|
||||
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);
|
||||
|
||||
// Wait long enough for the child to exit().
|
||||
sleep(2);
|
||||
// Tell the child to exit and wait until it is a zombie.
|
||||
SEND_INT_MESSAGE(pipefds[0], MSG_PARENT_REQUEST_CHILD_EXIT);
|
||||
// EXPECT_PID_ZOMBIE waits for up to ~500ms, that should be enough time for
|
||||
// the child to exit successfully.
|
||||
EXPECT_PID_ZOMBIE(pid);
|
||||
close(pipefds[0]);
|
||||
|
||||
// Wait for the the child.
|
||||
int status;
|
||||
@ -223,7 +234,10 @@ TEST(Pdfork, NonProcessDescriptor) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void *SubThreadMain(void *) {
|
||||
static void *SubThreadMain(void *arg) {
|
||||
// Notify the main thread that we have started
|
||||
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);
|
||||
@ -233,11 +247,28 @@ static void *SubThreadMain(void *) {
|
||||
|
||||
static void *ThreadMain(void *) {
|
||||
int pd;
|
||||
int pipefds[2];
|
||||
EXPECT_EQ(0, pipe(pipefds));
|
||||
pid_t child = pdfork(&pd, 0);
|
||||
if (child == 0) {
|
||||
// Child: start a subthread then loop
|
||||
close(pipefds[0]);
|
||||
// Child: start a subthread then loop.
|
||||
pthread_t child_subthread;
|
||||
EXPECT_OK(pthread_create(&child_subthread, NULL, SubThreadMain, NULL));
|
||||
// Wait for the subthread startup using another pipe.
|
||||
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]);
|
||||
// Child: Notify parent that all threads have started
|
||||
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);
|
||||
@ -245,7 +276,9 @@ static void *ThreadMain(void *) {
|
||||
exit(0);
|
||||
}
|
||||
if (verbose) fprintf(stderr, " thread generated pd %d\n", pd);
|
||||
sleep(2);
|
||||
close(pipefds[1]);
|
||||
AWAIT_INT_MESSAGE(pipefds[0], MSG_CHILD_STARTED);
|
||||
if (verbose) fprintf(stderr, "[%d] got child startup message\n", getpid_());
|
||||
|
||||
// Pass the process descriptor back to the main thread.
|
||||
return reinterpret_cast<void *>(pd);
|
||||
@ -278,7 +311,7 @@ TEST(Pdfork, FromThread) {
|
||||
class PipePdforkBase : public ::testing::Test {
|
||||
public:
|
||||
PipePdforkBase(int pdfork_flags) : pd_(-1), pid_(-1) {
|
||||
had_signal.clear();
|
||||
clear_had_signals();
|
||||
int pipes[2];
|
||||
EXPECT_OK(pipe(pipes));
|
||||
pipe_ = pipes[1];
|
||||
@ -356,24 +389,34 @@ TEST_F(PipePdfork, Poll) {
|
||||
|
||||
// Can multiple processes poll on the same descriptor?
|
||||
TEST_F(PipePdfork, PollMultiple) {
|
||||
int pipefds[2];
|
||||
EXPECT_EQ(0, pipe(pipefds));
|
||||
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);
|
||||
close(pipefds[0]);
|
||||
// Child: wait for parent to acknowledge startup
|
||||
SEND_INT_MESSAGE(pipefds[1], MSG_CHILD_STARTED);
|
||||
// Child: wait for two messages from the parent and the forked process
|
||||
// before telling the other process to terminate.
|
||||
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();
|
||||
exit(0);
|
||||
if (verbose) fprintf(stderr, "[%d] about to exit\n", getpid_());
|
||||
exit(testing::Test::HasFailure());
|
||||
}
|
||||
usleep(100); // ensure the child has a chance to run
|
||||
|
||||
close(pipefds[1]);
|
||||
AWAIT_INT_MESSAGE(pipefds[0], MSG_CHILD_STARTED);
|
||||
if (verbose) fprintf(stderr, "[%d] got child startup message\n", getpid_());
|
||||
// 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 C: fork()ed process, in read() above
|
||||
// +--pid D: doppel process, here
|
||||
|
||||
// Both A and D execute the following code.
|
||||
@ -384,12 +427,18 @@ TEST_F(PipePdfork, PollMultiple) {
|
||||
fdp.revents = 0;
|
||||
EXPECT_EQ(0, poll(&fdp, 1, 0));
|
||||
|
||||
// Both A and D ask C to exit, allowing it to do so.
|
||||
if (verbose) fprintf(stderr, "[%d] telling child to exit\n", getpid_());
|
||||
SEND_INT_MESSAGE(pipefds[0], MSG_PARENT_REQUEST_CHILD_EXIT);
|
||||
close(pipefds[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 C will finish its two read() calls, 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.
|
||||
if (verbose) fprintf(stderr, "[%d] waiting for child to exit\n", getpid_());
|
||||
EXPECT_EQ(1, poll(&fdp, 1, 2000));
|
||||
EXPECT_TRUE(fdp.revents & POLLHUP);
|
||||
|
||||
@ -522,6 +571,7 @@ TEST_F(PipePdfork, CloseLast) {
|
||||
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) {
|
||||
@ -542,15 +592,29 @@ FORK_TEST(Pdfork, OtherUserIfRoot) {
|
||||
EXPECT_EQ(EPERM, errno);
|
||||
EXPECT_PID_ALIVE(pid);
|
||||
|
||||
// Succeed with pdkill though.
|
||||
// Ideally, we should be able to send signals via a process descriptor even
|
||||
// if it's owned by another user, but this is not implementated on FreeBSD.
|
||||
#ifdef __FreeBSD__
|
||||
// On FreeBSD, pdkill() still performs all the same checks that kill() does
|
||||
// and therefore cannot be used to send a signal to a process with another
|
||||
// UID unless we are root.
|
||||
EXPECT_SYSCALL_FAIL(EBADF, pdkill(pid, SIGKILL));
|
||||
EXPECT_PID_ALIVE(pid);
|
||||
// However, the process will be killed when we close the process descriptor.
|
||||
EXPECT_OK(close(pd));
|
||||
EXPECT_PID_GONE(pid);
|
||||
// Can't pdwait4() after close() since close() reparents the child to a reaper (init)
|
||||
EXPECT_SYSCALL_FAIL(EBADF, pdwait4_(pd, &status, WNOHANG, NULL));
|
||||
#else
|
||||
// Sending a signal with pdkill() should be permitted 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));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(PipePdfork, WaitPidThenPd) {
|
||||
@ -624,18 +688,27 @@ TEST_F(PipePdforkDaemon, Pdkill) {
|
||||
|
||||
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) {
|
||||
// Child: watch for SIGUSR1 forever.
|
||||
had_signal.clear();
|
||||
// Child: tell the parent that we have started before entering the loop,
|
||||
// and importantly only do so once we have registered the SIGUSR1 handler.
|
||||
close(pipefds[0]);
|
||||
clear_had_signals();
|
||||
signal(SIGUSR1, handle_signal);
|
||||
SEND_INT_MESSAGE(pipefds[1], MSG_CHILD_STARTED);
|
||||
// Child: watch for SIGUSR1 forever.
|
||||
while (!had_signal[SIGUSR1]) {
|
||||
usleep(100000);
|
||||
}
|
||||
exit(123);
|
||||
}
|
||||
sleep(1);
|
||||
// Wait for child to start
|
||||
close(pipefds[1]);
|
||||
AWAIT_INT_MESSAGE(pipefds[0], MSG_CHILD_STARTED);
|
||||
close(pipefds[0]);
|
||||
|
||||
// Send an invalid signal.
|
||||
EXPECT_EQ(-1, pdkill(pd, 0xFFFF));
|
||||
@ -651,14 +724,15 @@ TEST(Pdfork, PdkillOtherSignal) {
|
||||
int rc = waitpid(pid, &status, __WALL);
|
||||
EXPECT_OK(rc);
|
||||
EXPECT_EQ(pid, rc);
|
||||
EXPECT_TRUE(WIFEXITED(status)) << "0x" << std::hex << rc;
|
||||
EXPECT_TRUE(WIFEXITED(status)) << "status: 0x" << std::hex << status;
|
||||
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 B: fork()ed process, starts a child process with pdfork() then
|
||||
// waits for parent to send a shutdown message.
|
||||
// +--pid C: pdfork()ed process, looping forever
|
||||
int sock_fds[2];
|
||||
EXPECT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds));
|
||||
@ -668,27 +742,45 @@ pid_t PdforkParentDeath(int pdfork_flags) {
|
||||
if (child == 0) {
|
||||
int pd;
|
||||
if (verbose) fprintf(stderr, " [%d] child about to pdfork()...\n", getpid_());
|
||||
int pipefds[2]; // for startup notification
|
||||
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", getpid_());
|
||||
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);
|
||||
// send grandchild pid to parent
|
||||
write(sock_fds[1], &grandchild, sizeof(grandchild));
|
||||
sleep(4);
|
||||
// Wait for grandchild to start.
|
||||
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]);
|
||||
|
||||
// Send grandchild pid to parent.
|
||||
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);
|
||||
// Wait for parent to acknowledge the message.
|
||||
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(0);
|
||||
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] receive grandchild id %d\n", getpid_(), grandchild);
|
||||
if (verbose) fprintf(stderr, "[%d] received grandchild id %d\n", getpid_(), grandchild);
|
||||
EXPECT_PID_ALIVE(child);
|
||||
EXPECT_PID_ALIVE(grandchild);
|
||||
sleep(6);
|
||||
// Tell child to exit.
|
||||
if (verbose) fprintf(stderr, "[%d] telling child %d to exit\n", getpid_(), child);
|
||||
SEND_INT_MESSAGE(sock_fds[0], MSG_PARENT_REQUEST_CHILD_EXIT);
|
||||
// Child dies, closing its process descriptor for the grandchild.
|
||||
EXPECT_PID_DEAD(child);
|
||||
CheckChildFinished(child);
|
||||
@ -713,7 +805,7 @@ TEST(Pdfork, BagpussDaemon) {
|
||||
|
||||
// The exit of a pdfork()ed process should not generate SIGCHLD.
|
||||
TEST_F(PipePdfork, NoSigchld) {
|
||||
had_signal.clear();
|
||||
clear_had_signals();
|
||||
sighandler_t original = signal(SIGCHLD, handle_signal);
|
||||
TerminateChild();
|
||||
int rc = 0;
|
||||
@ -728,7 +820,7 @@ TEST_F(PipePdfork, NoSigchld) {
|
||||
// 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();
|
||||
clear_had_signals();
|
||||
sighandler_t original = signal(SIGCHLD, handle_signal);
|
||||
|
||||
EXPECT_OK(close(pd_));
|
||||
@ -766,10 +858,8 @@ TEST_F(PipePdfork, ModeBits) {
|
||||
#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.
|
||||
EXPECT_PID_ZOMBIE(pid_); // Ensure child is truly dead.
|
||||
|
||||
// Wildcard waitpid(-1) should not see the pdfork()ed child because
|
||||
// there is still a process descriptor for it.
|
||||
@ -782,21 +872,30 @@ TEST_F(PipePdfork, WildcardWait) {
|
||||
}
|
||||
|
||||
FORK_TEST(Pdfork, Pdkill) {
|
||||
had_signal.clear();
|
||||
clear_had_signals();
|
||||
int pd;
|
||||
int pipefds[2];
|
||||
EXPECT_OK(pipe(pipefds));
|
||||
pid_t pid = pdfork(&pd, 0);
|
||||
EXPECT_OK(pid);
|
||||
|
||||
if (pid == 0) {
|
||||
// Child: set a SIGINT handler and sleep.
|
||||
had_signal.clear();
|
||||
// Child: set a SIGINT handler, notify the parent and sleep.
|
||||
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_());
|
||||
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);
|
||||
// Note: we could receive the SIGINT just before sleep(), so we use a loop
|
||||
// with a short delay instead of one long sleep().
|
||||
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]);
|
||||
// Return non-zero if we didn't see SIGINT.
|
||||
exit(had_signal[SIGINT] ? 0 : 99);
|
||||
}
|
||||
|
||||
// Parent: get child's PID.
|
||||
@ -804,9 +903,12 @@ FORK_TEST(Pdfork, Pdkill) {
|
||||
EXPECT_OK(pdgetpid(pd, &pd_pid));
|
||||
EXPECT_EQ(pid, pd_pid);
|
||||
|
||||
// Interrupt the child after a second.
|
||||
sleep(1);
|
||||
// Interrupt the child once it's registered the SIGINT handler.
|
||||
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_());
|
||||
|
||||
// Make sure the child finished properly (caught signal then exited).
|
||||
CheckChildFinished(pid);
|
||||
@ -814,19 +916,28 @@ FORK_TEST(Pdfork, Pdkill) {
|
||||
|
||||
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) {
|
||||
// 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);
|
||||
close(pipefds[0]);
|
||||
if (verbose) fprintf(stderr, "[%d] child started\n", getpid_());
|
||||
SEND_INT_MESSAGE(pipefds[1], MSG_CHILD_STARTED);
|
||||
// Child: wait for shutdown message. No SIGINT handler. The message should
|
||||
// never be received, since SIGINT should terminate the process.
|
||||
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);
|
||||
}
|
||||
|
||||
// Wait for child to start before signalling.
|
||||
if (verbose) fprintf(stderr, "[%d] waiting for child\n", getpid_());
|
||||
close(pipefds[1]);
|
||||
AWAIT_INT_MESSAGE(pipefds[0], MSG_CHILD_STARTED);
|
||||
// Kill the child (as it doesn't handle SIGINT).
|
||||
sleep(1);
|
||||
if (verbose) fprintf(stderr, "[%d] sending SIGINT\n", getpid_());
|
||||
EXPECT_OK(pdkill(pd, SIGINT));
|
||||
|
||||
// Make sure the child finished properly (terminated by signal).
|
||||
@ -922,7 +1033,7 @@ TEST_F(PipePdfork, PassProcessDescriptor) {
|
||||
if (child2 == 0) {
|
||||
// Child: close our copy of the original process descriptor.
|
||||
close(pd_);
|
||||
|
||||
SEND_INT_MESSAGE(sock_fds[0], MSG_CHILD_STARTED);
|
||||
// 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);
|
||||
@ -934,13 +1045,16 @@ TEST_F(PipePdfork, PassProcessDescriptor) {
|
||||
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);
|
||||
|
||||
// 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);
|
||||
// Wait until the parent has closed the process descriptor.
|
||||
AWAIT_INT_MESSAGE(sock_fds[0], MSG_PARENT_CLOSED_FD);
|
||||
|
||||
if (verbose) fprintf(stderr, " [%d] close process descriptor %d\n", getpid_(), pd);
|
||||
close(pd);
|
||||
|
||||
@ -949,7 +1063,8 @@ TEST_F(PipePdfork, PassProcessDescriptor) {
|
||||
|
||||
exit(HasFailure());
|
||||
}
|
||||
usleep(1000); // Ensure subprocess runs
|
||||
// Wait until the child has started.
|
||||
AWAIT_INT_MESSAGE(sock_fds[1], MSG_CHILD_STARTED);
|
||||
|
||||
// Send the process descriptor over the pipe to the sub-process
|
||||
mh.msg_controllen = CMSG_LEN(sizeof(int));
|
||||
@ -960,13 +1075,15 @@ TEST_F(PipePdfork, PassProcessDescriptor) {
|
||||
*(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);
|
||||
// Wait until the child has received the process descriptor.
|
||||
AWAIT_INT_MESSAGE(sock_fds[1], MSG_CHILD_FD_RECEIVED);
|
||||
|
||||
if (verbose) fprintf(stderr, "[%d] close process descriptor %d\n", getpid_(), pd_);
|
||||
close(pd_); // Not last open process descriptor
|
||||
SEND_INT_MESSAGE(sock_fds[1], MSG_PARENT_CLOSED_FD);
|
||||
|
||||
// wait for child2
|
||||
int status;
|
||||
|
18
socket.cc
18
socket.cc
@ -20,10 +20,13 @@ TEST(Socket, UnixDomain) {
|
||||
cap_rights_t r_all;
|
||||
cap_rights_init(&r_all, CAP_READ, CAP_WRITE, CAP_SOCK_CLIENT, CAP_SOCK_SERVER);
|
||||
|
||||
int pipefds[2];
|
||||
EXPECT_EQ(0, pipe(pipefds));
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
// Child process: wait for server setup
|
||||
sleep(1);
|
||||
close(pipefds[0]);
|
||||
AWAIT_INT_MESSAGE(pipefds[1], MSG_PARENT_CHILD_SHOULD_RUN);
|
||||
|
||||
// Create sockets
|
||||
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
@ -94,7 +97,9 @@ TEST(Socket, UnixDomain) {
|
||||
len = sizeof(value);
|
||||
EXPECT_OK(getsockopt(cap_sock_all, SOL_SOCKET, SO_DEBUG, &value, &len));
|
||||
|
||||
// Accept the incoming connection
|
||||
// Tell the child process that we are ready and accept the incoming connection.
|
||||
EXPECT_OK(close(pipefds[1]));
|
||||
SEND_INT_MESSAGE(pipefds[0], MSG_PARENT_CHILD_SHOULD_RUN);
|
||||
len = sizeof(un);
|
||||
memset(&un, 0, sizeof(un));
|
||||
EXPECT_NOTCAPABLE(accept(cap_sock_rw, (struct sockaddr *)&un, &len));
|
||||
@ -153,11 +158,14 @@ TEST(Socket, TCP) {
|
||||
getsockname(cap_sock_all, (struct sockaddr *)&addr, &len);
|
||||
int port = ntohs(addr.sin_port);
|
||||
|
||||
int pipefds[2];
|
||||
EXPECT_EQ(0, pipe(pipefds));
|
||||
// Now we know the port involved, fork off a child.
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
// Child process: wait for server setup
|
||||
sleep(1);
|
||||
close(pipefds[0]);
|
||||
AWAIT_INT_MESSAGE(pipefds[1], MSG_PARENT_CHILD_SHOULD_RUN);
|
||||
|
||||
// Create sockets
|
||||
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
@ -206,7 +214,9 @@ TEST(Socket, TCP) {
|
||||
len = sizeof(value);
|
||||
EXPECT_OK(getsockopt(cap_sock_all, SOL_SOCKET, SO_REUSEPORT, &value, &len));
|
||||
|
||||
// Accept the incoming connection
|
||||
// Tell the child process that we are ready and accept the incoming connection.
|
||||
EXPECT_OK(close(pipefds[1]));
|
||||
SEND_INT_MESSAGE(pipefds[0], MSG_PARENT_CHILD_SHOULD_RUN);
|
||||
len = sizeof(addr);
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
EXPECT_NOTCAPABLE(accept(cap_sock_rw, (struct sockaddr *)&addr, &len));
|
||||
|
Loading…
Reference in New Issue
Block a user