summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/binfmt_aout.c8
-rw-r--r--fs/binfmt_elf.c8
-rw-r--r--include/linux/ptrace.h12
-rw-r--r--include/linux/sched.h6
-rw-r--r--kernel/fork.c29
-rw-r--r--kernel/ptrace.c41
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();
+}