CHILD PARENT ===== ====== wait() >- waiting for an event from the child PT_TRACE_ME raise(SIGSTP) { -> _lwp_kill(_lwp_self(),SIGSTOP) ~\ \_________> wait() <- SIGSTOP \ PT_GETDBREGS \ lwp_userret() PT_SETDBREGS {set x86 debug register trap} \____ PT_CONTINUE {no signal} \ wait() >- waiting for an event from the child { SIGTRAP on debugregister } {not called?} \ \_____> wait() <- SIGSTOP from spurious _lwp_kill() userret() { this spurious signal sets p_xsig of child during the operation (inside): 730 error = do_sys_wait(&pid, &status, SCARG(uap, options), 731 SCARG(uap, rusage) != NULL ? &ru : NULL); we do lookup for a child inside and check for a child after lookup and before the operation to get signal, it's replaced by _lwp_kill() that happens to be still in operation; the set of the signal occurs at least before: /* Don't mark SIGCONT if we are being stopped */ *status = (child->p_xsig == SIGCONT && child->p_stat != SSTOP) ? W_CONTCODE() : W_STOPCODE(child->p_xsig); } Breakpoint 1, issignal (l=0xffffe4003c47da60) at /usr/src/sys/kern/kern_sig.c:1708 1708 printf("%s() %s:%d p_xsig=%d\n", __func__, __FILE__, __LINE__, signo); (gdb) bt #0 issignal (l=0xffffe4003c47da60) at /usr/src/sys/kern/kern_sig.c:1708 #1 0xffffffff80d28f3f in lwp_userret (l=0xffffe4003c47da60) at /usr/src/sys/kern/kern_lwp.c:1561 #2 0xffffffff8025f6dd in mi_userret (l=0xffffe4003c47da60) at /usr/src/sys/sys/userret.h:94 #3 0xffffffff8025f785 in userret (l=0xffffe4003c47da60) at ./machine/userret.h:84 #4 0xffffffff8025f9fd in syscall (frame=0xffff800032ef4000) at /usr/src/sys/arch/x86/x86/syscall.c:168 #5 0xffffffff802086dd in handle_syscall () #6 0x0000000000000001 in ?? () #7 0x0000000000000011 in ?? () #8 0x0000000000000000 in ?? () (gdb) f 4 #4 0xffffffff8025f9fd in syscall (frame=0xffff800032ef4000) at /usr/src/sys/arch/x86/x86/syscall.c:168 168 userret(l); (gdb) list 163 break; 164 } 165 } 166 167 SYSCALL_TIME_SYS_EXIT(l); 168 userret(l); 169 } 170 171 void 172 syscall_intern(struct proc *p) (gdb) p code $1 = 318 (gdb) p callp $2 = (const struct sysent *) 0xffffffff81a847d0 (gdb) p *callp $3 = {sy_narg = 2, sy_argsize = 16, sy_flags = 0, sy_call = 0xffffffff80da1f4f , sy_entry = 0, sy_return = 0} (gdb) f 0 #0 issignal (l=0xffffe4003c47da60) at /usr/src/sys/kern/kern_sig.c:1708 1708 printf("%s() %s:%d p_xsig=%d\n", __func__, __FILE__, __LINE__, signo); (gdb) p p->p_xsig $4 = 0 (gdb) p signo $5 = 17 {{ SIGSTOP }} $ cat db.c # the test-case #define _KERNTYPES #include #include #include #include #if defined(__FreeBSD__) #include #endif #include #include #include #include #include #include #include #include #define ATF_REQUIRE_MSG(a,b,...) assert(a) #define ATF_REQUIRE_EQ_MSG(a,b,c, ...) assert((a) == (b)) #define TWAIT_HAVE_STATUS #define TWAIT_GENERIC(a,b,c) waitpid((a),(b),(c)) #define TEST_REQUIRE_EQ(a,b) assert((a) == (b)) #define TWAIT_REQUIRE_SUCCESS(a,b) TEST_REQUIRE_EQ((a), (b)) #define TWAIT_FNAME "" #define ATF_REQUIRE(x) assert(x) #define ATF_REQUIRE_EQ(a,b) assert((a) == (b)) #define TWAIT_REQUIRE_FAILURE(a,b) do { assert((b) == -1); assert(errno == (a)); } while(0) #if 0 #define DPRINTF(x, ...) printf(x, ##__VA_ARGS__) #else #define DPRINTF(x, ...) #endif #define atf_utils_fork fork #define FORKEE_REQUIRE_SUCCESS(a,b) FORKEE_ASSERT_EQ(a, b) #define FORKEE_ASSERT_EQ(x, y) \ do { \ uintmax_t vx = (x); \ uintmax_t vy = (y); \ int ret = vx == vy; \ if (!ret) \ errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \ "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__, \ #x, vx, #y, vy); \ } while (/*CONSTCOND*/0) #define FORKEE_ASSERTX(x) \ do { \ int ret = (x); \ if (!ret) \ errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s",\ __FILE__, __LINE__, __func__, #x); \ } while (/*CONSTCOND*/0) #define FORKEE_ASSERT(x) \ do { \ int ret = (x); \ if (!ret) \ err(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s",\ __FILE__, __LINE__, __func__, #x); \ } while (/*CONSTCOND*/0) static void __used validate_status_stopped(int status, int expected) { ATF_REQUIRE_MSG(!WIFEXITED(status), "Reported exited process"); ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process"); ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process"); ATF_REQUIRE_MSG(WIFSTOPPED(status), "Reported !stopped process"); char st[128], ex[128]; strlcpy(st, strsignal(WSTOPSIG(status)), sizeof(st)); strlcpy(ex, strsignal(expected), sizeof(ex)); ATF_REQUIRE_EQ_MSG(WSTOPSIG(status), expected, "Unexpected stop signal received [%s] != [%s]", st, ex); } static void __used forkee_status_stopped(int status, int expected) { FORKEE_ASSERTX(!WIFEXITED(status)); FORKEE_ASSERTX(!WIFCONTINUED(status)); FORKEE_ASSERTX(!WIFSIGNALED(status)); FORKEE_ASSERTX(WIFSTOPPED(status)); FORKEE_ASSERT_EQ(WSTOPSIG(status), expected); } static void __used forkee_status_exited(int status, int expected) { FORKEE_ASSERTX(WIFEXITED(status)); FORKEE_ASSERTX(!WIFCONTINUED(status)); FORKEE_ASSERTX(!WIFSIGNALED(status)); FORKEE_ASSERTX(!WIFSTOPPED(status)); FORKEE_ASSERT_EQ(WEXITSTATUS(status), expected); } static void __used validate_status_exited(int status, int expected) { ATF_REQUIRE_MSG(WIFEXITED(status), "Reported !exited process"); ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process"); ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process"); ATF_REQUIRE_MSG(!WIFSTOPPED(status), "Reported stopped process"); ATF_REQUIRE_EQ_MSG(WEXITSTATUS(status), expected, "The process has exited with invalid value %d != %d", WEXITSTATUS(status), expected); } /* This function is currently designed to be run in the main/parent process */ static void __used await_zombie(pid_t process) { #if defined(__NetBSD__) struct kinfo_proc2 p; size_t len = sizeof(p); const int name[] = { [0] = CTL_KERN, [1] = KERN_PROC2, [2] = KERN_PROC_PID, [3] = process, [4] = sizeof(p), [5] = 1 }; const size_t namelen = __arraycount(name); /* Await the process becoming a zombie */ while(1) { int rv = sysctl(name, namelen, &p, &len, NULL, 0); if (rv != 0) { printf("rv=%d errno=%d\n", rv, errno); abort(); } if (p.p_stat == LSZOMB) break; ATF_REQUIRE(usleep(1000) == 0); } #elif defined(__FreeBSD__) /* * Wait for a process to exit. This is kind of gross, but * there is not a better way. * * Prior to r325719, the kern.proc.pid. sysctl failed * with ESRCH. After that change, a valid struct kinfo_proc * is returned for zombies with ki_stat set to SZOMB. */ for (;;) { struct kinfo_proc kp; size_t len; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = process; len = sizeof(kp); if (sysctl(mib, nitems(mib), &kp, &len, NULL, 0) == -1) { ATF_REQUIRE(errno == ESRCH); break; } if (kp.ki_stat == SZOMB) break; usleep(5000); } #else #error fatal #endif } /* $NetBSD: msg.h,v 1.1 2017/04/02 21:44:00 kamil Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Christos Zoulas. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ struct msg_fds { int pfd[2]; int cfd[2]; }; #define CLOSEFD(fd) do { \ if (fd != -1) { \ close(fd); \ fd = -1; \ } \ } while (/*CONSTCOND*/ 0) static int msg_open(struct msg_fds *fds) { if (pipe(fds->pfd) == -1) return -1; if (pipe(fds->cfd) == -1) { close(fds->pfd[0]); close(fds->pfd[1]); return -1; } return 0; } static void msg_close(struct msg_fds *fds) { CLOSEFD(fds->pfd[0]); CLOSEFD(fds->pfd[1]); CLOSEFD(fds->cfd[0]); CLOSEFD(fds->cfd[1]); } static int msg_write_child(const char *info, struct msg_fds *fds, void *msg, size_t len) { ssize_t rv; CLOSEFD(fds->cfd[1]); CLOSEFD(fds->pfd[0]); printf("Send %s\n", info); rv = write(fds->pfd[1], msg, len); if (rv != (ssize_t)len) return 1; // printf("Wait %s\n", info); rv = read(fds->cfd[0], msg, len); if (rv != (ssize_t)len) return 1; return 0; } static int msg_write_parent(const char *info, struct msg_fds *fds, void *msg, size_t len) { ssize_t rv; CLOSEFD(fds->pfd[1]); CLOSEFD(fds->cfd[0]); printf("Send %s\n", info); rv = write(fds->cfd[1], msg, len); if (rv != (ssize_t)len) return 1; // printf("Wait %s\n", info); rv = read(fds->pfd[0], msg, len); if (rv != (ssize_t)len) return 1; return 0; } static int msg_read_parent(const char *info, struct msg_fds *fds, void *msg, size_t len) { ssize_t rv; CLOSEFD(fds->pfd[1]); CLOSEFD(fds->cfd[0]); printf("Wait %s\n", info); rv = read(fds->pfd[0], msg, len); if (rv != (ssize_t)len) return 1; // printf("Send %s\n", info); rv = write(fds->cfd[1], msg, len); if (rv != (ssize_t)len) return 1; return 0; } static int msg_read_child(const char *info, struct msg_fds *fds, void *msg, size_t len) { ssize_t rv; CLOSEFD(fds->cfd[1]); CLOSEFD(fds->pfd[0]); printf("Wait %s\n", info); rv = read(fds->cfd[0], msg, len); if (rv != (ssize_t)len) return 1; // printf("Send %s\n", info); rv = write(fds->pfd[1], msg, len); if (rv != (ssize_t)len) return 1; return 0; } #define PARENT_TO_CHILD(info, fds, msg) \ SYSCALL_REQUIRE(msg_write_child(info " to child " # fds, &fds, &msg, sizeof(msg)) == 0) #define CHILD_FROM_PARENT(info, fds, msg) \ FORKEE_ASSERT(msg_read_parent(info " from parent " # fds, &fds, &msg, sizeof(msg)) == 0) #define CHILD_TO_PARENT(info, fds, msg) \ FORKEE_ASSERT(msg_write_parent(info " to parent " # fds, &fds, &msg, sizeof(msg)) == 0) #define PARENT_FROM_CHILD(info, fds, msg) \ SYSCALL_REQUIRE(msg_read_child(info " from parent " # fds, &fds, &msg, sizeof(msg)) == 0) #define SYSCALL_REQUIRE(expr) ATF_REQUIRE_MSG(expr, "%s: %s", # expr, \ strerror(errno)) #define SYSCALL_REQUIRE_ERRNO(res, exp) ATF_REQUIRE_MSG(res == exp, \ "%d(%s) != %d", res, strerror(res), exp) /* Happy number sequence -- this function is used to just consume cpu cycles */ #define HAPPY_NUMBER 1 /* If n is not happy then its sequence ends in the cycle: * 4, 16, 37, 58, 89, 145, 42, 20, 4, ... */ #define SAD_NUMBER 4 /* Calculate the sum of the squares of the digits of n */ static unsigned __used dsum(unsigned n) { unsigned sum, x; for (sum = 0; n; n /= 10) { x = n % 10; sum += x * x; } return sum; } static int __used #ifdef __clang__ __attribute__((__optnone__)) #else __attribute__((__optimize__("O0"))) #endif check_happy(unsigned n) { for (;;) { unsigned total = dsum(n); if (total == HAPPY_NUMBER) return 1; if (total == SAD_NUMBER) return 0; n = total; } } union u { unsigned long raw; struct { unsigned long local_dr0_breakpoint : 1; /* 0 */ unsigned long global_dr0_breakpoint : 1; /* 1 */ unsigned long local_dr1_breakpoint : 1; /* 2 */ unsigned long global_dr1_breakpoint : 1; /* 3 */ unsigned long local_dr2_breakpoint : 1; /* 4 */ unsigned long global_dr2_breakpoint : 1; /* 5 */ unsigned long local_dr3_breakpoint : 1; /* 6 */ unsigned long global_dr3_breakpoint : 1; /* 7 */ unsigned long local_exact_breakpt : 1; /* 8 */ unsigned long global_exact_breakpt : 1; /* 9 */ unsigned long reserved_10 : 1; /* 10 */ unsigned long rest_trans_memory : 1; /* 11 */ unsigned long reserved_12 : 1; /* 12 */ unsigned long general_detect_enable : 1; /* 13 */ unsigned long reserved_14 : 1; /* 14 */ unsigned long reserved_15 : 1; /* 15 */ unsigned long condition_dr0 : 2; /* 16-17 */ unsigned long len_dr0 : 2; /* 18-19 */ unsigned long condition_dr1 : 2; /* 20-21 */ unsigned long len_dr1 : 2; /* 22-23 */ unsigned long condition_dr2 : 2; /* 24-25 */ unsigned long len_dr2 : 2; /* 26-27 */ unsigned long condition_dr3 : 2; /* 28-29 */ unsigned long len_dr3 : 2; /* 30-31 */ } bits; }; unsigned N = 0; void loop(void) { struct msg_fds parent_tracer, parent_tracee; const int exitval_tracee = 5; const int exitval_tracer1 = 10, exitval_tracer2 = 20; pid_t tracee, tracer, wpid; uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ #if defined(TWAIT_HAVE_STATUS) int status; #endif printf("N=%d\n", N++); DPRINTF("Spawn tracee\n"); SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0); tracee = atf_utils_fork(); if (tracee == 0) { /* Wait for message from the parent */ CHILD_FROM_PARENT("Message 1", parent_tracee, msg); _exit(exitval_tracee); } DPRINTF("Spawn debugger\n"); SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0); tracer = atf_utils_fork(); if (tracer == 0) { /* Fork again and drop parent to reattach to PID 1 */ tracer = atf_utils_fork(); if (tracer != 0) _exit(exitval_tracer1); DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid()); FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); /* Wait for tracee and assert that it was stopped w/ SIGSTOP */ FORKEE_REQUIRE_SUCCESS( wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); forkee_status_stopped(status, SIGSTOP); /* Resume tracee with PT_CONTINUE */ FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); /* Inform parent that tracer has attached to tracee */ CHILD_TO_PARENT("Message 1", parent_tracer, msg); CHILD_FROM_PARENT("Message 2", parent_tracer, msg); /* Wait for tracee and assert that it exited */ FORKEE_REQUIRE_SUCCESS( wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); forkee_status_exited(status, exitval_tracee); DPRINTF("Before exiting of the tracer process\n"); _exit(exitval_tracer2); } DPRINTF("Wait for the tracer process (direct child) to exit calling " "%s()\n", TWAIT_FNAME); TWAIT_REQUIRE_SUCCESS( wpid = TWAIT_GENERIC(tracer, &status, 0), tracer); validate_status_exited(status, exitval_tracer1); DPRINTF("Wait for the non-exited tracee process with %s()\n", TWAIT_FNAME); TWAIT_REQUIRE_SUCCESS( wpid = TWAIT_GENERIC(tracee, NULL, WNOHANG), 0); DPRINTF("Wait for the tracer to attach to the tracee\n"); PARENT_FROM_CHILD("Message 1", parent_tracer, msg); DPRINTF("Resume the tracee and let it exit\n"); PARENT_TO_CHILD("Message 1", parent_tracee, msg); DPRINTF("Detect that tracee is zombie\n"); await_zombie(tracee); DPRINTF("Assert that there is no status about tracee - " "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME); TWAIT_REQUIRE_SUCCESS( wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0); DPRINTF("Resume the tracer and let it detect exited tracee\n"); PARENT_TO_CHILD("Message 2", parent_tracer, msg); DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n", TWAIT_FNAME); TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); // tutaj validate_status_exited(status, exitval_tracee); msg_close(&parent_tracer); msg_close(&parent_tracee); } static int dummy_print(volatile int w); static int dummy_print(volatile int w) { return w ? 1 : 2; } static void dbregs_trap_variable(int reg, int cond, int len, bool write) { const int exitval = 5; const int sigval = SIGSTOP; pid_t child, wpid; #if defined(TWAIT_HAVE_STATUS) int status; #endif struct dbreg r1; size_t i; volatile int watchme = 0; union u dr7; struct ptrace_siginfo info; memset(&info, 0, sizeof(info)); dr7.raw = 0; switch (reg) { case 0: dr7.bits.global_dr0_breakpoint = 1; dr7.bits.condition_dr0 = cond; dr7.bits.len_dr0 = len; case 1: dr7.bits.global_dr1_breakpoint = 1; dr7.bits.condition_dr1 = cond; dr7.bits.len_dr1 = len; case 2: dr7.bits.global_dr2_breakpoint = 1; dr7.bits.condition_dr2 = cond; dr7.bits.len_dr2 = len; case 3: dr7.bits.global_dr3_breakpoint = 1; dr7.bits.condition_dr3 = cond; dr7.bits.len_dr3 = len; break; } DPRINTF("Before forking process PID=%d\n", getpid()); SYSCALL_REQUIRE((child = fork()) != -1); if (child == 0) { DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); DPRINTF("Before raising %s from child\n", strsignal(sigval)); FORKEE_ASSERT(raise(sigval) == 0); if (write) watchme = 1; else dummy_print(watchme); // printf("watchme=%d\n", watchme); DPRINTF("Before raising %s from child\n", strsignal(sigval)); FORKEE_ASSERT(raise(sigval) == 0); DPRINTF("Before exiting of the child process\n"); _exit(exitval); } DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); validate_status_stopped(status, sigval); DPRINTF("Call GETDBREGS for the child process (r1)\n"); SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1); DPRINTF("State of the debug registers (r1):\n"); for (i = 0; i < __arraycount(r1.dr); i++) DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); r1.dr[reg] = (long)(intptr_t)&watchme; DPRINTF("Set DR%d (r1.dr[%d]) to new value %" PRIxREGISTER "\n", reg, reg, r1.dr[reg]); r1.dr[7] = dr7.raw; DPRINTF("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n", r1.dr[7]); DPRINTF("New state of the debug registers (r1):\n"); for (i = 0; i < __arraycount(r1.dr); i++) DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); DPRINTF("Call SETDBREGS for the child process (r1)\n"); SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); DPRINTF("Call CONTINUE for the child process\n"); SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0x00040000), child); validate_status_stopped(status, SIGTRAP); DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", info.psi_siginfo.si_signo, info.psi_siginfo.si_code, info.psi_siginfo.si_errno); DPRINTF("Before checking siginfo_t\n"); ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_DBREG); DPRINTF("Call CONTINUE for the child process\n"); SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); validate_status_stopped(status, sigval); DPRINTF("Before resuming the child process where it left off and " "without signal to be sent\n"); SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); validate_status_exited(status, exitval); DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); } int main(int argc, char **argv) { /* 0b11 -- break on data write&read */ /* 0b01 -- 2 bytes */ unsigned N = 0; while (1) { if (N++ % 100 == 0) printf("N=%u\n", N); dbregs_trap_variable(2, 3, 1, false); } return 0; } $ cvs -q -z6 diff -u sys/kern/ Index: sys/kern/kern_exit.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_exit.c,v retrieving revision 1.270 diff -u -r1.270 kern_exit.c --- sys/kern/kern_exit.c 7 Nov 2017 19:44:04 -0000 1.270 +++ sys/kern/kern_exit.c 21 Mar 2018 21:27:25 -0000 @@ -111,6 +111,8 @@ #include +int krtraced = 0; + #ifdef DEBUG_EXIT int debug_exit = 0; #define DPRINTF(x) if (debug_exit) printf x @@ -663,6 +665,11 @@ *pid = 0; return error; } +#if 0 + if ((options & 0x00040000) && (child->p_xsig == SIGSTOP)) { + printf("%s(): %s:%d\n", __func__, __FILE__, __LINE__); + } +#endif *pid = child->p_pid; if (child->p_stat == SZOMB) { @@ -675,6 +682,11 @@ proc_free(child, wru); } } else { +#if 0 + if ((options & 0x00040000) && (child->p_xsig == SIGSTOP)) { + printf("%s(): %s:%d\n", __func__, __FILE__, __LINE__); + } +#endif /* Don't mark SIGCONT if we are being stopped */ *status = (child->p_xsig == SIGCONT && child->p_stat != SSTOP) ? W_CONTCODE() : W_STOPCODE(child->p_xsig); @@ -725,11 +737,18 @@ syscallarg(struct rusage *) rusage; } */ int error, status, pid = SCARG(uap, pid); + int opt = SCARG(uap, options); struct rusage ru; - error = do_sys_wait(&pid, &status, SCARG(uap, options), + if (opt & 0x00040000) + krtraced = pid; + + error = do_sys_wait(&pid, &status, opt, SCARG(uap, rusage) != NULL ? &ru : NULL); + if (opt & 0x00040000) + krtraced = 0; + retval[0] = pid; if (pid == 0) { return error; @@ -821,6 +840,11 @@ int rv = 1; mutex_enter(p->p_lock); +#if 0 + if ((options & 0x00040000) && (p->p_xsig == SIGSTOP)) { + printf("%s(): %s:%d\n", __func__, __FILE__, __LINE__); + } +#endif switch (idtype) { case P_ALL: break; @@ -832,6 +856,11 @@ *q = NULL; return -1; } +#if 0 + if ((options & 0x00040000) && (p->p_xsig == SIGSTOP)) { + printf("%s(): %s:%d\n", __func__, __FILE__, __LINE__); + } +#endif mutex_enter(p->p_lock); } rv++; @@ -862,6 +891,12 @@ return 0; } +#if 0 + if ((options & 0x00040000) && (p->p_xsig == SIGSTOP)) { + printf("%s(): %s:%d\n", __func__, __FILE__, __LINE__); + } +#endif + if ((options & WEXITED) == 0 && p->p_stat == SZOMB) goto out; @@ -913,6 +948,11 @@ } mutex_exit(p->p_lock); +#if 0 + if ((options & 0x00040000) && (p->p_xsig == SIGSTOP)) { + printf("%s(): %s:%d\n", __func__, __FILE__, __LINE__); + } +#endif return rv; } @@ -991,10 +1031,12 @@ KASSERT(mutex_owned(proc_lock)); +#if 0 if (options & ~WALLOPTS) { *child_p = NULL; return EINVAL; } +#endif if ((options & WSELECTOPTS) == 0) { /* @@ -1026,6 +1068,12 @@ if (rv == 0) continue; +#if 0 + if ((options & 0x00040000) && (child->p_xsig == SIGSTOP)) { + printf("%s(): %s:%d\n", __func__, __FILE__, __LINE__); + } +#endif + /* * Wait for processes with p_exitsig != SIGCHLD * processes only if WALTSIG is set; wait for Index: sys/kern/kern_sig.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_sig.c,v retrieving revision 1.339 diff -u -r1.339 kern_sig.c --- sys/kern/kern_sig.c 7 Dec 2017 19:49:43 -0000 1.339 +++ sys/kern/kern_sig.c 21 Mar 2018 21:27:25 -0000 @@ -107,6 +107,8 @@ #include +extern int krtraced; + #define SIGQUEUE_MAX 32 static pool_cache_t sigacts_cache __read_mostly; static pool_cache_t ksiginfo_cache __read_mostly; @@ -1669,6 +1671,9 @@ if (p->p_stat == SSTOP || (p->p_sflag & PS_STOPPING) != 0) { sigswitch(true, PS_NOCLDSTOP, 0); signo = sigchecktrace(); + if (krtraced == p->p_pid && signo == SIGSTOP) { + printf("%s() %s:%d p_xsig=%d\n", __func__, __FILE__, __LINE__, signo); + } } else signo = 0; @@ -1687,14 +1692,22 @@ sigminusset(&stopsigmask, &ss); sigminusset(&l->l_sigmask, &ss); - if ((signo = firstsig(&ss)) == 0) { + signo = firstsig(&ss); + if (krtraced == p->p_pid && signo == SIGSTOP) { + printf("%s() %s:%d p_xsig=%d\n", __func__, __FILE__, __LINE__, signo); + } + if (signo == 0) { sp = &p->p_sigpend; ss = sp->sp_set; if ((p->p_lflag & PL_PPWAIT) != 0) sigminusset(&stopsigmask, &ss); sigminusset(&l->l_sigmask, &ss); - if ((signo = firstsig(&ss)) == 0) { + signo = firstsig(&ss); + if (krtraced == p->p_pid && signo == SIGSTOP) { + printf("%s() %s:%d p_xsig=%d\n", __func__, __FILE__, __LINE__, signo); + } + if (signo == 0) { /* * No signal pending - clear the * indicator and bail out. @@ -1732,6 +1745,9 @@ */ if (sp) sigdelset(&sp->sp_set, signo); + if (krtraced == p->p_pid && signo == SIGSTOP) { + printf("%s() %s:%d p_xsig=%d\n", __func__, __FILE__, __LINE__, signo); + } p->p_xsig = signo; /* Emulation-specific handling of signal trace */ @@ -1788,6 +1804,9 @@ } /* Take the signal. */ (void)sigget(sp, NULL, signo, NULL); + if (krtraced == p->p_pid) { + printf("%s() %s:%d p_xsig=%d\n", __func__, __FILE__, __LINE__, signo); + } p->p_xsig = signo; p->p_sflag &= ~PS_CONTINUED; signo = 0;