diff options
| -rw-r--r-- | fs/binfmt_aout.c | 8 | ||||
| -rw-r--r-- | fs/binfmt_elf.c | 8 | ||||
| -rw-r--r-- | include/linux/ptrace.h | 12 | ||||
| -rw-r--r-- | include/linux/sched.h | 6 | ||||
| -rw-r--r-- | kernel/fork.c | 29 | ||||
| -rw-r--r-- | kernel/ptrace.c | 41 |
6 files changed, 98 insertions, 6 deletions
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index d6660b597fdb..a74808f670f1 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -425,8 +425,12 @@ beyond_if: regs->gp = ex.a_gpvalue; #endif start_thread(regs, ex.a_entry, current->mm->start_stack); - if (current->ptrace & PT_PTRACED) - send_sig(SIGTRAP, current, 0); + if (unlikely(current->ptrace & PT_PTRACED)) { + if (current->ptrace & PT_TRACE_EXEC) + ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP); + else + send_sig(SIGTRAP, current, 0); + } return 0; } diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index ced982676f53..ed3a456ca89c 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -792,8 +792,12 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) #endif start_thread(regs, elf_entry, bprm->p); - if (current->ptrace & PT_PTRACED) - send_sig(SIGTRAP, current, 0); + if (unlikely(current->ptrace & PT_PTRACED)) { + if (current->ptrace & PT_TRACE_EXEC) + ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP); + else + send_sig(SIGTRAP, current, 0); + } retval = 0; out: return retval; diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 8d4f8dcefbab..c6de3a4ea70a 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -25,9 +25,20 @@ /* 0x4200-0x4300 are reserved for architecture-independent additions. */ #define PTRACE_SETOPTIONS 0x4200 +#define PTRACE_GETEVENTMSG 0x4201 /* options set using PTRACE_SETOPTIONS */ #define PTRACE_O_TRACESYSGOOD 0x00000001 +#define PTRACE_O_TRACEFORK 0x00000002 +#define PTRACE_O_TRACEVFORK 0x00000004 +#define PTRACE_O_TRACECLONE 0x00000008 +#define PTRACE_O_TRACEEXEC 0x00000010 + +/* 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 #include <asm/ptrace.h> #include <linux/sched.h> @@ -39,6 +50,7 @@ extern int ptrace_detach(struct task_struct *, unsigned int); extern void ptrace_disable(struct task_struct *); 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_link(struct task_struct *child, struct task_struct *new_parent); extern void __ptrace_unlink(struct task_struct *child); diff --git a/include/linux/sched.h b/include/linux/sched.h index cb1f3cfd54ed..395722faaad4 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -392,6 +392,8 @@ struct task_struct { void *journal_info; struct dentry *proc_dentry; struct backing_dev_info *backing_dev_info; + + unsigned long ptrace_message; }; extern void __put_task_struct(struct task_struct *tsk); @@ -431,6 +433,10 @@ do { if (atomic_dec_and_test(&(tsk)->usage)) __put_task_struct(tsk); } while(0) #define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */ #define PT_TRACESYSGOOD 0x00000004 #define PT_PTRACE_CAP 0x00000008 /* ptracer can follow suid-exec */ +#define PT_TRACE_FORK 0x00000010 +#define PT_TRACE_VFORK 0x00000020 +#define PT_TRACE_CLONE 0x00000040 +#define PT_TRACE_EXEC 0x00000080 /* * Limit the stack by to some sane default: root can always diff --git a/kernel/fork.c b/kernel/fork.c index 2f5f00301182..0280a5996d1d 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -945,6 +945,22 @@ bad_fork_free: goto fork_out; } +static inline int fork_traceflag (unsigned clone_flags) +{ + if (clone_flags & (CLONE_UNTRACED | CLONE_IDLETASK)) + return 0; + else if (clone_flags & CLONE_VFORK) { + if (current->ptrace & PT_TRACE_VFORK) + return PTRACE_EVENT_VFORK; + } else if ((clone_flags & CSIGNAL) != SIGCHLD) { + if (current->ptrace & PT_TRACE_CLONE) + return PTRACE_EVENT_CLONE; + } else if (current->ptrace & PT_TRACE_FORK) + return PTRACE_EVENT_FORK; + + return 0; +} + /* * Ok, this is the main fork-routine. * @@ -958,6 +974,13 @@ struct task_struct *do_fork(unsigned long clone_flags, int *user_tid) { struct task_struct *p; + int trace = 0; + + if (unlikely(current->ptrace)) { + trace = fork_traceflag (clone_flags); + if (trace) + clone_flags |= CLONE_PTRACE; + } p = copy_process(clone_flags, stack_start, regs, stack_size, user_tid); if (!IS_ERR(p)) { @@ -973,6 +996,12 @@ struct task_struct *do_fork(unsigned long clone_flags, wake_up_forked_process(p); /* do this last */ ++total_forks; + + if (unlikely (trace)) { + current->ptrace_message = (unsigned long) p->pid; + ptrace_notify ((trace << 8) | SIGTRAP); + } + if (clone_flags & CLONE_VFORK) wait_for_completion(&vfork); else diff --git a/kernel/ptrace.c b/kernel/ptrace.c index ab3aa39a38cf..a2611977e53b 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -249,14 +249,37 @@ int ptrace_writedata(struct task_struct *tsk, char * src, unsigned long dst, int return copied; } -int ptrace_setoptions(struct task_struct *child, long data) +static int ptrace_setoptions(struct task_struct *child, long data) { if (data & PTRACE_O_TRACESYSGOOD) child->ptrace |= PT_TRACESYSGOOD; else child->ptrace &= ~PT_TRACESYSGOOD; - if ((data & PTRACE_O_TRACESYSGOOD) != data) + if (data & PTRACE_O_TRACEFORK) + child->ptrace |= PT_TRACE_FORK; + else + child->ptrace &= ~PT_TRACE_FORK; + + if (data & PTRACE_O_TRACEVFORK) + child->ptrace |= PT_TRACE_VFORK; + else + child->ptrace &= ~PT_TRACE_VFORK; + + if (data & PTRACE_O_TRACECLONE) + child->ptrace |= PT_TRACE_CLONE; + else + child->ptrace &= ~PT_TRACE_CLONE; + + if (data & PTRACE_O_TRACEEXEC) + child->ptrace |= PT_TRACE_EXEC; + else + child->ptrace &= ~PT_TRACE_EXEC; + + if ((data & (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK + | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE + | PTRACE_O_TRACEEXEC)) + != data) return -EINVAL; return 0; @@ -274,9 +297,23 @@ int ptrace_request(struct task_struct *child, long request, case PTRACE_SETOPTIONS: ret = ptrace_setoptions(child, data); break; + case PTRACE_GETEVENTMSG: + ret = put_user(child->ptrace_message, (unsigned long *) data); + break; default: break; } 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(); +} |
