Messages in this thread Patch in this message |  | | | Date | Mon, 30 Aug 2004 22:43:36 -0700 | | From | Roland McGrath <> | | Subject | Re: [PATCH] cleanup ptrace stops and remove notify_parent |
> Well given that waitid ha baked for a bit longer I guess we'd best make > this patch come afterwards. So if you could redo this patch against > rc1-mm1, please?
Here is the TASK_TRACED patch against 2.6.9-rc1-mm1.
Thanks, Roland
diff -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/arch/arm/kernel/ptrace.c linux-2.6.9-rc1-mm1+TASK_TRACED/arch/arm/kernel/ptrace.c --- linux-2.6.9-rc1-mm1/arch/arm/kernel/ptrace.c 2004-08-14 03:54:49.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/arch/arm/kernel/ptrace.c 2004-08-30 22:02:55.000000000 -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 -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/arch/arm26/kernel/ptrace.c linux-2.6.9-rc1-mm1+TASK_TRACED/arch/arm26/kernel/ptrace.c --- linux-2.6.9-rc1-mm1/arch/arm26/kernel/ptrace.c 2004-08-14 03:55:32.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/arch/arm26/kernel/ptrace.c 2004-08-30 22:02:56.000000000 -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 -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/arch/cris/arch-v10/kernel/ptrace.c linux-2.6.9-rc1-mm1+TASK_TRACED/arch/cris/arch-v10/kernel/ptrace.c --- linux-2.6.9-rc1-mm1/arch/cris/arch-v10/kernel/ptrace.c 2004-08-14 03:56:23.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/arch/cris/arch-v10/kernel/ptrace.c 2004-08-30 22:02:56.000000000 -0700 @@ -85,17 +85,8 @@ sys_ptrace(long request, long pid, long goto out_tsk; } - ret = -ESRCH; - - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out_tsk; - } - - if (child->parent != current) + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) goto out_tsk; switch (request) { diff -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/arch/h8300/kernel/ptrace.c linux-2.6.9-rc1-mm1+TASK_TRACED/arch/h8300/kernel/ptrace.c --- linux-2.6.9-rc1-mm1/arch/h8300/kernel/ptrace.c 2004-08-14 03:55:10.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/arch/h8300/kernel/ptrace.c 2004-08-30 22:02:56.000000000 -0700 @@ -89,13 +89,6 @@ asmlinkage int sys_ptrace(long request, ret = ptrace_attach(child); goto out_tsk; } - ret = -ESRCH; - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out_tsk; - } ret = ptrace_check_attach(child, request == PTRACE_KILL); if (ret < 0) goto out_tsk; @@ -270,10 +263,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 -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/arch/m68k/kernel/ptrace.c linux-2.6.9-rc1-mm1+TASK_TRACED/arch/m68k/kernel/ptrace.c --- linux-2.6.9-rc1-mm1/arch/m68k/kernel/ptrace.c 2004-08-14 03:55:09.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/arch/m68k/kernel/ptrace.c 2004-08-30 22:02:56.000000000 -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 -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/arch/m68knommu/kernel/ptrace.c linux-2.6.9-rc1-mm1+TASK_TRACED/arch/m68knommu/kernel/ptrace.c --- linux-2.6.9-rc1-mm1/arch/m68knommu/kernel/ptrace.c 2004-08-14 03:55:33.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/arch/m68knommu/kernel/ptrace.c 2004-08-30 22:02:56.000000000 -0700 @@ -133,13 +133,6 @@ asmlinkage int sys_ptrace(long request, ret = ptrace_attach(child); goto out_tsk; } - ret = -ESRCH; - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out_tsk; - } ret = ptrace_check_attach(child, request == PTRACE_KILL); if (ret < 0) goto out_tsk; @@ -376,10 +369,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 -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/arch/parisc/kernel/ptrace.c linux-2.6.9-rc1-mm1+TASK_TRACED/arch/parisc/kernel/ptrace.c --- linux-2.6.9-rc1-mm1/arch/parisc/kernel/ptrace.c 2004-08-14 03:54:50.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/arch/parisc/kernel/ptrace.c 2004-08-30 22:02:56.000000000 -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 -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/arch/sh64/kernel/ptrace.c linux-2.6.9-rc1-mm1+TASK_TRACED/arch/sh64/kernel/ptrace.c --- linux-2.6.9-rc1-mm1/arch/sh64/kernel/ptrace.c 2004-08-14 03:55:47.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/arch/sh64/kernel/ptrace.c 2004-08-30 22:02:56.000000000 -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 -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/arch/sparc/kernel/ptrace.c linux-2.6.9-rc1-mm1+TASK_TRACED/arch/sparc/kernel/ptrace.c --- linux-2.6.9-rc1-mm1/arch/sparc/kernel/ptrace.c 2004-08-14 03:56:23.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/arch/sparc/kernel/ptrace.c 2004-08-30 22:02:56.000000000 -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 -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/arch/sparc64/kernel/ptrace.c linux-2.6.9-rc1-mm1+TASK_TRACED/arch/sparc64/kernel/ptrace.c --- linux-2.6.9-rc1-mm1/arch/sparc64/kernel/ptrace.c 2004-08-14 03:56:23.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/arch/sparc64/kernel/ptrace.c 2004-08-30 22:02:56.000000000 -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 -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/arch/v850/kernel/ptrace.c linux-2.6.9-rc1-mm1+TASK_TRACED/arch/v850/kernel/ptrace.c --- linux-2.6.9-rc1-mm1/arch/v850/kernel/ptrace.c 2004-08-14 03:55:32.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/arch/v850/kernel/ptrace.c 2004-08-30 22:02:56.000000000 -0700 @@ -147,14 +147,8 @@ int sys_ptrace(long request, long pid, l rval = ptrace_attach(child); goto out_tsk; } - rval = -ESRCH; - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out_tsk; - } - if (child->parent != current) + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) goto out_tsk; switch (request) { @@ -269,11 +263,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 -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/fs/proc/array.c linux-2.6.9-rc1-mm1+TASK_TRACED/fs/proc/array.c --- linux-2.6.9-rc1-mm1/fs/proc/array.c 2004-08-27 13:46:35.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/fs/proc/array.c 2004-08-30 22:02:56.000000000 -0700 @@ -130,8 +130,9 @@ static const char *task_state_array[] = "S (sleeping)", /* 1 */ "D (disk sleep)", /* 2 */ "T (stopped)", /* 4 */ - "Z (zombie)", /* 8 */ - "X (dead)" /* 16 */ + "T (tracing stop)", /* 8 */ + "Z (zombie)", /* 16 */ + "X (dead)" /* 32 */ }; static inline const char * get_task_state(struct task_struct *tsk) @@ -141,7 +142,8 @@ static inline const char * get_task_stat TASK_UNINTERRUPTIBLE | TASK_ZOMBIE | TASK_DEAD | - TASK_STOPPED); + TASK_STOPPED | + TASK_TRACED); const char **p = &task_state_array[0]; while (state) { diff -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/fs/proc/base.c linux-2.6.9-rc1-mm1+TASK_TRACED/fs/proc/base.c --- linux-2.6.9-rc1-mm1/fs/proc/base.c 2004-08-27 13:46:35.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/fs/proc/base.c 2004-08-30 22:02:56.000000000 -0700 @@ -300,7 +300,8 @@ static int proc_root_link(struct inode * #define MAY_PTRACE(task) \ (task == current || \ (task->parent == current && \ - (task->ptrace & PT_PTRACED) && task->state == TASK_STOPPED && \ + (task->ptrace & PT_PTRACED) && \ + (task->state == TASK_STOPPED || task->state == TASK_TRACED) && \ security_ptrace(current,task) == 0)) static int may_ptrace_attach(struct task_struct *task) diff -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/include/linux/sched.h linux-2.6.9-rc1-mm1+TASK_TRACED/include/linux/sched.h --- linux-2.6.9-rc1-mm1/include/linux/sched.h 2004-08-27 13:46:37.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/include/linux/sched.h 2004-08-30 22:02:56.657738337 -0700 @@ -107,8 +107,9 @@ extern unsigned long nr_iowait(void); #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define TASK_STOPPED 4 -#define TASK_ZOMBIE 8 -#define TASK_DEAD 16 +#define TASK_TRACED 8 +#define TASK_ZOMBIE 16 +#define TASK_DEAD 32 #define __set_task_state(tsk, state_value) \ do { (tsk)->state = (state_value); } while (0) @@ -758,7 +759,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 -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/kernel/exit.c linux-2.6.9-rc1-mm1+TASK_TRACED/kernel/exit.c --- linux-2.6.9-rc1-mm1/kernel/exit.c 2004-08-27 13:46:37.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/kernel/exit.c 2004-08-30 22:02:56.000000000 -0700 @@ -564,6 +564,14 @@ static inline void reparent_thread(task_ if (p->state == TASK_ZOMBIE && p->exit_signal != -1 && thread_group_empty(p)) do_notify_parent(p, p->exit_signal); + else if (p->state == TASK_TRACED) { + /* + * If it was at a trace stop, turn it into + * a normal stop since it's no longer being + * traced. + */ + p->state = TASK_STOPPED; + } } /* @@ -1201,7 +1209,7 @@ static int wait_task_stopped(task_t *p, * race with the TASK_ZOMBIE case. */ exit_code = xchg(&p->exit_code, 0); - if (unlikely(p->state > TASK_STOPPED)) { + if (unlikely(p->state >= TASK_ZOMBIE)) { /* * The task resumed and then died. Let the next iteration * catch it in TASK_ZOMBIE. Note that exit_code might @@ -1282,6 +1290,10 @@ repeat: flag = 1; switch (p->state) { + case TASK_TRACED: + if (!(p->ptrace & PT_PTRACED)) + continue; + /*FALLTHROUGH*/ case TASK_STOPPED: if (!(options & WUNTRACED) && !(p->ptrace & PT_PTRACED)) diff -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/kernel/power/process.c linux-2.6.9-rc1-mm1+TASK_TRACED/kernel/power/process.c --- linux-2.6.9-rc1-mm1/kernel/power/process.c 2004-08-14 03:54:47.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/kernel/power/process.c 2004-08-30 22:02:56.000000000 -0700 @@ -25,7 +25,8 @@ static inline int freezeable(struct task (p->flags & PF_NOFREEZE) || (p->state == TASK_ZOMBIE) || (p->state == TASK_DEAD) || - (p->state == TASK_STOPPED)) + (p->state == TASK_STOPPED) || + (p->state == TASK_TRACED)) return 0; return 1; } @@ -70,6 +71,7 @@ int freeze_processes(void) if (!freezeable(p)) continue; if ((p->flags & PF_FROZEN) || + (p->state == TASK_TRACED) || (p->state == TASK_STOPPED)) continue; diff -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/kernel/ptrace.c linux-2.6.9-rc1-mm1+TASK_TRACED/kernel/ptrace.c --- linux-2.6.9-rc1-mm1/kernel/ptrace.c 2004-08-14 03:56:24.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/kernel/ptrace.c 2004-08-30 22:02:56.000000000 -0700 @@ -55,6 +55,15 @@ void __ptrace_unlink(task_t *child) REMOVE_LINKS(child); child->parent = child->real_parent; SET_LINKS(child); + + if (child->state == TASK_TRACED) { + /* + * Turn a tracing stop into a normal stop now, + * since with no tracer there would be no way + * to wake it up with SIGCONT or SIGKILL. + */ + child->state = TASK_STOPPED; + } } /* @@ -62,20 +71,28 @@ void __ptrace_unlink(task_t *child) */ int ptrace_check_attach(struct task_struct *child, int kill) { - if (!(child->ptrace & PT_PTRACED)) - return -ESRCH; + int ret = -ESRCH; - if (child->parent != current) - return -ESRCH; + /* + * We take the read lock around doing both checks to close a + * possible race where someone else was tracing our child and + * detached between these two checks. After this locked check, + * we are sure that this is our traced child and that can only + * be changed by us so it's not changing right after this. + */ + read_lock(&tasklist_lock); + if ((child->ptrace & PT_PTRACED) && child->parent == current) + ret = 0; + read_unlock(&tasklist_lock); - if (!kill) { - if (child->state != TASK_STOPPED) + if (!ret && !kill) { + if (child->state != TASK_TRACED) return -ESRCH; wait_task_inactive(child); } /* All systems go.. */ - return 0; + return ret; } int ptrace_attach(struct task_struct *task) @@ -281,15 +298,13 @@ static int ptrace_setoptions(struct task static int ptrace_getsiginfo(struct task_struct *child, siginfo_t __user * data) { - if (child->last_siginfo == NULL) - return -EINVAL; + BUG_ON(child->last_siginfo == NULL); return copy_siginfo_to_user(data, child->last_siginfo); } static int ptrace_setsiginfo(struct task_struct *child, siginfo_t __user * data) { - if (child->last_siginfo == NULL) - return -EINVAL; + BUG_ON(child->last_siginfo == NULL); if (copy_from_user(child->last_siginfo, data, sizeof (siginfo_t)) != 0) return -EFAULT; return 0; @@ -322,24 +337,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 -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/kernel/sched.c linux-2.6.9-rc1-mm1+TASK_TRACED/kernel/sched.c --- linux-2.6.9-rc1-mm1/kernel/sched.c 2004-08-27 13:46:37.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/kernel/sched.c 2004-08-30 22:03:27.000000000 -0700 @@ -1174,7 +1174,7 @@ out: int fastcall wake_up_process(task_t * p) { - return try_to_wake_up(p, TASK_STOPPED | + return try_to_wake_up(p, TASK_STOPPED | TASK_TRACED | TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 0); } @@ -3493,7 +3493,7 @@ static void show_task(task_t * p) task_t *relative; unsigned state; unsigned long free = 0; - static const char *stat_nam[] = { "R", "S", "D", "T", "Z", "W" }; + static const char *stat_nam[] = { "R", "S", "D", "T", "t", "Z", "X" }; printk("%-13.13s ", p->comm); state = p->state ? __ffs(p->state) + 1 : 0; diff -rzBpu --exclude='.*' --exclude='*.o' linux-2.6.9-rc1-mm1/kernel/signal.c linux-2.6.9-rc1-mm1+TASK_TRACED/kernel/signal.c --- linux-2.6.9-rc1-mm1/kernel/signal.c 2004-08-27 13:46:37.000000000 -0700 +++ linux-2.6.9-rc1-mm1+TASK_TRACED/kernel/signal.c 2004-08-30 22:08:42.000000000 -0700 @@ -636,7 +636,8 @@ static int check_kill_permission(int sig /* forward decl */ static void do_notify_parent_cldstop(struct task_struct *tsk, - struct task_struct *parent); + struct task_struct *parent, + int why); /* * Handle magic process-wide effects of stop/continue signals. @@ -681,11 +682,13 @@ static void handle_stop_signal(int sig, p->signal->stop_state = 1; spin_unlock(&p->sighand->siglock); if (p->ptrace & PT_PTRACED) - do_notify_parent_cldstop(p, p->parent); + do_notify_parent_cldstop(p, p->parent, + CLD_STOPPED); else do_notify_parent_cldstop( p->group_leader, - p->group_leader->real_parent); + p->group_leader->real_parent, + CLD_STOPPED); spin_lock(&p->sighand->siglock); } rm_from_queue(SIG_KERNEL_STOP_MASK, &p->signal->shared_pending); @@ -727,11 +730,13 @@ static void handle_stop_signal(int sig, p->signal->group_exit_code = 0; spin_unlock(&p->sighand->siglock); if (p->ptrace & PT_PTRACED) - do_notify_parent_cldstop(p, p->parent); + do_notify_parent_cldstop(p, p->parent, + CLD_CONTINUED); else do_notify_parent_cldstop( p->group_leader, - p->group_leader->real_parent); + p->group_leader->real_parent, + CLD_CONTINUED); spin_lock(&p->sighand->siglock); } } @@ -899,11 +904,20 @@ force_sig_specific(int sig, struct task_ static void -__group_complete_signal(int sig, struct task_struct *p, unsigned int mask) +__group_complete_signal(int sig, struct task_struct *p) { + unsigned int mask; struct task_struct *t; /* + * Don't bother zombies and stopped tasks (but + * SIGKILL will punch through stopped state) + */ + mask = TASK_DEAD | TASK_ZOMBIE | TASK_TRACED; + if (sig != SIGKILL) + mask |= TASK_STOPPED; + + /* * Now find a thread we can wake up to take the signal off the queue. * * If the main thread wants the signal, it gets first crack. @@ -1004,7 +1018,6 @@ __group_complete_signal(int sig, struct static int __group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) { - unsigned int mask; int ret = 0; #ifdef CONFIG_SMP @@ -1028,14 +1041,6 @@ __group_send_sig_info(int sig, struct si return ret; /* - * Don't bother zombies and stopped tasks (but - * SIGKILL will punch through stopped state) - */ - mask = TASK_DEAD | TASK_ZOMBIE; - if (sig != SIGKILL) - mask |= TASK_STOPPED; - - /* * Put this signal on the shared-pending queue, or fail with EAGAIN. * We always use the shared queue for process-wide signals, * to avoid several races. @@ -1044,7 +1049,7 @@ __group_send_sig_info(int sig, struct si if (unlikely(ret)) return ret; - __group_complete_signal(sig, p, mask); + __group_complete_signal(sig, p); return 0; } @@ -1405,7 +1410,6 @@ int send_group_sigqueue(int sig, struct sigqueue *q, struct task_struct *p) { unsigned long flags; - unsigned int mask; int ret = 0; BUG_ON(!(q->flags & SIGQUEUE_PREALLOC)); @@ -1430,13 +1434,6 @@ send_group_sigqueue(int sig, struct sigq q->info.si_overrun++; goto out; } - /* - * Don't bother zombies and stopped tasks (but - * SIGKILL will punch through stopped state) - */ - mask = TASK_DEAD | TASK_ZOMBIE; - if (sig != SIGKILL) - mask |= TASK_STOPPED; /* * Put this signal on the shared-pending queue. @@ -1447,7 +1444,7 @@ send_group_sigqueue(int sig, struct sigq list_add_tail(&q->list, &p->signal->shared_pending.list); sigaddset(&p->signal->shared_pending.signal, sig); - __group_complete_signal(sig, p, mask); + __group_complete_signal(sig, p); out: spin_unlock_irqrestore(&p->sighand->siglock, flags); read_unlock(&tasklist_lock); @@ -1480,19 +1477,22 @@ static void __wake_up_parent(struct task } /* - * Let a parent know about a status change of a child. + * Let a parent know about the death of a child. + * For a stopped/continued status change, use do_notify_parent_cldstop instead. */ void do_notify_parent(struct task_struct *tsk, int sig) { struct siginfo info; unsigned long flags; - int why, status; struct sighand_struct *psig; if (sig == -1) BUG(); + /* do_notify_parent_cldstop should have been called instead. */ + BUG_ON(tsk->state & (TASK_STOPPED|TASK_TRACED)); + BUG_ON(!tsk->ptrace && (tsk->group_leader != tsk || !thread_group_empty(tsk))); @@ -1506,34 +1506,19 @@ void do_notify_parent(struct task_struct info.si_stime = tsk->stime + tsk->signal->stime; k_getrusage(tsk, RUSAGE_BOTH, &info.si_rusage); - status = tsk->exit_code & 0x7f; - why = SI_KERNEL; /* shouldn't happen */ - switch (tsk->state) { - case TASK_STOPPED: - /* FIXME -- can we deduce CLD_TRAPPED or CLD_CONTINUED? */ - if (tsk->ptrace & PT_PTRACED) - why = CLD_TRAPPED; - else - why = CLD_STOPPED; - break; - - default: - if (tsk->exit_code & 0x80) - why = CLD_DUMPED; - else if (tsk->exit_code & 0x7f) - why = CLD_KILLED; - else { - why = CLD_EXITED; - status = tsk->exit_code >> 8; - } - break; + info.si_status = tsk->exit_code & 0x7f; + if (tsk->exit_code & 0x80) + info.si_code = CLD_DUMPED; + else if (tsk->exit_code & 0x7f) + info.si_code = CLD_KILLED; + else { + info.si_code = CLD_EXITED; + info.si_status = tsk->exit_code >> 8; } - info.si_code = why; - info.si_status = status; psig = tsk->parent->sighand; spin_lock_irqsave(&psig->siglock, flags); - if (sig == SIGCHLD && tsk->state != TASK_STOPPED && + if (sig == SIGCHLD && (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN || (psig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT))) { /* @@ -1561,26 +1546,9 @@ 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 out "parent" pointer. - * - * exit.c calls "do_notify_parent()" directly, because - * it already has the tasklist lock. - */ -void -notify_parent(struct task_struct *tsk, int sig) -{ - if (sig != -1) { - read_lock(&tasklist_lock); - do_notify_parent(tsk, sig); - read_unlock(&tasklist_lock); - } -} - static void -do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent) +do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent, + int why) { struct siginfo info; unsigned long flags; @@ -1596,14 +1564,20 @@ do_notify_parent_cldstop(struct task_str info.si_stime = tsk->stime; k_getrusage(tsk, RUSAGE_BOTH, &info.si_rusage); - info.si_status = (tsk->signal ? tsk->signal->group_exit_code : - tsk->exit_code) & 0x7f; - if (info.si_status == 0) { - info.si_status = SIGCONT; - info.si_code = CLD_CONTINUED; - } else { - info.si_code = CLD_STOPPED; - } + info.si_code = why; + switch (why) { + case CLD_CONTINUED: + info.si_status = SIGCONT; + break; + case CLD_STOPPED: + info.si_status = tsk->signal->group_exit_code & 0x7f; + break; + case CLD_TRAPPED: + info.si_status = tsk->exit_code & 0x7f; + break; + default: + BUG(); + } sighand = parent->sighand; spin_lock_irqsave(&sighand->siglock, flags); @@ -1617,6 +1591,68 @@ do_notify_parent_cldstop(struct task_str spin_unlock_irqrestore(&sighand->siglock, flags); } +/* + * This must be called with current->sighand->siglock held. + * + * 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. + */ +static void ptrace_stop(int exit_code, 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; + current->exit_code = exit_code; + + /* Let the debugger run. */ + set_current_state(TASK_TRACED); + spin_unlock_irq(¤t->sighand->siglock); + read_lock(&tasklist_lock); + do_notify_parent_cldstop(current, current->parent, CLD_TRAPPED); + read_unlock(&tasklist_lock); + schedule(); + + /* + * We are back. Now reacquire the siglock before touching + * last_siginfo, so that we are sure to have synchronized with + * any signal-sending on another CPU that wants to examine it. + */ + spin_lock_irq(¤t->sighand->siglock); + current->last_siginfo = NULL; + + /* + * Queued signals ignored us while we were stopped for tracing. + * So check for any that we should take before resuming user mode. + */ + recalc_sigpending(); +} + +void ptrace_notify(int exit_code) +{ + siginfo_t info; + + BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP); + + 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. */ + spin_lock_irq(¤t->sighand->siglock); + ptrace_stop(exit_code, &info); + spin_unlock_irq(¤t->sighand->siglock); +} #ifndef HAVE_ARCH_GET_SIGNAL_TO_DELIVER @@ -1630,13 +1666,15 @@ finish_stop(int stop_count) */ if (stop_count < 0 || (current->ptrace & PT_PTRACED)) { read_lock(&tasklist_lock); - do_notify_parent_cldstop(current, current->parent); + do_notify_parent_cldstop(current, current->parent, + CLD_STOPPED); read_unlock(&tasklist_lock); } else if (stop_count == 0) { read_lock(&tasklist_lock); do_notify_parent_cldstop(current->group_leader, - current->group_leader->real_parent); + current->group_leader->real_parent, + CLD_STOPPED); read_unlock(&tasklist_lock); } @@ -1819,25 +1857,10 @@ 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_stop(signr, info); /* We're back. Did the debugger cancel the sig? */ - spin_lock_irq(¤t->sighand->siglock); signr = current->exit_code; if (signr == 0) continue; @@ -1968,7 +1991,7 @@ EXPORT_SYMBOL(kill_proc); EXPORT_SYMBOL(kill_proc_info); EXPORT_SYMBOL(kill_sl); EXPORT_SYMBOL(kill_sl_info); -EXPORT_SYMBOL(notify_parent); +EXPORT_SYMBOL(ptrace_notify); EXPORT_SYMBOL(send_sig); EXPORT_SYMBOL(send_sig_info); EXPORT_SYMBOL(send_group_sig_info); - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
|  |