#include "macros.h"
#include <sys/types.h>
#include <sys/event.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <sys/proc.h>
#include <sys/ptrace.h>
#include <sys/queue.h>
#include <sys/syscall.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <errno.h>
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "atf-c.h"
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#define CHILD_REQUIRE(exp) do { \
if (!(exp)) \
child_fail_require(__FILE__, __LINE__, \
#exp " not met"); \
} while (0)
static __dead2 void
child_fail_require(const char *file, int line, const char *str)
{
char buf[128];
snprintf(buf, sizeof(buf), "%s:%d: %s\n", file, line, str);
write(2, buf, strlen(buf));
_exit(32);
}
static void
trace_me(void)
{
CHILD_REQUIRE(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
raise(SIGSTOP);
}
static void
attach_child(pid_t pid)
{
pid_t wpid;
int status;
ATF_REQUIRE(ptrace(PT_ATTACH, pid, NULL, 0) == 0);
wpid = waitpid(pid, &status, 0);
ATF_REQUIRE(wpid == pid);
ATF_REQUIRE(WIFSTOPPED(status));
ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
}
static void
wait_for_zombie(pid_t pid)
{
for (;;) {
struct kinfo_proc kp;
size_t len;
int mib[6];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = pid;
mib[4] = len = sizeof(kp);
mib[5] = 1;
if (sysctl(mib, nitems(mib), &kp, &len, NULL, 0) == -1) {
ATF_REQUIRE(errno == ESRCH);
break;
}
if (kp.p_stat == SDEAD)
break;
usleep(5000);
}
}
ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_trace_me);
ATF_TC_BODY(ptrace__parent_wait_after_trace_me, tc)
{
pid_t child, wpid;
int status;
ATF_REQUIRE((child = fork()) != -1);
if (child == 0) {
trace_me();
_exit(1);
}
wpid = waitpid(child, &status, 0);
printf("first %d\n", wpid);
ATF_REQUIRE(wpid == child);
ATF_REQUIRE(WIFSTOPPED(status));
ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
wpid = waitpid(child, &status, 0);
printf("second %d\n", wpid);
ATF_REQUIRE(wpid == child);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE(WEXITSTATUS(status) == 1);
wpid = waitpid(child, &status, 0);
printf("third %d\n", wpid);
ATF_REQUIRE(wpid == -1);
ATF_REQUIRE(errno == ECHILD);
}
ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_attach);
ATF_TC_BODY(ptrace__parent_wait_after_attach, tc)
{
pid_t child, wpid;
int cpipe[2], status;
char c;
ATF_REQUIRE(pipe(cpipe) == 0);
ATF_REQUIRE((child = fork()) != -1);
if (child == 0) {
close(cpipe[0]);
CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == 0);
_exit(1);
}
close(cpipe[1]);
attach_child(child);
ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
close(cpipe[0]);
wpid = waitpid(child, &status, 0);
ATF_REQUIRE(wpid == child);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE(WEXITSTATUS(status) == 1);
wpid = waitpid(child, &status, 0);
ATF_REQUIRE(wpid == -1);
ATF_REQUIRE(errno == ECHILD);
}
ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_child_debugger);
ATF_TC_BODY(ptrace__parent_sees_exit_after_child_debugger, tc)
{
pid_t child, debugger, wpid;
int cpipe[2], dpipe[2], status;
char c;
if (atf_tc_get_config_var_as_bool_wd(tc, "ci", false))
atf_tc_skip("https://bugs.freebsd.org/239399");
ATF_REQUIRE(pipe(cpipe) == 0);
ATF_REQUIRE((child = fork()) != -1);
if (child == 0) {
close(cpipe[0]);
CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c));
_exit(1);
}
close(cpipe[1]);
ATF_REQUIRE(pipe(dpipe) == 0);
ATF_REQUIRE((debugger = fork()) != -1);
if (debugger == 0) {
close(dpipe[0]);
CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1);
wpid = waitpid(child, &status, 0);
CHILD_REQUIRE(wpid == child);
CHILD_REQUIRE(WIFSTOPPED(status));
CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP);
CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c));
CHILD_REQUIRE(read(dpipe[1], &c, sizeof(c)) == 0);
wpid = waitpid(child, &status, 0);
CHILD_REQUIRE(wpid == child);
CHILD_REQUIRE(WIFEXITED(status));
CHILD_REQUIRE(WEXITSTATUS(status) == 1);
_exit(0);
}
close(dpipe[1]);
ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c));
ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c));
ATF_REQUIRE(read(cpipe[0], &c, sizeof(c)) == 0);
close(cpipe[0]);
wait_for_zombie(child);
wpid = waitpid(child, &status, WNOHANG);
ATF_REQUIRE(wpid == 0);
close(dpipe[0]);
wpid = waitpid(debugger, &status, 0);
ATF_REQUIRE(wpid == debugger);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE(WEXITSTATUS(status) == 0);
wpid = waitpid(child, &status, WNOHANG);
ATF_REQUIRE(wpid == child);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE(WEXITSTATUS(status) == 1);
}
ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_unrelated_debugger);
ATF_TC_BODY(ptrace__parent_sees_exit_after_unrelated_debugger, tc)
{
pid_t child, debugger, fpid, wpid;
int cpipe[2], dpipe[2], status;
char c;
ATF_REQUIRE(pipe(cpipe) == 0);
ATF_REQUIRE((child = fork()) != -1);
if (child == 0) {
close(cpipe[0]);
CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c));
_exit(1);
}
close(cpipe[1]);
ATF_REQUIRE(pipe(dpipe) == 0);
ATF_REQUIRE((debugger = fork()) != -1);
if (debugger == 0) {
CHILD_REQUIRE((fpid = fork()) != -1);
if (fpid != 0)
_exit(2);
close(dpipe[0]);
CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1);
wpid = waitpid(child, &status, 0);
CHILD_REQUIRE(wpid == child);
CHILD_REQUIRE(WIFSTOPPED(status));
CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP);
CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c));
CHILD_REQUIRE(read(dpipe[1], &c, sizeof(c)) == sizeof(c));
wpid = waitpid(child, &status, 0);
CHILD_REQUIRE(wpid == child);
CHILD_REQUIRE(WIFEXITED(status));
CHILD_REQUIRE(WEXITSTATUS(status) == 1);
_exit(0);
}
close(dpipe[1]);
wpid = waitpid(debugger, &status, 0);
ATF_REQUIRE(wpid == debugger);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE(WEXITSTATUS(status) == 2);
wpid = waitpid(child, &status, WNOHANG);
ATF_REQUIRE(wpid == 0);
ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c));
ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c));
ATF_REQUIRE(read(cpipe[0], &c, sizeof(c)) == 0);
close(cpipe[0]);
wait_for_zombie(child);
wpid = waitpid(child, &status, WNOHANG);
ATF_REQUIRE(wpid == 0);
ATF_REQUIRE(write(dpipe[0], &c, sizeof(c)) == sizeof(c));
ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == 0);
close(dpipe[0]);
wpid = waitpid(child, &status, WNOHANG);
ATF_REQUIRE(wpid == child);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE(WEXITSTATUS(status) == 1);
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_trace_me);
ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_attach);
ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_child_debugger);
ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_unrelated_debugger);
return (atf_no_error());
}