#include <sys/types.h>
#include <sys/capsicum.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/procdesc.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
#include <machine/atomic.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <atf-c.h>
#include <kvm.h>
static void
wait_for_naptime(pid_t pid, const char *wmesg)
{
kvm_t *kd;
int count;
kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, NULL);
ATF_REQUIRE(kd != NULL);
for (;;) {
struct kinfo_proc *kip;
int i;
usleep(1000);
kip = kvm_getprocs(kd, KERN_PROC_PID | KERN_PROC_INC_THREAD,
pid, &count);
ATF_REQUIRE(kip != NULL);
for (i = 0; i < count; i++) {
ATF_REQUIRE(kip[i].ki_stat != SZOMB);
if (kip[i].ki_stat == SSLEEP &&
strcmp(kip[i].ki_wmesg, wmesg) == 0)
break;
}
if (i < count)
break;
}
kvm_close(kd);
}
ATF_TC_WITHOUT_HEAD(pid_recycle);
ATF_TC_BODY(pid_recycle, tc)
{
size_t len;
int i, pd, pid_max;
pid_t dangle_pid;
len = sizeof(pid_max);
ATF_REQUIRE_EQ_MSG(0,
sysctlbyname("kern.pid_max", &pid_max, &len, NULL, 0),
"sysctlbyname: %s", strerror(errno));
dangle_pid = pdfork(&pd, PD_CLOEXEC | PD_DAEMON);
ATF_REQUIRE_MSG(dangle_pid >= 0, "pdfork: %s", strerror(errno));
if (dangle_pid == 0) {
_exit(0);
}
ATF_REQUIRE_EQ(dangle_pid, waitpid(dangle_pid, NULL, WEXITED));
for (i = 0; i < pid_max; i++) {
pid_t pid;
pid = vfork();
ATF_REQUIRE_MSG(pid >= 0, "vfork: %s", strerror(errno));
if (pid == 0)
_exit(0);
ATF_REQUIRE_MSG(pid != dangle_pid,
"pid got recycled after %d forks", i);
ATF_REQUIRE_EQ(pid, waitpid(pid, NULL, WEXITED));
}
close(pd);
}
static void *
poll_procdesc(void *arg)
{
struct pollfd pfd;
pfd.fd = *(int *)arg;
pfd.events = POLLHUP;
(void)poll(&pfd, 1, 5000);
return ((void *)(uintptr_t)pfd.revents);
}
ATF_TC_WITHOUT_HEAD(poll_close_race);
ATF_TC_BODY(poll_close_race, tc)
{
pthread_t thr;
pid_t pid;
uintptr_t revents;
int error, pd;
pid = pdfork(&pd, PD_DAEMON);
ATF_REQUIRE_MSG(pid >= 0, "pdfork: %s", strerror(errno));
if (pid == 0) {
pause();
_exit(0);
}
error = pthread_create(&thr, NULL, poll_procdesc, &pd);
ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error));
wait_for_naptime(getpid(), "select");
ATF_REQUIRE_MSG(close(pd) == 0, "close: %s", strerror(errno));
error = pthread_join(thr, (void *)&revents);
ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error));
ATF_REQUIRE_EQ(revents, POLLNVAL);
}
ATF_TC_WITHOUT_HEAD(poll_exit_wakeup);
ATF_TC_BODY(poll_exit_wakeup, tc)
{
pthread_t thr;
uintptr_t revents;
pid_t pid;
int error, pd;
pid = pdfork(&pd, PD_DAEMON);
ATF_REQUIRE_MSG(pid >= 0, "pdfork: %s", strerror(errno));
if (pid == 0) {
pause();
_exit(0);
}
error = pthread_create(&thr, NULL, poll_procdesc, &pd);
ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error));
wait_for_naptime(getpid(), "select");
ATF_REQUIRE_MSG(pdkill(pd, SIGKILL) == 0,
"pdkill: %s", strerror(errno));
error = pthread_join(thr, (void *)&revents);
ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error));
ATF_REQUIRE_EQ(revents, POLLHUP);
ATF_REQUIRE_MSG(close(pd) == 0, "close: %s", strerror(errno));
}
ATF_TC_WITHOUT_HEAD(pdopenpid_basic);
ATF_TC_BODY(pdopenpid_basic, tc)
{
pid_t child, queried;
int fd;
child = fork();
ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
if (child == 0) {
for (;;)
pause();
_exit(0);
}
fd = pdopenpid(child, 0);
ATF_REQUIRE_MSG(fd >= 0, "pdopenpid: %s", strerror(errno));
ATF_REQUIRE_MSG(pdgetpid(fd, &queried) == 0,
"pdgetpid: %s", strerror(errno));
ATF_REQUIRE_EQ(child, queried);
ATF_REQUIRE_MSG(pdkill(fd, SIGKILL) == 0,
"pdkill: %s", strerror(errno));
ATF_REQUIRE_EQ(child, waitpid(child, NULL, 0));
ATF_REQUIRE(close(fd) == 0);
}
ATF_TC_WITHOUT_HEAD(pdopenpid_cloexec);
ATF_TC_BODY(pdopenpid_cloexec, tc)
{
pid_t child;
int fd, flags;
child = fork();
ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
if (child == 0) {
for (;;)
pause();
_exit(0);
}
fd = pdopenpid(child, PD_CLOEXEC);
ATF_REQUIRE_MSG(fd >= 0, "pdopenpid: %s", strerror(errno));
flags = fcntl(fd, F_GETFD);
ATF_REQUIRE(flags >= 0);
ATF_REQUIRE(flags & FD_CLOEXEC);
ATF_REQUIRE_MSG(pdkill(fd, SIGKILL) == 0,
"pdkill: %s", strerror(errno));
ATF_REQUIRE_EQ(child, waitpid(child, NULL, 0));
ATF_REQUIRE(close(fd) == 0);
}
ATF_TC_WITHOUT_HEAD(pdopenpid_einval);
ATF_TC_BODY(pdopenpid_einval, tc)
{
ATF_REQUIRE_ERRNO(EINVAL, pdopenpid(getpid(), 0xdeadbeef) < 0);
ATF_REQUIRE_ERRNO(EINVAL, pdopenpid(getpid(), ~PD_ALLOWED_AT_FORK) < 0);
}
ATF_TC_WITHOUT_HEAD(pdopenpid_emfile);
ATF_TC_BODY(pdopenpid_emfile, tc)
{
pid_t child;
struct rlimit rl;
int fd;
child = fork();
ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
if (child == 0) {
for (;;)
pause();
_exit(0);
}
fd = dup(STDIN_FILENO);
ATF_REQUIRE(fd >= 0);
ATF_REQUIRE(close(fd) == 0);
ATF_REQUIRE_EQ(getrlimit(RLIMIT_NOFILE, &rl), 0);
rl.rlim_cur = fd;
ATF_REQUIRE_EQ(setrlimit(RLIMIT_NOFILE, &rl), 0);
ATF_REQUIRE_ERRNO(EMFILE, pdopenpid(child, 0) < 0);
ATF_REQUIRE_MSG(kill(child, 0) == 0,
"child was killed: %s", strerror(errno));
ATF_REQUIRE_MSG(waitpid(child, NULL, WNOHANG) == 0,
"child was reparented");
ATF_REQUIRE_MSG(kill(child, SIGKILL) == 0,
"kill: %s", strerror(errno));
ATF_REQUIRE_EQ(child, waitpid(child, NULL, 0));
}
ATF_TC_WITHOUT_HEAD(pdopenpid_esrch);
ATF_TC_BODY(pdopenpid_esrch, tc)
{
ATF_REQUIRE_ERRNO(ESRCH, pdopenpid(123456789, 0) < 0);
}
ATF_TC_WITHOUT_HEAD(pdopenpid_capmode);
ATF_TC_BODY(pdopenpid_capmode, tc)
{
pid_t child, parent;
parent = getpid();
child = fork();
ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
if (child == 0) {
while (getppid() == parent)
sleep(1);
_exit(0);
}
ATF_REQUIRE_MSG(cap_enter() == 0, "cap_enter: %s", strerror(errno));
ATF_REQUIRE_ERRNO(ECAPMODE, pdopenpid(child, 0) < 0);
}
ATF_TC_WITHOUT_HEAD(pdopenpid_pdfork_then_open);
ATF_TC_BODY(pdopenpid_pdfork_then_open, tc)
{
pid_t child, pid1, pid2;
int fd1, fd2;
child = pdfork(&fd1, PD_DAEMON);
ATF_REQUIRE_MSG(child >= 0, "pdfork: %s", strerror(errno));
if (child == 0) {
for (;;)
pause();
_exit(0);
}
fd2 = pdopenpid(child, 0);
ATF_REQUIRE_MSG(fd2 >= 0, "pdopenpid: %s", strerror(errno));
ATF_REQUIRE(pdgetpid(fd1, &pid1) == 0);
ATF_REQUIRE(pdgetpid(fd2, &pid2) == 0);
ATF_REQUIRE_EQ(pid1, pid2);
ATF_REQUIRE_EQ(child, pid1);
ATF_REQUIRE_MSG(pdkill(fd2, SIGKILL) == 0,
"pdkill: %s", strerror(errno));
ATF_REQUIRE_MSG(pdwait(fd1, NULL, WEXITED, NULL, NULL) == 0,
"pdwait: %s", strerror(errno));
ATF_REQUIRE(close(fd1) == 0);
ATF_REQUIRE(close(fd2) == 0);
}
ATF_TC_WITHOUT_HEAD(pdopenpid_fork_then_open);
ATF_TC_BODY(pdopenpid_fork_then_open, tc)
{
pid_t child;
int fd, status;
child = fork();
ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
if (child == 0) {
for (;;)
pause();
_exit(0);
}
fd = pdopenpid(child, 0);
ATF_REQUIRE_MSG(fd >= 0, "pdopenpid: %s", strerror(errno));
ATF_REQUIRE_MSG(pdkill(fd, SIGKILL) == 0,
"pdkill: %s", strerror(errno));
ATF_REQUIRE_MSG(pdwait(fd, &status, WEXITED, NULL, NULL) == 0,
"pdwait: %s", strerror(errno));
ATF_REQUIRE(WIFSIGNALED(status));
ATF_REQUIRE_EQ(WTERMSIG(status), SIGKILL);
ATF_REQUIRE(close(fd) == 0);
}
ATF_TC_WITHOUT_HEAD(pdopenpid_close_one_of_two);
ATF_TC_BODY(pdopenpid_close_one_of_two, tc)
{
pid_t child, queried;
int fd1, fd2, status;
child = pdfork(&fd1, PD_DAEMON);
ATF_REQUIRE_MSG(child >= 0, "pdfork: %s", strerror(errno));
if (child == 0) {
for (;;)
pause();
_exit(0);
}
fd2 = pdopenpid(child, 0);
ATF_REQUIRE_MSG(fd2 >= 0, "pdopenpid: %s", strerror(errno));
ATF_REQUIRE(close(fd1) == 0);
ATF_REQUIRE_EQ(0, pdgetpid(fd2, &queried));
ATF_REQUIRE_EQ(child, queried);
ATF_REQUIRE_MSG(pdkill(fd2, 0) == 0,
"pdkill(0) after closing first fd: %s", strerror(errno));
ATF_REQUIRE_MSG(pdkill(fd2, SIGKILL) == 0,
"pdkill: %s", strerror(errno));
ATF_REQUIRE_MSG(pdwait(fd2, &status, WEXITED, NULL, NULL) == 0,
"pdwait: %s", strerror(errno));
ATF_REQUIRE(WIFSIGNALED(status));
ATF_REQUIRE_EQ(WTERMSIG(status), SIGKILL);
ATF_REQUIRE(close(fd2) == 0);
}
ATF_TC_WITHOUT_HEAD(pdopenpid_open_twice);
ATF_TC_BODY(pdopenpid_open_twice, tc)
{
pid_t child, pid1, pid2;
int fd1, fd2;
child = fork();
ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
if (child == 0) {
for (;;)
pause();
_exit(0);
}
fd1 = pdopenpid(child, 0);
ATF_REQUIRE_MSG(fd1 >= 0, "pdopenpid(1): %s", strerror(errno));
fd2 = pdopenpid(child, 0);
ATF_REQUIRE_MSG(fd2 >= 0, "pdopenpid(2): %s", strerror(errno));
ATF_REQUIRE_EQ(0, pdgetpid(fd1, &pid1));
ATF_REQUIRE_EQ(0, pdgetpid(fd2, &pid2));
ATF_REQUIRE_EQ(pid1, pid2);
ATF_REQUIRE(close(fd1) == 0);
ATF_REQUIRE_MSG(pdkill(fd2, 0) == 0,
"pdkill(0) after closing first fd: %s", strerror(errno));
ATF_REQUIRE_MSG(pdkill(fd2, SIGKILL) == 0,
"pdkill: %s", strerror(errno));
ATF_REQUIRE_MSG(pdwait(fd2, NULL, WEXITED, NULL, NULL) == 0,
"pdwait: %s", strerror(errno));
ATF_REQUIRE(close(fd2) == 0);
}
ATF_TC_WITHOUT_HEAD(pdopenpid_pdwait_only_one);
ATF_TC_BODY(pdopenpid_pdwait_only_one, tc)
{
pid_t child;
int fd1, fd2, pip[2], status;
ATF_REQUIRE_EQ(pipe(pip), 0);
child = pdfork(&fd1, PD_DAEMON);
ATF_REQUIRE_MSG(child >= 0, "pdfork: %s", strerror(errno));
if (child == 0) {
char c;
close(pip[1]);
(void)read(pip[0], &c, 1);
_exit(42);
}
ATF_REQUIRE(close(pip[0]) == 0);
fd2 = pdopenpid(child, 0);
ATF_REQUIRE_MSG(fd2 >= 0, "pdopenpid: %s", strerror(errno));
ATF_REQUIRE(close(pip[1]) == 0);
ATF_REQUIRE_MSG(pdwait(fd1, &status, WEXITED, NULL, NULL) == 0,
"pdwait(fd1): %s", strerror(errno));
ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 42);
ATF_REQUIRE_ERRNO(ESRCH, pdwait(fd2, &status, WEXITED, NULL, NULL) < 0);
ATF_REQUIRE(close(fd1) == 0);
ATF_REQUIRE(close(fd2) == 0);
}
ATF_TC_WITHOUT_HEAD(pdopenpid_self);
ATF_TC_BODY(pdopenpid_self, tc)
{
pid_t child, queried;
int childfd, fd, status;
child = pdfork(&childfd, 0);
if (child == 0) {
fd = pdopenpid(getpid(), PD_DAEMON);
if (fd < 0)
_exit(21);
if (pdgetpid(fd, &queried) == -1)
_exit(22);
if (getpid() != queried)
_exit(23);
if (close(fd) != 0)
_exit(24);
_exit(0);
}
ATF_REQUIRE(pdgetpid(childfd, &queried) == 0);
ATF_REQUIRE_EQ(queried, child);
ATF_REQUIRE(pdwait(childfd, &status, WEXITED, NULL, NULL) == 0);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
ATF_REQUIRE(close(childfd) == 0);
}
ATF_TC_WITHOUT_HEAD(pdopenpid_exiting);
ATF_TC_BODY(pdopenpid_exiting, tc)
{
pid_t child;
int fd, pip[2], status;
ATF_REQUIRE_EQ(pipe(pip), 0);
child = fork();
ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
if (child == 0) {
char c;
close(pip[1]);
(void)read(pip[0], &c, 1);
_exit(0);
}
ATF_REQUIRE(close(pip[0]) == 0);
ATF_REQUIRE(close(pip[1]) == 0);
usleep(1000);
fd = pdopenpid(child, 0);
if (fd >= 0) {
ATF_REQUIRE(close(fd) == 0);
} else {
ATF_REQUIRE_MSG(errno == EBUSY,
"unexpected errno %d (%s)", errno, strerror(errno));
}
ATF_REQUIRE(waitpid(child, &status, 0) == child);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
}
ATF_TC_WITHOUT_HEAD(pdopenpid_no_wakeup);
ATF_TC_BODY(pdopenpid_no_wakeup, tc)
{
pid_t child;
int fd, pip[2];
ATF_REQUIRE(pipe(pip) == 0);
child = fork();
ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
if (child == 0) {
char buf[1];
close(pip[1]);
(void)read(pip[0], buf, 1);
_exit(0);
}
close(pip[0]);
wait_for_naptime(child, "piperd");
fd = pdopenpid(child, 0);
ATF_REQUIRE_MSG(fd >= 0, "pdopenpid: %s", strerror(errno));
wait_for_naptime(child, "piperd");
ATF_REQUIRE(close(pip[1]) == 0);
ATF_REQUIRE(close(fd) == 0);
}
#define EXPECTED_OFFSET 123
ATF_TC_WITHOUT_HEAD(pddupfd_basic);
ATF_TC_BODY(pddupfd_basic, tc)
{
int *comm;
struct __wrusage wu;
struct __siginfo si;
int fd, pp, rfd, status, workfd, r, r1;
off_t off;
pid_t child;
comm = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED |
MAP_ANONYMOUS, -1, 0);
ATF_REQUIRE(comm != MAP_FAILED);
atomic_store_int(comm, -1);
child = fork();
ATF_REQUIRE(child != -1);
if (child == 0) {
workfd = open("/etc/passwd", O_RDONLY);
ATF_REQUIRE(workfd != -1);
atomic_store_int(comm, workfd);
while ((r = (int)atomic_load_int(comm)) >= 0)
sleep(1);
lseek(workfd, EXPECTED_OFFSET, SEEK_SET);
r -= 1;
atomic_store_int(comm, r);
while (r == (int)atomic_load_int(comm))
sleep(1);
_exit(0x23);
}
fd = pdopenpid(child, 0);
ATF_REQUIRE(fd != -1);
ATF_REQUIRE(pdgetpid(fd, &pp) == 0);
while ((rfd = (int)atomic_load_int(comm)) == -1)
;
ATF_REQUIRE(rfd >= 0);
workfd = pddupfd(fd, rfd, 0);
ATF_REQUIRE(workfd != -1);
off = lseek(workfd, 0, SEEK_CUR);
ATF_REQUIRE_EQ(off, 0);
r = -1;
atomic_store_int(comm, r);
while ((r1 = (int)atomic_load_int(comm)) == r)
sleep(1);
off = lseek(workfd, 0, SEEK_CUR);
ATF_REQUIRE_EQ(off, EXPECTED_OFFSET);
r1 -= 1;
atomic_store_int(comm, r1);
ATF_REQUIRE(close(workfd) == 0);
ATF_REQUIRE(pdwait(fd, &status, WEXITED, &wu, &si) != -1);
ATF_REQUIRE(close(fd) == 0);
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, pid_recycle);
ATF_TP_ADD_TC(tp, poll_close_race);
ATF_TP_ADD_TC(tp, poll_exit_wakeup);
ATF_TP_ADD_TC(tp, pdopenpid_basic);
ATF_TP_ADD_TC(tp, pdopenpid_cloexec);
ATF_TP_ADD_TC(tp, pdopenpid_einval);
ATF_TP_ADD_TC(tp, pdopenpid_emfile);
ATF_TP_ADD_TC(tp, pdopenpid_esrch);
ATF_TP_ADD_TC(tp, pdopenpid_capmode);
ATF_TP_ADD_TC(tp, pdopenpid_pdfork_then_open);
ATF_TP_ADD_TC(tp, pdopenpid_fork_then_open);
ATF_TP_ADD_TC(tp, pdopenpid_close_one_of_two);
ATF_TP_ADD_TC(tp, pdopenpid_open_twice);
ATF_TP_ADD_TC(tp, pdopenpid_pdwait_only_one);
ATF_TP_ADD_TC(tp, pdopenpid_self);
ATF_TP_ADD_TC(tp, pdopenpid_exiting);
ATF_TP_ADD_TC(tp, pdopenpid_no_wakeup);
ATF_TP_ADD_TC(tp, pddupfd_basic);
return (atf_no_error());
}