From: Roland McGrath I have removed notify_parent entirely now. The callers should use either ptrace_notify or ptrace_notify_info. The latter I've added to consolidate the traced-signal and traced-event cases and be the main entry point for reporting a ptrace stop; ptrace_notify now calls ptrace_notify_info. This changes the behavior of the event stops so that they too set current->last_siginfo and PTRACE_GETSIGINFO at after an event stop will return a siginfo_t whose si_code matches the extended exit code returned by wait. We can now test current->last_siginfo to reliably distinguish a ptrace stop from a job-control signal stop, which might be useful in the future. I've removed the recalc_sigpending call that was done in ptrace_notify. I don't know when or why this was added, probably it was before various signal fixes. This call should never be necessary. I believe that all paths of signal sending already do recalc_sigpending_tsk when needed. This patch updates all the arch-specific syscall trace code so that it uses ptrace_notify, though I only actually tried compiling on x86. I did this uniformly, and it makes a few arch's implement TRACESYSGOOD properly that failed to before. The h8300, m68k, and m68knommu implementations of do_signal call notify_parent. These will no longer compile and I didn't touch the code. They all have broken signal semantics in a variety of ways because they have not been updated in a long time. Each needs to be rewritten to use get_signal_to_deliver, and define the ptrace_signal_deliver macro as appropriate. This will bring those ports' signal behavior into line with everything else. Signed-off-by: Roland McGrath Signed-off-by: Andrew Morton --- 25-akpm/arch/arm/kernel/ptrace.c | 7 -- 25-akpm/arch/arm26/kernel/ptrace.c | 7 -- 25-akpm/arch/h8300/kernel/ptrace.c | 6 -- 25-akpm/arch/m68k/kernel/ptrace.c | 7 -- 25-akpm/arch/m68knommu/kernel/ptrace.c | 6 -- 25-akpm/arch/parisc/kernel/ptrace.c | 7 -- 25-akpm/arch/sh64/kernel/ptrace.c | 7 -- 25-akpm/arch/sparc/kernel/ptrace.c | 7 -- 25-akpm/arch/sparc64/kernel/ptrace.c | 7 -- 25-akpm/arch/v850/kernel/ptrace.c | 7 -- 25-akpm/include/linux/ptrace.h | 1 25-akpm/include/linux/sched.h | 1 25-akpm/kernel/ptrace.c | 21 ------- 25-akpm/kernel/signal.c | 93 +++++++++++++++++++++------------ 14 files changed, 81 insertions(+), 103 deletions(-) diff -puN arch/arm26/kernel/ptrace.c~remove-notify_parent arch/arm26/kernel/ptrace.c --- 25/arch/arm26/kernel/ptrace.c~remove-notify_parent 2004-08-22 00:10:30.390371736 -0700 +++ 25-akpm/arch/arm26/kernel/ptrace.c 2004-08-22 00:10:30.413368240 -0700 @@ -729,11 +729,8 @@ asmlinkage void syscall_trace(int why, s /* the 0x80 provides a way for the tracing parent to distinguish between a syscall stop and SIGTRAP delivery */ - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the diff -puN arch/arm/kernel/ptrace.c~remove-notify_parent arch/arm/kernel/ptrace.c --- 25/arch/arm/kernel/ptrace.c~remove-notify_parent 2004-08-22 00:10:30.391371584 -0700 +++ 25-akpm/arch/arm/kernel/ptrace.c 2004-08-22 00:10:30.413368240 -0700 @@ -792,11 +792,8 @@ asmlinkage void syscall_trace(int why, s /* the 0x80 provides a way for the tracing parent to distinguish between a syscall stop and SIGTRAP delivery */ - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the diff -puN arch/h8300/kernel/ptrace.c~remove-notify_parent arch/h8300/kernel/ptrace.c --- 25/arch/h8300/kernel/ptrace.c~remove-notify_parent 2004-08-22 00:10:30.393371280 -0700 +++ 25-akpm/arch/h8300/kernel/ptrace.c 2004-08-22 00:10:30.414368088 -0700 @@ -270,10 +270,8 @@ asmlinkage void syscall_trace(void) return; if (!(current->ptrace & PT_PTRACED)) return; - current->exit_code = SIGTRAP; - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the diff -puN arch/m68k/kernel/ptrace.c~remove-notify_parent arch/m68k/kernel/ptrace.c --- 25/arch/m68k/kernel/ptrace.c~remove-notify_parent 2004-08-22 00:10:30.394371128 -0700 +++ 25-akpm/arch/m68k/kernel/ptrace.c 2004-08-22 00:10:30.414368088 -0700 @@ -379,11 +379,8 @@ asmlinkage void syscall_trace(void) if (!current->thread.work.delayed_trace && !current->thread.work.syscall_trace) return; - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the diff -puN arch/m68knommu/kernel/ptrace.c~remove-notify_parent arch/m68knommu/kernel/ptrace.c --- 25/arch/m68knommu/kernel/ptrace.c~remove-notify_parent 2004-08-22 00:10:30.395370976 -0700 +++ 25-akpm/arch/m68knommu/kernel/ptrace.c 2004-08-22 00:10:30.415367936 -0700 @@ -376,10 +376,8 @@ asmlinkage void syscall_trace(void) return; if (!(current->ptrace & PT_PTRACED)) return; - current->exit_code = SIGTRAP; - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the diff -puN arch/parisc/kernel/ptrace.c~remove-notify_parent arch/parisc/kernel/ptrace.c --- 25/arch/parisc/kernel/ptrace.c~remove-notify_parent 2004-08-22 00:10:30.397370672 -0700 +++ 25-akpm/arch/parisc/kernel/ptrace.c 2004-08-22 00:10:30.415367936 -0700 @@ -404,11 +404,8 @@ void syscall_trace(void) return; if (!(current->ptrace & PT_PTRACED)) return; - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the diff -puN arch/sh64/kernel/ptrace.c~remove-notify_parent arch/sh64/kernel/ptrace.c --- 25/arch/sh64/kernel/ptrace.c~remove-notify_parent 2004-08-22 00:10:30.398370520 -0700 +++ 25-akpm/arch/sh64/kernel/ptrace.c 2004-08-22 00:10:30.416367784 -0700 @@ -311,11 +311,8 @@ asmlinkage void syscall_trace(void) if (!(tsk->ptrace & PT_PTRACED)) return; - tsk->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - tsk->state = TASK_STOPPED; - notify_parent(tsk, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the diff -puN arch/sparc64/kernel/ptrace.c~remove-notify_parent arch/sparc64/kernel/ptrace.c --- 25/arch/sparc64/kernel/ptrace.c~remove-notify_parent 2004-08-22 00:10:30.400370216 -0700 +++ 25-akpm/arch/sparc64/kernel/ptrace.c 2004-08-22 00:10:30.416367784 -0700 @@ -627,11 +627,8 @@ asmlinkage void syscall_trace(void) return; if (!(current->ptrace & PT_PTRACED)) return; - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do diff -puN arch/sparc/kernel/ptrace.c~remove-notify_parent arch/sparc/kernel/ptrace.c --- 25/arch/sparc/kernel/ptrace.c~remove-notify_parent 2004-08-22 00:10:30.401370064 -0700 +++ 25-akpm/arch/sparc/kernel/ptrace.c 2004-08-22 00:10:30.417367632 -0700 @@ -614,12 +614,9 @@ asmlinkage void syscall_trace(void) return; if (!(current->ptrace & PT_PTRACED)) return; - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; current->thread.flags ^= MAGIC_CONSTANT; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the diff -puN arch/v850/kernel/ptrace.c~remove-notify_parent arch/v850/kernel/ptrace.c --- 25/arch/v850/kernel/ptrace.c~remove-notify_parent 2004-08-22 00:10:30.403369760 -0700 +++ 25-akpm/arch/v850/kernel/ptrace.c 2004-08-22 00:10:30.418367480 -0700 @@ -269,11 +269,8 @@ asmlinkage void syscall_trace(void) return; /* The 0x80 provides a way for the tracing parent to distinguish between a syscall stop and SIGTRAP delivery */ - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the diff -puN include/linux/ptrace.h~remove-notify_parent include/linux/ptrace.h --- 25/include/linux/ptrace.h~remove-notify_parent 2004-08-22 00:10:30.404369608 -0700 +++ 25-akpm/include/linux/ptrace.h 2004-08-22 00:10:30.418367480 -0700 @@ -83,6 +83,7 @@ extern void ptrace_disable(struct task_s extern int ptrace_check_attach(struct task_struct *task, int kill); extern int ptrace_request(struct task_struct *child, long request, long addr, long data); extern void ptrace_notify(int exit_code); +extern void ptrace_notify_info(siginfo_t *); extern void __ptrace_link(struct task_struct *child, struct task_struct *new_parent); extern void __ptrace_unlink(struct task_struct *child); diff -puN include/linux/sched.h~remove-notify_parent include/linux/sched.h --- 25/include/linux/sched.h~remove-notify_parent 2004-08-22 00:10:30.406369304 -0700 +++ 25-akpm/include/linux/sched.h 2004-08-22 00:10:30.419367328 -0700 @@ -736,7 +736,6 @@ extern int __kill_pg_info(int sig, struc extern int kill_pg_info(int, struct siginfo *, pid_t); extern int kill_sl_info(int, struct siginfo *, pid_t); extern int kill_proc_info(int, struct siginfo *, pid_t); -extern void notify_parent(struct task_struct *, int); extern void do_notify_parent(struct task_struct *, int); extern void force_sig(int, struct task_struct *); extern void force_sig_specific(int, struct task_struct *); diff -puN kernel/ptrace.c~remove-notify_parent kernel/ptrace.c --- 25/kernel/ptrace.c~remove-notify_parent 2004-08-22 00:10:30.407369152 -0700 +++ 25-akpm/kernel/ptrace.c 2004-08-22 00:10:30.420367176 -0700 @@ -322,24 +322,3 @@ int ptrace_request(struct task_struct *c return ret; } - -void ptrace_notify(int exit_code) -{ - BUG_ON (!(current->ptrace & PT_PTRACED)); - - /* Let the debugger run. */ - current->exit_code = exit_code; - set_current_state(TASK_STOPPED); - notify_parent(current, SIGCHLD); - schedule(); - - /* - * Signals sent while we were stopped might set TIF_SIGPENDING. - */ - - spin_lock_irq(¤t->sighand->siglock); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); -} - -EXPORT_SYMBOL(ptrace_notify); diff -puN kernel/signal.c~remove-notify_parent kernel/signal.c --- 25/kernel/signal.c~remove-notify_parent 2004-08-22 00:10:30.409368848 -0700 +++ 25-akpm/kernel/signal.c 2004-08-22 00:10:30.422366872 -0700 @@ -1510,24 +1510,6 @@ void do_notify_parent(struct task_struct spin_unlock_irqrestore(&psig->siglock, flags); } - -/* - * We need the tasklist lock because it's the only - * thing that protects our "parent" pointer. - * - * This entrypoint was once used in other ways, but now it is - * only ever called as "notify_parent(current, SIGCHLD)" after - * entering TASK_STOPPED state. - */ -void -notify_parent(struct task_struct *tsk, int sig) -{ - BUG_ON(sig != SIGCHLD); - read_lock(&tasklist_lock); - do_notify_parent_cldstop(tsk, tsk->parent); - read_unlock(&tasklist_lock); -} - static void do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent) { @@ -1550,7 +1532,7 @@ do_notify_parent_cldstop(struct task_str if (info.si_status == 0) { info.si_status = SIGCONT; info.si_code = CLD_CONTINUED; - } else if (tsk->ptrace & PT_PTRACED) { + } else if (tsk->last_siginfo) { info.si_code = CLD_TRAPPED; } else { info.si_code = CLD_STOPPED; @@ -1770,22 +1752,9 @@ relock: if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { ptrace_signal_deliver(regs, cookie); - /* - * If there is a group stop in progress, - * we must participate in the bookkeeping. - */ - if (current->signal->group_stop_count > 0) - --current->signal->group_stop_count; - /* Let the debugger run. */ - current->exit_code = signr; - current->last_siginfo = info; - set_current_state(TASK_STOPPED); spin_unlock_irq(¤t->sighand->siglock); - notify_parent(current, SIGCHLD); - schedule(); - - current->last_siginfo = NULL; + ptrace_notify_info(info); /* We're back. Did the debugger cancel the sig? */ spin_lock_irq(¤t->sighand->siglock); @@ -1908,6 +1877,62 @@ relock: #endif +/* + * This should be the path for all ptrace stops. + * We always set current->last_siginfo while stopped here. + * That makes it a way to test a stopped process for + * being ptrace-stopped vs being job-control-stopped. + */ +void ptrace_notify_info(siginfo_t *info) +{ + BUG_ON (!(current->ptrace & PT_PTRACED)); + + /* + * If there is a group stop in progress, + * we must participate in the bookkeeping. + */ + if (current->signal->group_stop_count > 0) + --current->signal->group_stop_count; + + current->last_siginfo = info; + if (info->si_signo == SIGTRAP && + info->si_code > 0 && !(info->si_code & __SI_MASK)) { + /* + * This is an explicit notification with a special code, + * not an intercepted signal with its own normal info. + */ + BUG_ON((info->si_code & 0x7f) != SIGTRAP); + current->exit_code = info->si_code; + } else { + current->exit_code = info->si_signo; + } + + /* Let the debugger run. */ + set_current_state(TASK_STOPPED); + spin_unlock_irq(¤t->sighand->siglock); + read_lock(&tasklist_lock); + do_notify_parent_cldstop(current, current->parent); + read_unlock(&tasklist_lock); + schedule(); + + /* We are back. */ + current->last_siginfo = NULL; +} + +void ptrace_notify(int exit_code) +{ + siginfo_t info; + + memset(&info, 0, sizeof info); + info.si_signo = SIGTRAP; + info.si_code = exit_code; + info.si_pid = current->pid; + info.si_uid = current->uid; + + /* Let the debugger run. */ + ptrace_notify_info(&info); +} + EXPORT_SYMBOL(recalc_sigpending); EXPORT_SYMBOL_GPL(dequeue_signal); EXPORT_SYMBOL(flush_signals); @@ -1919,6 +1944,8 @@ EXPORT_SYMBOL(kill_proc); EXPORT_SYMBOL(kill_proc_info); EXPORT_SYMBOL(kill_sl); EXPORT_SYMBOL(kill_sl_info); +EXPORT_SYMBOL(ptrace_notify); +EXPORT_SYMBOL(ptrace_notify_info); EXPORT_SYMBOL(send_sig); EXPORT_SYMBOL(send_sig_info); EXPORT_SYMBOL(send_group_sig_info); _