
From: Roland McGrath <roland@redhat.com>

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 <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 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(&current->sighand->siglock);
-	recalc_sigpending();
-	spin_unlock_irq(&current->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(&current->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(&current->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(&current->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);
_
