From 98a3d3b1f9b82e88d4e4a338b20532c628b347e5 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Sat, 18 Jan 2003 08:21:37 -0500 Subject: Tweak has_stopped_jobs for use with debugging --- kernel/exit.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/kernel/exit.c b/kernel/exit.c index 743ed76ed243..03801540a5e6 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -203,6 +203,17 @@ static inline int has_stopped_jobs(int pgrp) for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) { if (p->state != TASK_STOPPED) continue; + + /* If p is stopped by a debugger on a signal that won't + stop it, then don't count p as stopped. This isn't + perfect but it's a good approximation. */ + if (unlikely (p->ptrace) + && p->exit_code != SIGSTOP + && p->exit_code != SIGTSTP + && p->exit_code != SIGTTOU + && p->exit_code != SIGTTIN) + continue; + retval = 1; break; } -- cgit v1.2.3 From 1669ce53e2ff7b49a60d0230866d3faee5f45573 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Sat, 18 Jan 2003 10:40:18 -0500 Subject: Add PTRACE_GETSIGINFO and PTRACE_SETSIGINFO These new ptrace commands allow a debugger to control signals more precisely; for instance, store a signal and deliver it later, as if it had come from the original outside process or in response to the same faulting memory access. --- include/linux/ptrace.h | 2 ++ include/linux/sched.h | 1 + kernel/ptrace.c | 23 +++++++++++++++++++++++ kernel/signal.c | 8 +++++++- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index c6de3a4ea70a..b56bbe7ca800 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -26,6 +26,8 @@ /* 0x4200-0x4300 are reserved for architecture-independent additions. */ #define PTRACE_SETOPTIONS 0x4200 #define PTRACE_GETEVENTMSG 0x4201 +#define PTRACE_GETSIGINFO 0x4202 +#define PTRACE_SETSIGINFO 0x4203 /* options set using PTRACE_SETOPTIONS */ #define PTRACE_O_TRACESYSGOOD 0x00000001 diff --git a/include/linux/sched.h b/include/linux/sched.h index 15a951d2d27e..a325e5a8c645 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -400,6 +400,7 @@ struct task_struct { struct backing_dev_info *backing_dev_info; unsigned long ptrace_message; + siginfo_t *last_siginfo; /* For ptrace use. */ }; extern void __put_task_struct(struct task_struct *tsk); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index a16dfb90d412..9f3769bfdc7e 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -286,6 +286,23 @@ static int ptrace_setoptions(struct task_struct *child, long data) return 0; } +static int ptrace_getsiginfo(struct task_struct *child, long data) +{ + if (child->last_siginfo == NULL) + return -EINVAL; + return copy_siginfo_to_user ((siginfo_t *) data, child->last_siginfo); +} + +static int ptrace_setsiginfo(struct task_struct *child, long data) +{ + if (child->last_siginfo == NULL) + return -EINVAL; + if (copy_from_user (child->last_siginfo, (siginfo_t *) data, + sizeof (siginfo_t)) != 0) + return -EFAULT; + return 0; +} + int ptrace_request(struct task_struct *child, long request, long addr, long data) { @@ -301,6 +318,12 @@ int ptrace_request(struct task_struct *child, long request, case PTRACE_GETEVENTMSG: ret = put_user(child->ptrace_message, (unsigned long *) data); break; + case PTRACE_GETSIGINFO: + ret = ptrace_getsiginfo(child, data); + break; + case PTRACE_SETSIGINFO: + ret = ptrace_setsiginfo(child, data); + break; default: break; } diff --git a/kernel/signal.c b/kernel/signal.c index 7c485d01a4b0..b683402178ec 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1244,10 +1244,13 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs) if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { /* Let the debugger run. */ current->exit_code = signr; + current->last_siginfo = info; set_current_state(TASK_STOPPED); notify_parent(current, SIGCHLD); schedule(); + current->last_siginfo = NULL; + /* We're back. Did the debugger cancel the sig? */ signr = current->exit_code; if (signr == 0) @@ -1258,7 +1261,10 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs) if (signr == SIGSTOP) continue; - /* Update the siginfo structure. Is this good? */ + /* Update the siginfo structure if the signal has + changed. If the debugger wanted something + specific in the siginfo structure then it should + have updated *info via PTRACE_SETSIGINFO. */ if (signr != info->si_signo) { info->si_signo = signr; info->si_errno = 0; -- cgit v1.2.3 From 762cc267d645943264363ce957c226976909b491 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Tue, 4 Feb 2003 07:05:49 -0500 Subject: Use force_sig_specific to send SIGSTOP to newly-created CLONE_PTRACE processes. --- kernel/fork.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/fork.c b/kernel/fork.c index 0ba9e64b5821..347f957b080e 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1036,7 +1036,7 @@ struct task_struct *do_fork(unsigned long clone_flags, } if (p->ptrace & PT_PTRACED) - send_sig(SIGSTOP, p, 1); + force_sig_specific(SIGSTOP, p); wake_up_forked_process(p); /* do this last */ ++total_forks; -- cgit v1.2.3 From 45c1a159b85b3b30afd26a77b4be312226bba416 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Thu, 6 Feb 2003 04:32:29 -0500 Subject: Add PTRACE_O_TRACEVFORKDONE and PTRACE_O_TRACEEXIT facilities. --- include/linux/ptrace.h | 4 ++++ include/linux/sched.h | 2 ++ kernel/exit.c | 3 +++ kernel/fork.c | 6 ++++-- kernel/ptrace.c | 13 ++++++++++++- 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index b56bbe7ca800..706b420fb5c9 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -35,12 +35,16 @@ #define PTRACE_O_TRACEVFORK 0x00000004 #define PTRACE_O_TRACECLONE 0x00000008 #define PTRACE_O_TRACEEXEC 0x00000010 +#define PTRACE_O_TRACEVFORKDONE 0x00000020 +#define PTRACE_O_TRACEEXIT 0x00000040 /* Wait extended result codes for the above trace options. */ #define PTRACE_EVENT_FORK 1 #define PTRACE_EVENT_VFORK 2 #define PTRACE_EVENT_CLONE 3 #define PTRACE_EVENT_EXEC 4 +#define PTRACE_EVENT_VFORK_DONE 5 +#define PTRACE_EVENT_EXIT 6 #include #include diff --git a/include/linux/sched.h b/include/linux/sched.h index a325e5a8c645..c424a353a748 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -441,6 +441,8 @@ do { if (atomic_dec_and_test(&(tsk)->usage)) __put_task_struct(tsk); } while(0) #define PT_TRACE_VFORK 0x00000020 #define PT_TRACE_CLONE 0x00000040 #define PT_TRACE_EXEC 0x00000080 +#define PT_TRACE_VFORK_DONE 0x00000100 +#define PT_TRACE_EXIT 0x00000200 #if CONFIG_SMP extern void set_cpus_allowed(task_t *p, unsigned long new_mask); diff --git a/kernel/exit.c b/kernel/exit.c index 03801540a5e6..5cb58a1d2075 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -653,6 +653,9 @@ NORET_TYPE void do_exit(long code) profile_exit_task(tsk); + if (unlikely(current->ptrace & PT_TRACE_EXIT)) + ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP); + fake_volatile: acct_process(code); __exit_mm(tsk); diff --git a/kernel/fork.c b/kernel/fork.c index 347f957b080e..3da82d978812 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1046,9 +1046,11 @@ struct task_struct *do_fork(unsigned long clone_flags, ptrace_notify ((trace << 8) | SIGTRAP); } - if (clone_flags & CLONE_VFORK) + if (clone_flags & CLONE_VFORK) { wait_for_completion(&vfork); - else + if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) + ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP); + } else /* * Let the child process run first, to avoid most of the * COW overhead when the child exec()s afterwards. diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 9f3769bfdc7e..14d158864d9e 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -277,9 +277,20 @@ static int ptrace_setoptions(struct task_struct *child, long data) else child->ptrace &= ~PT_TRACE_EXEC; + if (data & PTRACE_O_TRACEVFORKDONE) + child->ptrace |= PT_TRACE_VFORK_DONE; + else + child->ptrace &= ~PT_TRACE_VFORK_DONE; + + if (data & PTRACE_O_TRACEEXIT) + child->ptrace |= PT_TRACE_EXIT; + else + child->ptrace &= ~PT_TRACE_EXIT; + if ((data & (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE - | PTRACE_O_TRACEEXEC)) + | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT + | PTRACE_O_TRACEVFORKDONE)) != data) return -EINVAL; -- cgit v1.2.3 From a866697c6eadb9d46eff2c8ebac4dfe4834ce565 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Thu, 6 Feb 2003 06:44:58 -0500 Subject: Signal handling bugs for thread exit + ptrace --- kernel/exit.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/kernel/exit.c b/kernel/exit.c index 5cb58a1d2075..0846290aeea3 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -586,7 +586,7 @@ static void exit_notify(void) * is about to become orphaned. */ - t = current->parent; + t = current->real_parent; if ((t->pgrp != current->pgrp) && (t->session == current->session) && @@ -619,8 +619,16 @@ static void exit_notify(void) current->exit_signal = SIGCHLD; - if (current->exit_signal != -1) - do_notify_parent(current, current->exit_signal); + /* If something other than our normal parent is ptracing us, then + * send it a SIGCHLD instead of honoring exit_signal. exit_signal + * only has special meaning to our real parent. + */ + if (current->exit_signal != -1) { + if (current->parent == current->real_parent) + do_notify_parent(current, current->exit_signal); + else + do_notify_parent(current, SIGCHLD); + } current->state = TASK_ZOMBIE; /* @@ -877,7 +885,7 @@ repeat: if (p->real_parent != p->parent) { write_lock_irq(&tasklist_lock); __ptrace_unlink(p); - do_notify_parent(p, SIGCHLD); + do_notify_parent(p, p->exit_signal); p->state = TASK_ZOMBIE; write_unlock_irq(&tasklist_lock); } else -- cgit v1.2.3