Kyle McMartin 07d3322
implement utrace-ptrace
Kyle McMartin 07d3322
Kyle McMartin 07d3322
The patch adds the new file, kernel/ptrace-utrace.c, which contains
Kyle McMartin 07d3322
the new implementation of ptrace over utrace.
Kyle McMartin 07d3322
Kyle McMartin 07d3322
This file is not compiled until we have CONFIG_UTRACE option, will be
Kyle McMartin 07d3322
added by the next "utrace core" patch.
Kyle McMartin 07d3322
Kyle McMartin 07d3322
It's supposed to be an invisible implementation change, nothing should
Kyle McMartin 07d3322
change to userland when CONFIG_UTRACE is enabled.
Kyle McMartin 07d3322
Kyle McMartin 07d3322
Signed-off-by: Roland McGrath <roland@redhat.com>
Kyle McMartin 07d3322
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Kyle McMartin 07d3322
---
Kyle McMartin 71f70cd
 include/linux/ptrace.h |    1 +
Kyle McMartin 07d3322
 kernel/Makefile        |    1 +
Kyle McMartin 71f70cd
 kernel/ptrace-utrace.c | 1187 ++++++++++++++++++++++++++++++++++++++++++++++++
Kyle McMartin 71f70cd
 kernel/ptrace.c        |  690 ++++++++++++++--------------
Kyle McMartin 07d3322
 kernel/utrace.c        |   16 +
Kyle McMartin 71f70cd
 5 files changed, 1544 insertions(+), 351 deletions(-)
Kyle McMartin 07d3322
Kyle McMartin 07d3322
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
Kyle McMartin 71f70cd
index 619cdf0..e391bdb 100644  
Kyle McMartin 07d3322
--- a/include/linux/ptrace.h
Kyle McMartin 07d3322
+++ b/include/linux/ptrace.h
Kyle McMartin 71f70cd
@@ -100,6 +100,7 @@
Kyle McMartin 07d3322
 #include <linux/sched.h>		/* For struct task_struct.  */
Kyle McMartin 07d3322
 
Kyle McMartin 71f70cd
 
Kyle McMartin 07d3322
+extern void ptrace_notify_stop(struct task_struct *tracee);
Kyle McMartin 1aea232
 extern long arch_ptrace(struct task_struct *child, long request,
Kyle McMartin 1aea232
 			unsigned long addr, unsigned long data);
Kyle McMartin 07d3322
 extern int ptrace_traceme(void);
Kyle McMartin 07d3322
diff --git a/kernel/Makefile b/kernel/Makefile
Kyle McMartin 71f70cd
index 1172528..9a815a5 100644  
Kyle McMartin 07d3322
--- a/kernel/Makefile
Kyle McMartin 07d3322
+++ b/kernel/Makefile
Kyle McMartin 07d3322
@@ -71,6 +71,7 @@ obj-$(CONFIG_RESOURCE_COUNTERS) += res_c
Kyle McMartin 07d3322
 obj-$(CONFIG_SMP) += stop_machine.o
Kyle McMartin 07d3322
 obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
Kyle McMartin 07d3322
 obj-$(CONFIG_UTRACE) += utrace.o
Kyle McMartin 07d3322
+obj-$(CONFIG_UTRACE) += ptrace-utrace.o
Kyle McMartin 07d3322
 obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
Kyle McMartin 07d3322
 obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
Kyle McMartin 07d3322
 obj-$(CONFIG_AUDIT_WATCH) += audit_watch.o
Kyle McMartin 07d3322
diff --git a/kernel/ptrace-utrace.c b/kernel/ptrace-utrace.c
Kyle McMartin 07d3322
new file mode 100644
Kyle McMartin 71f70cd
index ...a5bcb9e 100644  
Kyle McMartin 07d3322
--- /dev/null
Kyle McMartin 07d3322
+++ b/kernel/ptrace-utrace.c
Kyle McMartin a135357
@@ -0,0 +1,1186 @@
Kyle McMartin 07d3322
+/*
Kyle McMartin 07d3322
+ * linux/kernel/ptrace.c
Kyle McMartin 07d3322
+ *
Kyle McMartin 07d3322
+ * (C) Copyright 1999 Linus Torvalds
Kyle McMartin 07d3322
+ *
Kyle McMartin 07d3322
+ * Common interfaces for "ptrace()" which we do not want
Kyle McMartin 07d3322
+ * to continually duplicate across every architecture.
Kyle McMartin 07d3322
+ */
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+#include <linux/capability.h>
Kyle McMartin 07d3322
+#include <linux/module.h>
Kyle McMartin 07d3322
+#include <linux/sched.h>
Kyle McMartin 07d3322
+#include <linux/errno.h>
Kyle McMartin 07d3322
+#include <linux/mm.h>
Kyle McMartin 07d3322
+#include <linux/highmem.h>
Kyle McMartin 07d3322
+#include <linux/pagemap.h>
Kyle McMartin 07d3322
+#include <linux/ptrace.h>
Kyle McMartin 07d3322
+#include <linux/utrace.h>
Kyle McMartin 07d3322
+#include <linux/security.h>
Kyle McMartin 07d3322
+#include <linux/signal.h>
Kyle McMartin 07d3322
+#include <linux/audit.h>
Kyle McMartin 07d3322
+#include <linux/pid_namespace.h>
Kyle McMartin 07d3322
+#include <linux/syscalls.h>
Kyle McMartin 07d3322
+#include <linux/uaccess.h>
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+/*
Kyle McMartin 07d3322
+ * ptrace a task: make the debugger its new parent and
Kyle McMartin 07d3322
+ * move it to the ptrace list.
Kyle McMartin 07d3322
+ *
Kyle McMartin 07d3322
+ * Must be called with the tasklist lock write-held.
Kyle McMartin 07d3322
+ */
Kyle McMartin 07d3322
+void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	BUG_ON(!list_empty(&child->ptrace_entry));
Kyle McMartin 07d3322
+	list_add(&child->ptrace_entry, &new_parent->ptraced);
Kyle McMartin 07d3322
+	child->parent = new_parent;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+/*
Kyle McMartin 07d3322
+ * unptrace a task: move it back to its original parent and
Kyle McMartin 07d3322
+ * remove it from the ptrace list.
Kyle McMartin 07d3322
+ *
Kyle McMartin 07d3322
+ * Must be called with the tasklist lock write-held.
Kyle McMartin 07d3322
+ */
Kyle McMartin 07d3322
+void __ptrace_unlink(struct task_struct *child)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	BUG_ON(!child->ptrace);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	child->ptrace = 0;
Kyle McMartin 07d3322
+	child->parent = child->real_parent;
Kyle McMartin 07d3322
+	list_del_init(&child->ptrace_entry);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+struct ptrace_context {
Kyle McMartin 07d3322
+	int				options;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	int				signr;
Kyle McMartin 07d3322
+	siginfo_t			*siginfo;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	int				stop_code;
Kyle McMartin 07d3322
+	unsigned long			eventmsg;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	enum utrace_resume_action	resume;
Kyle McMartin 07d3322
+};
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+#define PT_UTRACED			0x00001000
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+#define PTRACE_O_SYSEMU			0x100
Kyle McMartin 71f70cd
+#define PTRACE_O_DETACHED		0x200
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+#define PTRACE_EVENT_SYSCALL		(1 << 16)
Kyle McMartin 07d3322
+#define PTRACE_EVENT_SIGTRAP		(2 << 16)
Kyle McMartin 07d3322
+#define PTRACE_EVENT_SIGNAL		(3 << 16)
Kyle McMartin 07d3322
+/* events visible to user-space */
Kyle McMartin 07d3322
+#define PTRACE_EVENT_MASK		0xFFFF
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static inline bool ptrace_event_pending(struct ptrace_context *ctx)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	return ctx->stop_code != 0;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static inline int get_stop_event(struct ptrace_context *ctx)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	return ctx->stop_code >> 8;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static inline void set_stop_code(struct ptrace_context *ctx, int event)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	ctx->stop_code = (event << 8) | SIGTRAP;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static inline struct ptrace_context *
Kyle McMartin 07d3322
+ptrace_context(struct utrace_engine *engine)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	return engine->data;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static const struct utrace_engine_ops ptrace_utrace_ops; /* forward decl */
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static struct utrace_engine *ptrace_lookup_engine(struct task_struct *tracee)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	return utrace_attach_task(tracee, UTRACE_ATTACH_MATCH_OPS,
Kyle McMartin 07d3322
+					&ptrace_utrace_ops, NULL);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 71f70cd
+static int utrace_barrier_uninterruptible(struct task_struct *target,
Kyle McMartin 71f70cd
+					struct utrace_engine *engine)
Kyle McMartin 71f70cd
+{
Kyle McMartin 71f70cd
+	for (;;) {
Kyle McMartin 71f70cd
+		int err = utrace_barrier(target, engine);
Kyle McMartin 71f70cd
+
Kyle McMartin 71f70cd
+		if (err != -ERESTARTSYS)
Kyle McMartin 71f70cd
+			return err;
Kyle McMartin 71f70cd
+
Kyle McMartin 71f70cd
+		schedule_timeout_uninterruptible(1);
Kyle McMartin 71f70cd
+	}
Kyle McMartin 71f70cd
+}
Kyle McMartin 71f70cd
+
Kyle McMartin 07d3322
+static struct utrace_engine *
Kyle McMartin 07d3322
+ptrace_reuse_engine(struct task_struct *tracee)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct utrace_engine *engine;
Kyle McMartin 07d3322
+	struct ptrace_context *ctx;
Kyle McMartin 07d3322
+	int err = -EPERM;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	engine = ptrace_lookup_engine(tracee);
Kyle McMartin 07d3322
+	if (IS_ERR(engine))
Kyle McMartin 07d3322
+		return engine;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ctx = ptrace_context(engine);
Kyle McMartin 71f70cd
+	if (unlikely(ctx->options == PTRACE_O_DETACHED)) {
Kyle McMartin 07d3322
+		/*
Kyle McMartin 07d3322
+		 * Try to reuse this self-detaching engine.
Kyle McMartin 07d3322
+		 * The only caller which can hit this case is ptrace_attach(),
Kyle McMartin 07d3322
+		 * it holds ->cred_guard_mutex.
Kyle McMartin 07d3322
+		 */
Kyle McMartin 07d3322
+		ctx->options = 0;
Kyle McMartin 07d3322
+		ctx->eventmsg = 0;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		/* make sure we don't get unwanted reports */
Kyle McMartin 07d3322
+		err = utrace_set_events(tracee, engine, UTRACE_EVENT(QUIESCE));
Kyle McMartin 07d3322
+		if (!err || err == -EINPROGRESS) {
Kyle McMartin 07d3322
+			ctx->resume = UTRACE_RESUME;
Kyle McMartin 07d3322
+			/* synchronize with ptrace_report_signal() */
Kyle McMartin 71f70cd
+			err = utrace_barrier_uninterruptible(tracee, engine);
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+
Kyle McMartin 71f70cd
+		if (!err) {
Kyle McMartin 71f70cd
+			WARN_ON(engine->ops != &ptrace_utrace_ops &&
Kyle McMartin 71f70cd
+				!tracee->exit_state);
Kyle McMartin 07d3322
+			return engine;
Kyle McMartin 71f70cd
+		}
Kyle McMartin 71f70cd
+
Kyle McMartin 71f70cd
+		WARN_ON(engine->ops == &ptrace_utrace_ops);
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	utrace_engine_put(engine);
Kyle McMartin 07d3322
+	return ERR_PTR(err);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static struct utrace_engine *
Kyle McMartin 07d3322
+ptrace_attach_engine(struct task_struct *tracee)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct utrace_engine *engine;
Kyle McMartin 07d3322
+	struct ptrace_context *ctx;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (unlikely(task_utrace_flags(tracee))) {
Kyle McMartin 07d3322
+		engine = ptrace_reuse_engine(tracee);
Kyle McMartin 07d3322
+		if (!IS_ERR(engine) || IS_ERR(engine) == -EPERM)
Kyle McMartin 07d3322
+			return engine;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
Kyle McMartin 07d3322
+	if (unlikely(!ctx))
Kyle McMartin 07d3322
+		return ERR_PTR(-ENOMEM);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ctx->resume = UTRACE_RESUME;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	engine = utrace_attach_task(tracee, UTRACE_ATTACH_CREATE |
Kyle McMartin 07d3322
+						UTRACE_ATTACH_EXCLUSIVE |
Kyle McMartin 07d3322
+						UTRACE_ATTACH_MATCH_OPS,
Kyle McMartin 07d3322
+						&ptrace_utrace_ops, ctx);
Kyle McMartin 07d3322
+	if (unlikely(IS_ERR(engine))) {
Kyle McMartin 07d3322
+		if (engine != ERR_PTR(-ESRCH) &&
Kyle McMartin 07d3322
+		    engine != ERR_PTR(-ERESTARTNOINTR))
Kyle McMartin 07d3322
+			engine = ERR_PTR(-EPERM);
Kyle McMartin 07d3322
+		kfree(ctx);
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	return engine;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static inline int ptrace_set_events(struct task_struct *target,
Kyle McMartin 07d3322
+					struct utrace_engine *engine,
Kyle McMartin 07d3322
+					unsigned long options)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct ptrace_context *ctx = ptrace_context(engine);
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * We need QUIESCE for resume handling, CLONE to check
Kyle McMartin 07d3322
+	 * for CLONE_PTRACE, other events are always reported.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	unsigned long events = UTRACE_EVENT(QUIESCE) | UTRACE_EVENT(CLONE) |
Kyle McMartin 07d3322
+			       UTRACE_EVENT(EXEC) | UTRACE_EVENT_SIGNAL_ALL;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ctx->options = options;
Kyle McMartin 07d3322
+	if (options & PTRACE_O_TRACEEXIT)
Kyle McMartin 07d3322
+		events |= UTRACE_EVENT(EXIT);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	return utrace_set_events(target, engine, events);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+/*
Kyle McMartin 07d3322
+ * Attach a utrace engine for ptrace and set up its event mask.
Kyle McMartin 07d3322
+ * Returns error code or 0 on success.
Kyle McMartin 07d3322
+ */
Kyle McMartin 07d3322
+static int ptrace_attach_task(struct task_struct *tracee, int options)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct utrace_engine *engine;
Kyle McMartin 07d3322
+	int err;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	engine = ptrace_attach_engine(tracee);
Kyle McMartin 07d3322
+	if (IS_ERR(engine))
Kyle McMartin 07d3322
+		return PTR_ERR(engine);
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * It can fail only if the tracee is dead, the caller
Kyle McMartin 07d3322
+	 * must notice this before setting PT_UTRACED.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	err = ptrace_set_events(tracee, engine, options);
Kyle McMartin 07d3322
+	WARN_ON(err && !tracee->exit_state);
Kyle McMartin 07d3322
+	utrace_engine_put(engine);
Kyle McMartin 07d3322
+	return 0;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static int ptrace_wake_up(struct task_struct *tracee,
Kyle McMartin 07d3322
+				struct utrace_engine *engine,
Kyle McMartin 07d3322
+				enum utrace_resume_action action,
Kyle McMartin 07d3322
+				bool force_wakeup)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	if (force_wakeup) {
Kyle McMartin 07d3322
+		unsigned long flags;
Kyle McMartin 07d3322
+		/*
Kyle McMartin 07d3322
+		 * Preserve the compatibility bug. Historically ptrace
Kyle McMartin 07d3322
+		 * wakes up the tracee even if it should not. Clear
Kyle McMartin 07d3322
+		 * SIGNAL_STOP_STOPPED for utrace_wakeup().
Kyle McMartin 07d3322
+		 */
Kyle McMartin 07d3322
+		if (lock_task_sighand(tracee, &flags)) {
Kyle McMartin 07d3322
+			tracee->signal->flags &= ~SIGNAL_STOP_STOPPED;
Kyle McMartin 07d3322
+			unlock_task_sighand(tracee, &flags);
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (action != UTRACE_REPORT)
Kyle McMartin 07d3322
+		ptrace_context(engine)->stop_code = 0;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	return utrace_control(tracee, engine, action);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static void ptrace_detach_task(struct task_struct *tracee, int sig)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * If true, the caller is PTRACE_DETACH, otherwise
Kyle McMartin 07d3322
+	 * the tracer detaches implicitly during exit.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 71f70cd
+	bool explicit = (sig >= 0);
Kyle McMartin 07d3322
+	struct utrace_engine *engine = ptrace_lookup_engine(tracee);
Kyle McMartin 07d3322
+	enum utrace_resume_action action = UTRACE_DETACH;
Kyle McMartin 71f70cd
+	struct ptrace_context *ctx;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (unlikely(IS_ERR(engine)))
Kyle McMartin 07d3322
+		return;
Kyle McMartin 07d3322
+
Kyle McMartin 71f70cd
+	ctx = ptrace_context(engine);
Kyle McMartin 71f70cd
+
Kyle McMartin 71f70cd
+	if (!explicit) {
Kyle McMartin 71f70cd
+		int err;
Kyle McMartin 71f70cd
+
Kyle McMartin 71f70cd
+		/*
Kyle McMartin 71f70cd
+		 * We are going to detach, the tracee can be running.
Kyle McMartin 71f70cd
+		 * Ensure ptrace_report_signal() won't report a signal.
Kyle McMartin 71f70cd
+		 */
Kyle McMartin 71f70cd
+		ctx->resume = UTRACE_DETACH;
Kyle McMartin 71f70cd
+		err = utrace_barrier_uninterruptible(tracee, engine);
Kyle McMartin 71f70cd
+
Kyle McMartin 71f70cd
+		if (!err && ctx->siginfo) {
Kyle McMartin 71f70cd
+			/*
Kyle McMartin 71f70cd
+			 * The tracee has already reported a signal
Kyle McMartin 71f70cd
+			 * before utrace_barrier().
Kyle McMartin 71f70cd
+			 *
Kyle McMartin 71f70cd
+			 * Resume it like we do in PTRACE_EVENT_SIGNAL
Kyle McMartin 71f70cd
+			 * case below. The difference is that we can race
Kyle McMartin 71f70cd
+			 * with ptrace_report_signal() if the tracee is
Kyle McMartin 71f70cd
+			 * running but this doesn't matter. In any case
Kyle McMartin 71f70cd
+			 * UTRACE_SIGNAL_REPORT must be pending and it
Kyle McMartin 71f70cd
+			 * can return nothing but UTRACE_DETACH.
Kyle McMartin 71f70cd
+			 */
Kyle McMartin 71f70cd
+			action = UTRACE_RESUME;
Kyle McMartin 71f70cd
+		}
Kyle McMartin 07d3322
+
Kyle McMartin 71f70cd
+	} else if (sig) {
Kyle McMartin 07d3322
+		switch (get_stop_event(ctx)) {
Kyle McMartin 07d3322
+		case PTRACE_EVENT_SYSCALL:
Kyle McMartin 71f70cd
+			send_sig_info(sig, SEND_SIG_PRIV, tracee);
Kyle McMartin 07d3322
+			break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		case PTRACE_EVENT_SIGNAL:
Kyle McMartin 71f70cd
+			ctx->signr = sig;
Kyle McMartin 07d3322
+			ctx->resume = UTRACE_DETACH;
Kyle McMartin 07d3322
+			action = UTRACE_RESUME;
Kyle McMartin 07d3322
+			break;
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 71f70cd
+	ptrace_wake_up(tracee, engine, action, explicit);
Kyle McMartin 71f70cd
+
Kyle McMartin 71f70cd
+	if (action != UTRACE_DETACH)
Kyle McMartin 71f70cd
+		ctx->options = PTRACE_O_DETACHED;
Kyle McMartin 71f70cd
+
Kyle McMartin 07d3322
+	utrace_engine_put(engine);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static void ptrace_abort_attach(struct task_struct *tracee)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	ptrace_detach_task(tracee, 0);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static u32 ptrace_report_exit(u32 action, struct utrace_engine *engine,
Kyle McMartin 07d3322
+			      long orig_code, long *code)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct ptrace_context *ctx = ptrace_context(engine);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	WARN_ON(ptrace_event_pending(ctx) &&
Kyle McMartin 07d3322
+		!signal_group_exit(current->signal));
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	set_stop_code(ctx, PTRACE_EVENT_EXIT);
Kyle McMartin 07d3322
+	ctx->eventmsg = *code;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	return UTRACE_STOP;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static void ptrace_clone_attach(struct task_struct *child,
Kyle McMartin 07d3322
+				int options)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct task_struct *parent = current;
Kyle McMartin 07d3322
+	struct task_struct *tracer;
Kyle McMartin 07d3322
+	bool abort = true;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (unlikely(ptrace_attach_task(child, options))) {
Kyle McMartin 07d3322
+		WARN_ON(1);
Kyle McMartin 07d3322
+		return;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	write_lock_irq(&tasklist_lock);
Kyle McMartin 07d3322
+	tracer = parent->parent;
Kyle McMartin 07d3322
+	if (!(tracer->flags & PF_EXITING) && parent->ptrace) {
Kyle McMartin 07d3322
+		child->ptrace = parent->ptrace;
Kyle McMartin 07d3322
+		__ptrace_link(child, tracer);
Kyle McMartin 07d3322
+		abort = false;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+	write_unlock_irq(&tasklist_lock);
Kyle McMartin 07d3322
+	if (unlikely(abort)) {
Kyle McMartin 07d3322
+		ptrace_abort_attach(child);
Kyle McMartin 07d3322
+		return;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	sigaddset(&child->pending.signal, SIGSTOP);
Kyle McMartin 07d3322
+	set_tsk_thread_flag(child, TIF_SIGPENDING);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static u32 ptrace_report_clone(u32 action, struct utrace_engine *engine,
Kyle McMartin 07d3322
+			       unsigned long clone_flags,
Kyle McMartin 07d3322
+			       struct task_struct *child)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct ptrace_context *ctx = ptrace_context(engine);
Kyle McMartin 07d3322
+	int event = 0;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	WARN_ON(ptrace_event_pending(ctx));
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (clone_flags & CLONE_UNTRACED) {
Kyle McMartin 07d3322
+		/* no events reported */
Kyle McMartin 07d3322
+	} else if (clone_flags & CLONE_VFORK) {
Kyle McMartin 07d3322
+		if (ctx->options & PTRACE_O_TRACEVFORK)
Kyle McMartin 07d3322
+			event = PTRACE_EVENT_VFORK;
Kyle McMartin 07d3322
+		else if (ctx->options & PTRACE_O_TRACEVFORKDONE)
Kyle McMartin 07d3322
+			event = PTRACE_EVENT_VFORK_DONE;
Kyle McMartin 07d3322
+	} else if ((clone_flags & CSIGNAL) != SIGCHLD) {
Kyle McMartin 07d3322
+		if (ctx->options & PTRACE_O_TRACECLONE)
Kyle McMartin 07d3322
+			event = PTRACE_EVENT_CLONE;
Kyle McMartin 07d3322
+	} else if (ctx->options & PTRACE_O_TRACEFORK) {
Kyle McMartin 07d3322
+		event = PTRACE_EVENT_FORK;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * Any of these reports implies auto-attaching the new child.
Kyle McMartin 07d3322
+	 * So does CLONE_PTRACE, even with no event to report.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	if ((event && event != PTRACE_EVENT_VFORK_DONE) ||
Kyle McMartin 07d3322
+				(clone_flags & CLONE_PTRACE))
Kyle McMartin 07d3322
+		ptrace_clone_attach(child, ctx->options);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (!event)
Kyle McMartin 07d3322
+		return UTRACE_RESUME;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	set_stop_code(ctx, event);
Kyle McMartin 07d3322
+	ctx->eventmsg = child->pid;
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * We shouldn't stop now, inside the do_fork() path.
Kyle McMartin 07d3322
+	 * We will stop later, before return to user-mode.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	if (event == PTRACE_EVENT_VFORK_DONE)
Kyle McMartin 07d3322
+		return UTRACE_REPORT;
Kyle McMartin 07d3322
+	else
Kyle McMartin 07d3322
+		return UTRACE_STOP;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static inline void set_syscall_code(struct ptrace_context *ctx)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	set_stop_code(ctx, PTRACE_EVENT_SYSCALL);
Kyle McMartin 07d3322
+	if (ctx->options & PTRACE_O_TRACESYSGOOD)
Kyle McMartin 07d3322
+		ctx->stop_code |= 0x80;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static u32 ptrace_report_syscall_entry(u32 action, struct utrace_engine *engine,
Kyle McMartin 07d3322
+				       struct pt_regs *regs)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct ptrace_context *ctx = ptrace_context(engine);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (action & UTRACE_SYSCALL_RESUMED) {
Kyle McMartin 07d3322
+		/*
Kyle McMartin 07d3322
+		 * We already reported the first time.
Kyle McMartin 07d3322
+		 * Nothing more to do now.
Kyle McMartin 07d3322
+		 */
Kyle McMartin 07d3322
+		if (unlikely(ctx->options & PTRACE_O_SYSEMU))
Kyle McMartin 07d3322
+			return UTRACE_SYSCALL_ABORT | UTRACE_REPORT;
Kyle McMartin 07d3322
+		return utrace_syscall_action(action) | UTRACE_RESUME;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	WARN_ON(ptrace_event_pending(ctx));
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	set_syscall_code(ctx);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (unlikely(ctx->options & PTRACE_O_SYSEMU))
Kyle McMartin 07d3322
+		return UTRACE_SYSCALL_ABORT | UTRACE_REPORT;
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * Stop now to report.  We will get another callback after
Kyle McMartin 07d3322
+	 * we resume, with the UTRACE_SYSCALL_RESUMED flag set.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	return UTRACE_SYSCALL_RUN | UTRACE_STOP;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 71f70cd
+static inline bool is_step_resume(enum utrace_resume_action resume)
Kyle McMartin 71f70cd
+{
Kyle McMartin 71f70cd
+	return resume == UTRACE_BLOCKSTEP || resume == UTRACE_SINGLESTEP;
Kyle McMartin 71f70cd
+}
Kyle McMartin 71f70cd
+
Kyle McMartin 07d3322
+static u32 ptrace_report_syscall_exit(u32 action, struct utrace_engine *engine,
Kyle McMartin 07d3322
+				      struct pt_regs *regs)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct ptrace_context *ctx = ptrace_context(engine);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (ptrace_event_pending(ctx))
Kyle McMartin 07d3322
+		return UTRACE_STOP;
Kyle McMartin 07d3322
+
Kyle McMartin 71f70cd
+	if (is_step_resume(ctx->resume)) {
Kyle McMartin 07d3322
+		ctx->signr = SIGTRAP;
Kyle McMartin 07d3322
+		return UTRACE_INTERRUPT;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	set_syscall_code(ctx);
Kyle McMartin 07d3322
+	return UTRACE_STOP;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static u32 ptrace_report_exec(u32 action, struct utrace_engine *engine,
Kyle McMartin 07d3322
+			      const struct linux_binfmt *fmt,
Kyle McMartin 07d3322
+			      const struct linux_binprm *bprm,
Kyle McMartin 07d3322
+			      struct pt_regs *regs)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct ptrace_context *ctx = ptrace_context(engine);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	WARN_ON(ptrace_event_pending(ctx));
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (!(ctx->options & PTRACE_O_TRACEEXEC)) {
Kyle McMartin 07d3322
+		/*
Kyle McMartin 07d3322
+		 * Old-fashioned ptrace'd exec just posts a plain signal.
Kyle McMartin 07d3322
+		 */
Kyle McMartin 07d3322
+		send_sig(SIGTRAP, current, 0);
Kyle McMartin 07d3322
+		return UTRACE_RESUME;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	set_stop_code(ctx, PTRACE_EVENT_EXEC);
Kyle McMartin 07d3322
+	return UTRACE_STOP;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static enum utrace_signal_action resume_signal(struct ptrace_context *ctx,
Kyle McMartin 07d3322
+					       struct k_sigaction *return_ka)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	siginfo_t *info = ctx->siginfo;
Kyle McMartin 07d3322
+	int signr = ctx->signr;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ctx->siginfo = NULL;
Kyle McMartin 07d3322
+	ctx->signr = 0;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	/* Did the debugger cancel the sig? */
Kyle McMartin 07d3322
+	if (!signr)
Kyle McMartin 07d3322
+		return UTRACE_SIGNAL_IGN;
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * Update the siginfo structure if the signal has changed.
Kyle McMartin 07d3322
+	 * If the debugger wanted something specific in the siginfo
Kyle McMartin 07d3322
+	 * then it should have updated *info via PTRACE_SETSIGINFO.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	if (info->si_signo != signr) {
Kyle McMartin 07d3322
+		info->si_signo = signr;
Kyle McMartin 07d3322
+		info->si_errno = 0;
Kyle McMartin 07d3322
+		info->si_code = SI_USER;
Kyle McMartin 07d3322
+		info->si_pid = task_pid_vnr(current->parent);
Kyle McMartin 07d3322
+		info->si_uid = task_uid(current->parent);
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	/* If the (new) signal is now blocked, requeue it. */
Kyle McMartin 07d3322
+	if (sigismember(&current->blocked, signr)) {
Kyle McMartin 07d3322
+		send_sig_info(signr, info, current);
Kyle McMartin 07d3322
+		return UTRACE_SIGNAL_IGN;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	spin_lock_irq(&current->sighand->siglock);
Kyle McMartin 07d3322
+	*return_ka = current->sighand->action[signr - 1];
Kyle McMartin 07d3322
+	spin_unlock_irq(&current->sighand->siglock);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	return UTRACE_SIGNAL_DELIVER;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static u32 ptrace_report_signal(u32 action, struct utrace_engine *engine,
Kyle McMartin 07d3322
+				struct pt_regs *regs,
Kyle McMartin 07d3322
+				siginfo_t *info,
Kyle McMartin 07d3322
+				const struct k_sigaction *orig_ka,
Kyle McMartin 07d3322
+				struct k_sigaction *return_ka)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct ptrace_context *ctx = ptrace_context(engine);
Kyle McMartin 07d3322
+	enum utrace_resume_action resume = ctx->resume;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (ptrace_event_pending(ctx)) {
Kyle McMartin 07d3322
+		action = utrace_signal_action(action);
Kyle McMartin 07d3322
+		WARN_ON(action != UTRACE_SIGNAL_REPORT);
Kyle McMartin 07d3322
+		return action | UTRACE_STOP;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	switch (utrace_signal_action(action)) {
Kyle McMartin 07d3322
+	case UTRACE_SIGNAL_HANDLER:
Kyle McMartin 07d3322
+		if (WARN_ON(ctx->siginfo))
Kyle McMartin 07d3322
+			ctx->siginfo = NULL;
Kyle McMartin 07d3322
+
Kyle McMartin 71f70cd
+		if (is_step_resume(resume)) {
Kyle McMartin 07d3322
+			set_stop_code(ctx, PTRACE_EVENT_SIGTRAP);
Kyle McMartin 07d3322
+			return UTRACE_STOP | UTRACE_SIGNAL_IGN;
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case UTRACE_SIGNAL_REPORT:
Kyle McMartin 07d3322
+		if (!ctx->siginfo) {
Kyle McMartin 07d3322
+			if (ctx->signr) {
Kyle McMartin 07d3322
+				/* set by ptrace_resume(SYSCALL_EXIT) */
Kyle McMartin 07d3322
+				WARN_ON(ctx->signr != SIGTRAP);
Kyle McMartin 07d3322
+				user_single_step_siginfo(current, regs, info);
Kyle McMartin 07d3322
+				force_sig_info(SIGTRAP, info, current);
Kyle McMartin 07d3322
+			}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+			return resume | UTRACE_SIGNAL_IGN;
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		if (WARN_ON(ctx->siginfo != info))
Kyle McMartin 07d3322
+			return resume | UTRACE_SIGNAL_IGN;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		return resume | resume_signal(ctx, return_ka);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	default:
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	WARN_ON(ctx->siginfo);
Kyle McMartin 71f70cd
+
Kyle McMartin 71f70cd
+	/* Raced with the exiting tracer ? */
Kyle McMartin 71f70cd
+	if (resume == UTRACE_DETACH)
Kyle McMartin 71f70cd
+		return action;
Kyle McMartin 71f70cd
+
Kyle McMartin 07d3322
+	ctx->siginfo = info;
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * ctx->siginfo points to the caller's stack.
Kyle McMartin 07d3322
+	 * Make sure the subsequent UTRACE_SIGNAL_REPORT clears
Kyle McMartin 07d3322
+	 * ->siginfo before return from get_signal_to_deliver().
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	if (utrace_control(current, engine, UTRACE_INTERRUPT))
Kyle McMartin 07d3322
+		WARN_ON(1);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ctx->signr = info->si_signo;
Kyle McMartin 07d3322
+	ctx->stop_code = (PTRACE_EVENT_SIGNAL << 8) | ctx->signr;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	return UTRACE_STOP | UTRACE_SIGNAL_IGN;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static u32 ptrace_report_quiesce(u32 action, struct utrace_engine *engine,
Kyle McMartin 07d3322
+				 unsigned long event)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct ptrace_context *ctx = ptrace_context(engine);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (ptrace_event_pending(ctx))
Kyle McMartin 07d3322
+		return UTRACE_STOP;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	return event ? UTRACE_RESUME : ctx->resume;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static void ptrace_release(void *data)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	kfree(data);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static const struct utrace_engine_ops ptrace_utrace_ops = {
Kyle McMartin 07d3322
+	.report_signal = ptrace_report_signal,
Kyle McMartin 07d3322
+	.report_quiesce = ptrace_report_quiesce,
Kyle McMartin 07d3322
+	.report_exec = ptrace_report_exec,
Kyle McMartin 07d3322
+	.report_exit = ptrace_report_exit,
Kyle McMartin 07d3322
+	.report_clone = ptrace_report_clone,
Kyle McMartin 07d3322
+	.report_syscall_entry = ptrace_report_syscall_entry,
Kyle McMartin 07d3322
+	.report_syscall_exit = ptrace_report_syscall_exit,
Kyle McMartin 07d3322
+	.release = ptrace_release,
Kyle McMartin 07d3322
+};
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+int ptrace_check_attach(struct task_struct *child, int kill)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct utrace_engine *engine;
Kyle McMartin 07d3322
+	struct utrace_examiner exam;
Kyle McMartin 07d3322
+	int ret = -ESRCH;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	engine = ptrace_lookup_engine(child);
Kyle McMartin 07d3322
+	if (IS_ERR(engine))
Kyle McMartin 07d3322
+		return ret;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (child->parent != current)
Kyle McMartin 07d3322
+		goto out;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (unlikely(kill))
Kyle McMartin 07d3322
+		ret = 0;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (!task_is_stopped_or_traced(child))
Kyle McMartin 07d3322
+		goto out;
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * Make sure our engine has already stopped the child.
Kyle McMartin 07d3322
+	 * Then wait for it to be off the CPU.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	if (!utrace_control(child, engine, UTRACE_STOP) &&
Kyle McMartin 07d3322
+	    !utrace_prepare_examine(child, engine, &exam))
Kyle McMartin 07d3322
+		ret = 0;
Kyle McMartin 07d3322
+out:
Kyle McMartin 07d3322
+	utrace_engine_put(engine);
Kyle McMartin 07d3322
+	return ret;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+int ptrace_attach(struct task_struct *task)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	int retval;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	audit_ptrace(task);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	retval = -EPERM;
Kyle McMartin 07d3322
+	if (unlikely(task->flags & PF_KTHREAD))
Kyle McMartin 07d3322
+		goto out;
Kyle McMartin 07d3322
+	if (same_thread_group(task, current))
Kyle McMartin 07d3322
+		goto out;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * Protect exec's credential calculations against our interference;
Kyle McMartin 07d3322
+	 * interference; SUID, SGID and LSM creds get determined differently
Kyle McMartin 07d3322
+	 * under ptrace.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	retval = -ERESTARTNOINTR;
Kyle McMartin 1aea232
+	if (mutex_lock_interruptible(&task->signal->cred_guard_mutex))
Kyle McMartin 07d3322
+		goto out;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	task_lock(task);
Kyle McMartin 07d3322
+	retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
Kyle McMartin 07d3322
+	task_unlock(task);
Kyle McMartin 07d3322
+	if (retval)
Kyle McMartin 07d3322
+		goto unlock_creds;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	retval = ptrace_attach_task(task, 0);
Kyle McMartin 07d3322
+	if (unlikely(retval))
Kyle McMartin 07d3322
+		goto unlock_creds;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	write_lock_irq(&tasklist_lock);
Kyle McMartin 07d3322
+	retval = -EPERM;
Kyle McMartin 07d3322
+	if (unlikely(task->exit_state))
Kyle McMartin 07d3322
+		goto unlock_tasklist;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	BUG_ON(task->ptrace);
Kyle McMartin 07d3322
+	task->ptrace = PT_UTRACED;
Kyle McMartin 07d3322
+	if (capable(CAP_SYS_PTRACE))
Kyle McMartin 07d3322
+		task->ptrace |= PT_PTRACE_CAP;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	__ptrace_link(task, current);
Kyle McMartin 07d3322
+	send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	retval = 0;
Kyle McMartin 07d3322
+unlock_tasklist:
Kyle McMartin 07d3322
+	write_unlock_irq(&tasklist_lock);
Kyle McMartin 07d3322
+unlock_creds:
Kyle McMartin 1aea232
+	mutex_unlock(&task->signal->cred_guard_mutex);
Kyle McMartin 07d3322
+out:
Kyle McMartin 07d3322
+	return retval;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+/*
Kyle McMartin 07d3322
+ * Performs checks and sets PT_UTRACED.
Kyle McMartin 07d3322
+ * Should be used by all ptrace implementations for PTRACE_TRACEME.
Kyle McMartin 07d3322
+ */
Kyle McMartin 07d3322
+int ptrace_traceme(void)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	bool detach = true;
Kyle McMartin 07d3322
+	int ret = ptrace_attach_task(current, 0);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (unlikely(ret))
Kyle McMartin 07d3322
+		return ret;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ret = -EPERM;
Kyle McMartin 07d3322
+	write_lock_irq(&tasklist_lock);
Kyle McMartin 07d3322
+	BUG_ON(current->ptrace);
Kyle McMartin 07d3322
+	ret = security_ptrace_traceme(current->parent);
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * Check PF_EXITING to ensure ->real_parent has not passed
Kyle McMartin 07d3322
+	 * exit_ptrace(). Otherwise we don't report the error but
Kyle McMartin 07d3322
+	 * pretend ->real_parent untraces us right after return.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	if (!ret && !(current->real_parent->flags & PF_EXITING)) {
Kyle McMartin 07d3322
+		current->ptrace = PT_UTRACED;
Kyle McMartin 07d3322
+		__ptrace_link(current, current->real_parent);
Kyle McMartin 07d3322
+		detach = false;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+	write_unlock_irq(&tasklist_lock);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (detach)
Kyle McMartin 07d3322
+		ptrace_abort_attach(current);
Kyle McMartin 07d3322
+	return ret;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static void ptrace_do_detach(struct task_struct *tracee, unsigned int data)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	bool detach, release;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	write_lock_irq(&tasklist_lock);
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * This tracee can be already killed. Make sure de_thread() or
Kyle McMartin 07d3322
+	 * our sub-thread doing do_wait() didn't do release_task() yet.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	detach = tracee->ptrace != 0;
Kyle McMartin 07d3322
+	release = false;
Kyle McMartin 07d3322
+	if (likely(detach))
Kyle McMartin 07d3322
+		release = __ptrace_detach(current, tracee);
Kyle McMartin 07d3322
+	write_unlock_irq(&tasklist_lock);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (unlikely(release))
Kyle McMartin 07d3322
+		release_task(tracee);
Kyle McMartin 07d3322
+	else if (likely(detach))
Kyle McMartin 07d3322
+		ptrace_detach_task(tracee, data);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+int ptrace_detach(struct task_struct *child, unsigned int data)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	if (!valid_signal(data))
Kyle McMartin 07d3322
+		return -EIO;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ptrace_do_detach(child, data);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	return 0;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+/*
Kyle McMartin 07d3322
+ * Detach all tasks we were using ptrace on. Called with tasklist held
Kyle McMartin 07d3322
+ * for writing, and returns with it held too. But note it can release
Kyle McMartin 07d3322
+ * and reacquire the lock.
Kyle McMartin 07d3322
+ */
Kyle McMartin 07d3322
+void exit_ptrace(struct task_struct *tracer)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	bool locked = true;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	for (;;) {
Kyle McMartin 07d3322
+		struct task_struct *tracee = NULL;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		if (!locked)
Kyle McMartin 07d3322
+			read_lock(&tasklist_lock);
Kyle McMartin 07d3322
+		if (!list_empty(&tracer->ptraced)) {
Kyle McMartin 07d3322
+			tracee = list_first_entry(&tracer->ptraced,
Kyle McMartin 07d3322
+					struct task_struct, ptrace_entry);
Kyle McMartin 07d3322
+			get_task_struct(tracee);
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+		if (!locked)
Kyle McMartin 07d3322
+			read_unlock(&tasklist_lock);
Kyle McMartin 07d3322
+		if (!tracee)
Kyle McMartin 07d3322
+			break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		if (locked) {
Kyle McMartin 07d3322
+			write_unlock_irq(&tasklist_lock);
Kyle McMartin 07d3322
+			locked = false;
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+		ptrace_do_detach(tracee, -1);
Kyle McMartin 07d3322
+		put_task_struct(tracee);
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (!locked)
Kyle McMartin 07d3322
+		write_lock_irq(&tasklist_lock);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static int ptrace_set_options(struct task_struct *tracee,
Kyle McMartin 07d3322
+				struct utrace_engine *engine, long data)
Kyle McMartin 07d3322
+{
Kyle McMartin 71f70cd
+	BUILD_BUG_ON(PTRACE_O_MASK & (PTRACE_O_SYSEMU | PTRACE_O_DETACHED));
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ptrace_set_events(tracee, engine, data & PTRACE_O_MASK);
Kyle McMartin 07d3322
+	return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static int ptrace_rw_siginfo(struct task_struct *tracee,
Kyle McMartin 07d3322
+				struct ptrace_context *ctx,
Kyle McMartin 07d3322
+				siginfo_t *info, bool write)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	unsigned long flags;
Kyle McMartin 07d3322
+	int err;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	switch (get_stop_event(ctx)) {
Kyle McMartin 07d3322
+	case 0: /* jctl stop */
Kyle McMartin 07d3322
+		return -EINVAL;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_EVENT_SIGNAL:
Kyle McMartin 07d3322
+		err = -ESRCH;
Kyle McMartin 07d3322
+		if (lock_task_sighand(tracee, &flags)) {
Kyle McMartin 07d3322
+			if (likely(task_is_traced(tracee))) {
Kyle McMartin 07d3322
+				if (write)
Kyle McMartin 07d3322
+					*ctx->siginfo = *info;
Kyle McMartin 07d3322
+				else
Kyle McMartin 07d3322
+					*info = *ctx->siginfo;
Kyle McMartin 07d3322
+				err = 0;
Kyle McMartin 07d3322
+			}
Kyle McMartin 07d3322
+			unlock_task_sighand(tracee, &flags);
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		return err;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	default:
Kyle McMartin 07d3322
+		if (!write) {
Kyle McMartin 07d3322
+			memset(info, 0, sizeof(*info));
Kyle McMartin 07d3322
+			info->si_signo = SIGTRAP;
Kyle McMartin 07d3322
+			info->si_code = ctx->stop_code & PTRACE_EVENT_MASK;
Kyle McMartin 07d3322
+			info->si_pid = task_pid_vnr(tracee);
Kyle McMartin 07d3322
+			info->si_uid = task_uid(tracee);
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		return 0;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static void do_ptrace_notify_stop(struct ptrace_context *ctx,
Kyle McMartin 07d3322
+					struct task_struct *tracee)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * This can race with SIGKILL, but we borrow this race from
Kyle McMartin 07d3322
+	 * the old ptrace implementation. ->exit_code is only needed
Kyle McMartin 07d3322
+	 * for wait_task_stopped()->task_stopped_code(), we should
Kyle McMartin 07d3322
+	 * change it to use ptrace_context.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	tracee->exit_code = ctx->stop_code & PTRACE_EVENT_MASK;
Kyle McMartin 07d3322
+	WARN_ON(!tracee->exit_code);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	read_lock(&tasklist_lock);
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * Don't want to allow preemption here, because
Kyle McMartin 07d3322
+	 * sys_ptrace() needs this task to be inactive.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	preempt_disable();
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * It can be killed and then released by our subthread,
Kyle McMartin 07d3322
+	 * or ptrace_attach() has not completed yet.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	if (task_ptrace(tracee))
Kyle McMartin 07d3322
+		do_notify_parent_cldstop(tracee, CLD_TRAPPED);
Kyle McMartin 07d3322
+	read_unlock(&tasklist_lock);
Kyle McMartin 07d3322
+	preempt_enable_no_resched();
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+void ptrace_notify_stop(struct task_struct *tracee)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct utrace_engine *engine = ptrace_lookup_engine(tracee);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (IS_ERR(engine))
Kyle McMartin 07d3322
+		return;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	do_ptrace_notify_stop(ptrace_context(engine), tracee);
Kyle McMartin 07d3322
+	utrace_engine_put(engine);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static int ptrace_resume_action(struct task_struct *tracee,
Kyle McMartin 07d3322
+				struct utrace_engine *engine, long request)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct ptrace_context *ctx = ptrace_context(engine);
Kyle McMartin 07d3322
+	unsigned long events;
Kyle McMartin 07d3322
+	int action;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ctx->options &= ~PTRACE_O_SYSEMU;
Kyle McMartin 07d3322
+	events = engine->flags & ~UTRACE_EVENT_SYSCALL;
Kyle McMartin 07d3322
+	action = UTRACE_RESUME;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	switch (request) {
Kyle McMartin 07d3322
+#ifdef PTRACE_SINGLEBLOCK
Kyle McMartin 07d3322
+	case PTRACE_SINGLEBLOCK:
Kyle McMartin 07d3322
+		if (unlikely(!arch_has_block_step()))
Kyle McMartin 07d3322
+			return -EIO;
Kyle McMartin 07d3322
+		action = UTRACE_BLOCKSTEP;
Kyle McMartin 07d3322
+		events |= UTRACE_EVENT(SYSCALL_EXIT);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+#endif
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+#ifdef PTRACE_SINGLESTEP
Kyle McMartin 07d3322
+	case PTRACE_SINGLESTEP:
Kyle McMartin 07d3322
+		if (unlikely(!arch_has_single_step()))
Kyle McMartin 07d3322
+			return -EIO;
Kyle McMartin 07d3322
+		action = UTRACE_SINGLESTEP;
Kyle McMartin 07d3322
+		events |= UTRACE_EVENT(SYSCALL_EXIT);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+#endif
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+#ifdef PTRACE_SYSEMU
Kyle McMartin 07d3322
+	case PTRACE_SYSEMU_SINGLESTEP:
Kyle McMartin 07d3322
+		if (unlikely(!arch_has_single_step()))
Kyle McMartin 07d3322
+			return -EIO;
Kyle McMartin 07d3322
+		action = UTRACE_SINGLESTEP;
Kyle McMartin 07d3322
+	case PTRACE_SYSEMU:
Kyle McMartin 07d3322
+		ctx->options |= PTRACE_O_SYSEMU;
Kyle McMartin 07d3322
+		events |= UTRACE_EVENT(SYSCALL_ENTRY);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+#endif
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_SYSCALL:
Kyle McMartin 07d3322
+		events |= UTRACE_EVENT_SYSCALL;
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_CONT:
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+	default:
Kyle McMartin 07d3322
+		return -EIO;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (events != engine->flags &&
Kyle McMartin 07d3322
+	    utrace_set_events(tracee, engine, events))
Kyle McMartin 07d3322
+		return -ESRCH;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	return action;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static int ptrace_resume(struct task_struct *tracee,
Kyle McMartin 07d3322
+				struct utrace_engine *engine,
Kyle McMartin 07d3322
+				long request, long data)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct ptrace_context *ctx = ptrace_context(engine);
Kyle McMartin 07d3322
+	int action;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (!valid_signal(data))
Kyle McMartin 07d3322
+		return -EIO;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	action = ptrace_resume_action(tracee, engine, request);
Kyle McMartin 07d3322
+	if (action < 0)
Kyle McMartin 07d3322
+		return action;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	switch (get_stop_event(ctx)) {
Kyle McMartin 07d3322
+	case PTRACE_EVENT_VFORK:
Kyle McMartin 07d3322
+		if (ctx->options & PTRACE_O_TRACEVFORKDONE) {
Kyle McMartin 07d3322
+			set_stop_code(ctx, PTRACE_EVENT_VFORK_DONE);
Kyle McMartin 07d3322
+			action = UTRACE_REPORT;
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_EVENT_EXEC:
Kyle McMartin 07d3322
+	case PTRACE_EVENT_FORK:
Kyle McMartin 07d3322
+	case PTRACE_EVENT_CLONE:
Kyle McMartin 07d3322
+	case PTRACE_EVENT_VFORK_DONE:
Kyle McMartin 07d3322
+		if (request == PTRACE_SYSCALL) {
Kyle McMartin 07d3322
+			set_syscall_code(ctx);
Kyle McMartin 07d3322
+			do_ptrace_notify_stop(ctx, tracee);
Kyle McMartin 07d3322
+			return 0;
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		if (action != UTRACE_RESUME) {
Kyle McMartin 07d3322
+			/*
Kyle McMartin 07d3322
+			 * single-stepping. UTRACE_SIGNAL_REPORT will
Kyle McMartin 07d3322
+			 * synthesize a trap to follow the syscall insn.
Kyle McMartin 07d3322
+			*/
Kyle McMartin 07d3322
+			ctx->signr = SIGTRAP;
Kyle McMartin 07d3322
+			action = UTRACE_INTERRUPT;
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_EVENT_SYSCALL:
Kyle McMartin 07d3322
+		if (data)
Kyle McMartin 07d3322
+			send_sig_info(data, SEND_SIG_PRIV, tracee);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_EVENT_SIGNAL:
Kyle McMartin 07d3322
+		ctx->signr = data;
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ctx->resume = action;
Kyle McMartin 07d3322
+	ptrace_wake_up(tracee, engine, action, true);
Kyle McMartin 07d3322
+	return 0;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+extern int ptrace_regset(struct task_struct *task, int req, unsigned int type,
Kyle McMartin 07d3322
+			 struct iovec *kiov);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+int ptrace_request(struct task_struct *child, long request,
Kyle McMartin 1aea232
+		   unsigned long addr, unsigned long data)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct utrace_engine *engine = ptrace_lookup_engine(child);
Kyle McMartin 07d3322
+	siginfo_t siginfo;
Kyle McMartin 07d3322
+	int ret;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (unlikely(IS_ERR(engine)))
Kyle McMartin 07d3322
+		return -ESRCH;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	switch (request) {
Kyle McMartin 07d3322
+	case PTRACE_PEEKTEXT:
Kyle McMartin 07d3322
+	case PTRACE_PEEKDATA:
Kyle McMartin 07d3322
+		ret = generic_ptrace_peekdata(child, addr, data);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+	case PTRACE_POKETEXT:
Kyle McMartin 07d3322
+	case PTRACE_POKEDATA:
Kyle McMartin 07d3322
+		ret = generic_ptrace_pokedata(child, addr, data);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+#ifdef PTRACE_OLDSETOPTIONS
Kyle McMartin 07d3322
+	case PTRACE_OLDSETOPTIONS:
Kyle McMartin 07d3322
+#endif
Kyle McMartin 07d3322
+	case PTRACE_SETOPTIONS:
Kyle McMartin 07d3322
+		ret = ptrace_set_options(child, engine, data);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+	case PTRACE_GETEVENTMSG:
Kyle McMartin 07d3322
+		ret = put_user(ptrace_context(engine)->eventmsg,
Kyle McMartin 07d3322
+				(unsigned long __user *) data);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_GETSIGINFO:
Kyle McMartin 07d3322
+		ret = ptrace_rw_siginfo(child, ptrace_context(engine),
Kyle McMartin 07d3322
+					&siginfo, false);
Kyle McMartin 07d3322
+		if (!ret)
Kyle McMartin 07d3322
+			ret = copy_siginfo_to_user((siginfo_t __user *) data,
Kyle McMartin 07d3322
+						   &siginfo);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_SETSIGINFO:
Kyle McMartin 07d3322
+		if (copy_from_user(&siginfo, (siginfo_t __user *) data,
Kyle McMartin 07d3322
+				   sizeof siginfo))
Kyle McMartin 07d3322
+			ret = -EFAULT;
Kyle McMartin 07d3322
+		else
Kyle McMartin 07d3322
+			ret = ptrace_rw_siginfo(child, ptrace_context(engine),
Kyle McMartin 07d3322
+						&siginfo, true);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_DETACH:	 /* detach a process that was attached. */
Kyle McMartin 07d3322
+		ret = ptrace_detach(child, data);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_KILL:
Kyle McMartin 07d3322
+		/* Ugly historical behaviour. */
Kyle McMartin 07d3322
+		if (task_is_traced(child))
Kyle McMartin 07d3322
+			ptrace_resume(child, engine, PTRACE_CONT, SIGKILL);
Kyle McMartin 07d3322
+		ret = 0;
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_GETREGSET:
Kyle McMartin 07d3322
+	case PTRACE_SETREGSET:
Kyle McMartin 07d3322
+	{
Kyle McMartin 07d3322
+		struct iovec kiov;
Kyle McMartin 07d3322
+		struct iovec __user *uiov = (struct iovec __user *) data;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov)))
Kyle McMartin 07d3322
+			return -EFAULT;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		if (__get_user(kiov.iov_base, &uiov->iov_base) ||
Kyle McMartin 07d3322
+		    __get_user(kiov.iov_len, &uiov->iov_len))
Kyle McMartin 07d3322
+			return -EFAULT;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		ret = ptrace_regset(child, request, addr, &kiov);
Kyle McMartin 07d3322
+		if (!ret)
Kyle McMartin 07d3322
+			ret = __put_user(kiov.iov_len, &uiov->iov_len);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	default:
Kyle McMartin 07d3322
+		ret = ptrace_resume(child, engine, request, data);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	utrace_engine_put(engine);
Kyle McMartin 07d3322
+	return ret;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+#if defined CONFIG_COMPAT
Kyle McMartin 07d3322
+#include <linux/compat.h>
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+int compat_ptrace_request(struct task_struct *child, compat_long_t request,
Kyle McMartin 07d3322
+			  compat_ulong_t addr, compat_ulong_t data)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct utrace_engine *engine = ptrace_lookup_engine(child);
Kyle McMartin 07d3322
+	compat_ulong_t __user *datap = compat_ptr(data);
Kyle McMartin 07d3322
+	compat_ulong_t word;
Kyle McMartin 07d3322
+	siginfo_t siginfo;
Kyle McMartin 07d3322
+	int ret;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (unlikely(IS_ERR(engine)))
Kyle McMartin 07d3322
+		return -ESRCH;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	switch (request) {
Kyle McMartin 07d3322
+	case PTRACE_PEEKTEXT:
Kyle McMartin 07d3322
+	case PTRACE_PEEKDATA:
Kyle McMartin 07d3322
+		ret = access_process_vm(child, addr, &word, sizeof(word), 0);
Kyle McMartin 07d3322
+		if (ret != sizeof(word))
Kyle McMartin 07d3322
+			ret = -EIO;
Kyle McMartin 07d3322
+		else
Kyle McMartin 07d3322
+			ret = put_user(word, datap);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_POKETEXT:
Kyle McMartin 07d3322
+	case PTRACE_POKEDATA:
Kyle McMartin 07d3322
+		ret = access_process_vm(child, addr, &data, sizeof(data), 1);
Kyle McMartin 07d3322
+		ret = (ret != sizeof(data) ? -EIO : 0);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_GETEVENTMSG:
Kyle McMartin 07d3322
+		ret = put_user((compat_ulong_t)ptrace_context(engine)->eventmsg,
Kyle McMartin 07d3322
+				datap);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_GETSIGINFO:
Kyle McMartin 07d3322
+		ret = ptrace_rw_siginfo(child, ptrace_context(engine),
Kyle McMartin 07d3322
+					&siginfo, false);
Kyle McMartin 07d3322
+		if (!ret)
Kyle McMartin 07d3322
+			ret = copy_siginfo_to_user32(
Kyle McMartin 07d3322
+				(struct compat_siginfo __user *) datap,
Kyle McMartin 07d3322
+				&siginfo);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_SETSIGINFO:
Kyle McMartin 07d3322
+		memset(&siginfo, 0, sizeof siginfo);
Kyle McMartin 07d3322
+		if (copy_siginfo_from_user32(
Kyle McMartin 07d3322
+			    &siginfo, (struct compat_siginfo __user *) datap))
Kyle McMartin 07d3322
+			ret = -EFAULT;
Kyle McMartin 07d3322
+		else
Kyle McMartin 07d3322
+			ret = ptrace_rw_siginfo(child, ptrace_context(engine),
Kyle McMartin 07d3322
+						&siginfo, true);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	case PTRACE_GETREGSET:
Kyle McMartin 07d3322
+	case PTRACE_SETREGSET:
Kyle McMartin 07d3322
+	{
Kyle McMartin 07d3322
+		struct iovec kiov;
Kyle McMartin 07d3322
+		struct compat_iovec __user *uiov =
Kyle McMartin 07d3322
+			(struct compat_iovec __user *) datap;
Kyle McMartin 07d3322
+		compat_uptr_t ptr;
Kyle McMartin 07d3322
+		compat_size_t len;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov)))
Kyle McMartin 07d3322
+			return -EFAULT;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		if (__get_user(ptr, &uiov->iov_base) ||
Kyle McMartin 07d3322
+		    __get_user(len, &uiov->iov_len))
Kyle McMartin 07d3322
+			return -EFAULT;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		kiov.iov_base = compat_ptr(ptr);
Kyle McMartin 07d3322
+		kiov.iov_len = len;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		ret = ptrace_regset(child, request, addr, &kiov);
Kyle McMartin 07d3322
+		if (!ret)
Kyle McMartin 07d3322
+			ret = __put_user(kiov.iov_len, &uiov->iov_len);
Kyle McMartin 07d3322
+		break;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	default:
Kyle McMartin 07d3322
+		ret = ptrace_request(child, request, addr, data);
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	utrace_engine_put(engine);
Kyle McMartin 07d3322
+	return ret;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+#endif	/* CONFIG_COMPAT */
Kyle McMartin 07d3322
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
Kyle McMartin 71f70cd
index e275608..72ea65c 100644  
Kyle McMartin 07d3322
--- a/kernel/ptrace.c
Kyle McMartin 07d3322
+++ b/kernel/ptrace.c
Kyle McMartin 07d3322
@@ -15,7 +15,6 @@
Kyle McMartin 07d3322
 #include <linux/highmem.h>
Kyle McMartin 07d3322
 #include <linux/pagemap.h>
Kyle McMartin 07d3322
 #include <linux/ptrace.h>
Kyle McMartin 07d3322
-#include <linux/utrace.h>
Kyle McMartin 07d3322
 #include <linux/security.h>
Kyle McMartin 07d3322
 #include <linux/signal.h>
Kyle McMartin 07d3322
 #include <linux/audit.h>
Kyle McMartin 71f70cd
@@ -24,7 +23,320 @@
Kyle McMartin 07d3322
 #include <linux/uaccess.h>
Kyle McMartin 07d3322
 #include <linux/regset.h>
Kyle McMartin 07d3322
 
Kyle McMartin 07d3322
+int __ptrace_may_access(struct task_struct *task, unsigned int mode)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	const struct cred *cred = current_cred(), *tcred;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	/* May we inspect the given task?
Kyle McMartin 07d3322
+	 * This check is used both for attaching with ptrace
Kyle McMartin 07d3322
+	 * and for allowing access to sensitive information in /proc.
Kyle McMartin 07d3322
+	 *
Kyle McMartin 07d3322
+	 * ptrace_attach denies several cases that /proc allows
Kyle McMartin 07d3322
+	 * because setting up the necessary parent/child relationship
Kyle McMartin 07d3322
+	 * or halting the specified task is impossible.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	int dumpable = 0;
Kyle McMartin 07d3322
+	/* Don't let security modules deny introspection */
Kyle McMartin 07d3322
+	if (task == current)
Kyle McMartin 07d3322
+		return 0;
Kyle McMartin 07d3322
+	rcu_read_lock();
Kyle McMartin 07d3322
+	tcred = __task_cred(task);
Kyle McMartin 07d3322
+	if ((cred->uid != tcred->euid ||
Kyle McMartin 07d3322
+	     cred->uid != tcred->suid ||
Kyle McMartin 07d3322
+	     cred->uid != tcred->uid  ||
Kyle McMartin 07d3322
+	     cred->gid != tcred->egid ||
Kyle McMartin 07d3322
+	     cred->gid != tcred->sgid ||
Kyle McMartin 07d3322
+	     cred->gid != tcred->gid) &&
Kyle McMartin 07d3322
+	    !capable(CAP_SYS_PTRACE)) {
Kyle McMartin 07d3322
+		rcu_read_unlock();
Kyle McMartin 07d3322
+		return -EPERM;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+	rcu_read_unlock();
Kyle McMartin 07d3322
+	smp_rmb();
Kyle McMartin 07d3322
+	if (task->mm)
Kyle McMartin 07d3322
+		dumpable = get_dumpable(task->mm);
Kyle McMartin 07d3322
+	if (!dumpable && !capable(CAP_SYS_PTRACE))
Kyle McMartin 07d3322
+		return -EPERM;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	return security_ptrace_access_check(task, mode);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+bool ptrace_may_access(struct task_struct *task, unsigned int mode)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	int err;
Kyle McMartin 07d3322
+	task_lock(task);
Kyle McMartin 07d3322
+	err = __ptrace_may_access(task, mode);
Kyle McMartin 07d3322
+	task_unlock(task);
Kyle McMartin 07d3322
+	return !err;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+/*
Kyle McMartin 07d3322
+ * Called with irqs disabled, returns true if childs should reap themselves.
Kyle McMartin 07d3322
+ */
Kyle McMartin 07d3322
+static int ignoring_children(struct sighand_struct *sigh)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	int ret;
Kyle McMartin 07d3322
+	spin_lock(&sigh->siglock);
Kyle McMartin 07d3322
+	ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
Kyle McMartin 07d3322
+	      (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
Kyle McMartin 07d3322
+	spin_unlock(&sigh->siglock);
Kyle McMartin 07d3322
+	return ret;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+/*
Kyle McMartin 07d3322
+ * Called with tasklist_lock held for writing.
Kyle McMartin 07d3322
+ * Unlink a traced task, and clean it up if it was a traced zombie.
Kyle McMartin 07d3322
+ * Return true if it needs to be reaped with release_task().
Kyle McMartin 07d3322
+ * (We can't call release_task() here because we already hold tasklist_lock.)
Kyle McMartin 07d3322
+ *
Kyle McMartin 07d3322
+ * If it's a zombie, our attachedness prevented normal parent notification
Kyle McMartin 07d3322
+ * or self-reaping.  Do notification now if it would have happened earlier.
Kyle McMartin 07d3322
+ * If it should reap itself, return true.
Kyle McMartin 07d3322
+ *
Kyle McMartin 07d3322
+ * If it's our own child, there is no notification to do. But if our normal
Kyle McMartin 07d3322
+ * children self-reap, then this child was prevented by ptrace and we must
Kyle McMartin 07d3322
+ * reap it now, in that case we must also wake up sub-threads sleeping in
Kyle McMartin 07d3322
+ * do_wait().
Kyle McMartin 07d3322
+ */
Kyle McMartin 07d3322
+bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	__ptrace_unlink(p);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (p->exit_state == EXIT_ZOMBIE) {
Kyle McMartin 07d3322
+		if (!task_detached(p) && thread_group_empty(p)) {
Kyle McMartin 07d3322
+			if (!same_thread_group(p->real_parent, tracer))
Kyle McMartin 07d3322
+				do_notify_parent(p, p->exit_signal);
Kyle McMartin 07d3322
+			else if (ignoring_children(tracer->sighand)) {
Kyle McMartin 07d3322
+				__wake_up_parent(p, tracer);
Kyle McMartin 07d3322
+				p->exit_signal = -1;
Kyle McMartin 07d3322
+			}
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+		if (task_detached(p)) {
Kyle McMartin 07d3322
+			/* Mark it as in the process of being reaped. */
Kyle McMartin 07d3322
+			p->exit_state = EXIT_DEAD;
Kyle McMartin 07d3322
+			return true;
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	return false;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	int copied = 0;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	while (len > 0) {
Kyle McMartin 07d3322
+		char buf[128];
Kyle McMartin 07d3322
+		int this_len, retval;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
Kyle McMartin 07d3322
+		retval = access_process_vm(tsk, src, buf, this_len, 0);
Kyle McMartin 07d3322
+		if (!retval) {
Kyle McMartin 07d3322
+			if (copied)
Kyle McMartin 07d3322
+				break;
Kyle McMartin 07d3322
+			return -EIO;
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+		if (copy_to_user(dst, buf, retval))
Kyle McMartin 07d3322
+			return -EFAULT;
Kyle McMartin 07d3322
+		copied += retval;
Kyle McMartin 07d3322
+		src += retval;
Kyle McMartin 07d3322
+		dst += retval;
Kyle McMartin 07d3322
+		len -= retval;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+	return copied;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	int copied = 0;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	while (len > 0) {
Kyle McMartin 07d3322
+		char buf[128];
Kyle McMartin 07d3322
+		int this_len, retval;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
Kyle McMartin 07d3322
+		if (copy_from_user(buf, src, this_len))
Kyle McMartin 07d3322
+			return -EFAULT;
Kyle McMartin 07d3322
+		retval = access_process_vm(tsk, dst, buf, this_len, 1);
Kyle McMartin 07d3322
+		if (!retval) {
Kyle McMartin 07d3322
+			if (copied)
Kyle McMartin 07d3322
+				break;
Kyle McMartin 07d3322
+			return -EIO;
Kyle McMartin 07d3322
+		}
Kyle McMartin 07d3322
+		copied += retval;
Kyle McMartin 07d3322
+		src += retval;
Kyle McMartin 07d3322
+		dst += retval;
Kyle McMartin 07d3322
+		len -= retval;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+	return copied;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static const struct user_regset *
Kyle McMartin 07d3322
+find_regset(const struct user_regset_view *view, unsigned int type)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	const struct user_regset *regset;
Kyle McMartin 07d3322
+	int n;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	for (n = 0; n < view->n; ++n) {
Kyle McMartin 07d3322
+		regset = view->regsets + n;
Kyle McMartin 07d3322
+		if (regset->core_note_type == type)
Kyle McMartin 07d3322
+			return regset;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	return NULL;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+int ptrace_regset(struct task_struct *task, int req, unsigned int type,
Kyle McMartin 07d3322
+		  struct iovec *kiov)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	const struct user_regset_view *view = task_user_regset_view(task);
Kyle McMartin 07d3322
+	const struct user_regset *regset = find_regset(view, type);
Kyle McMartin 07d3322
+	int regset_no;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (!regset || (kiov->iov_len % regset->size) != 0)
Kyle McMartin 07d3322
+		return -EINVAL;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	regset_no = regset - view->regsets;
Kyle McMartin 07d3322
+	kiov->iov_len = min(kiov->iov_len,
Kyle McMartin 07d3322
+			    (__kernel_size_t) (regset->n * regset->size));
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (req == PTRACE_GETREGSET)
Kyle McMartin 07d3322
+		return copy_regset_to_user(task, view, regset_no, 0,
Kyle McMartin 07d3322
+					   kiov->iov_len, kiov->iov_base);
Kyle McMartin 07d3322
+	else
Kyle McMartin 07d3322
+		return copy_regset_from_user(task, view, regset_no, 0,
Kyle McMartin 07d3322
+					     kiov->iov_len, kiov->iov_base);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+#endif
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+static struct task_struct *ptrace_get_task_struct(pid_t pid)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct task_struct *child;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	rcu_read_lock();
Kyle McMartin 07d3322
+	child = find_task_by_vpid(pid);
Kyle McMartin 07d3322
+	if (child)
Kyle McMartin 07d3322
+		get_task_struct(child);
Kyle McMartin 07d3322
+	rcu_read_unlock();
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (!child)
Kyle McMartin 07d3322
+		return ERR_PTR(-ESRCH);
Kyle McMartin 07d3322
+	return child;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+#ifndef arch_ptrace_attach
Kyle McMartin 07d3322
+#define arch_ptrace_attach(child)	do { } while (0)
Kyle McMartin 07d3322
+#endif
Kyle McMartin 07d3322
+
Kyle McMartin 71f70cd
+SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,
Kyle McMartin 71f70cd
+		unsigned long, data)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct task_struct *child;
Kyle McMartin 07d3322
+	long ret;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (request == PTRACE_TRACEME) {
Kyle McMartin 07d3322
+		ret = ptrace_traceme();
Kyle McMartin 07d3322
+		if (!ret)
Kyle McMartin 07d3322
+			arch_ptrace_attach(current);
Kyle McMartin 07d3322
+		goto out;
Kyle McMartin 07d3322
+	}
Kyle McMartin 71f70cd
 
Kyle McMartin 07d3322
+	child = ptrace_get_task_struct(pid);
Kyle McMartin 07d3322
+	if (IS_ERR(child)) {
Kyle McMartin 07d3322
+		ret = PTR_ERR(child);
Kyle McMartin 07d3322
+		goto out;
Kyle McMartin 07d3322
+	}
Kyle McMartin 71f70cd
+
Kyle McMartin 07d3322
+	if (request == PTRACE_ATTACH) {
Kyle McMartin 07d3322
+		ret = ptrace_attach(child);
Kyle McMartin 07d3322
+		/*
Kyle McMartin 07d3322
+		 * Some architectures need to do book-keeping after
Kyle McMartin 07d3322
+		 * a ptrace attach.
Kyle McMartin 07d3322
+		 */
Kyle McMartin 07d3322
+		if (!ret)
Kyle McMartin 07d3322
+			arch_ptrace_attach(child);
Kyle McMartin 07d3322
+		goto out_put_task_struct;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ret = ptrace_check_attach(child, request == PTRACE_KILL);
Kyle McMartin 07d3322
+	if (ret < 0)
Kyle McMartin 07d3322
+		goto out_put_task_struct;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ret = arch_ptrace(child, request, addr, data);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+ out_put_task_struct:
Kyle McMartin 07d3322
+	put_task_struct(child);
Kyle McMartin 07d3322
+ out:
Kyle McMartin 07d3322
+	return ret;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 71f70cd
+int generic_ptrace_peekdata(struct task_struct *tsk, unsigned long addr,
Kyle McMartin 71f70cd
+			    unsigned long data)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	unsigned long tmp;
Kyle McMartin 07d3322
+	int copied;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
Kyle McMartin 07d3322
+	if (copied != sizeof(tmp))
Kyle McMartin 07d3322
+		return -EIO;
Kyle McMartin 07d3322
+	return put_user(tmp, (unsigned long __user *)data);
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 71f70cd
+int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr,
Kyle McMartin 71f70cd
+			    unsigned long data)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	int copied;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
Kyle McMartin 07d3322
+	return (copied == sizeof(data)) ? 0 : -EIO;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+#if defined CONFIG_COMPAT
Kyle McMartin 07d3322
+#include <linux/compat.h>
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
Kyle McMartin 07d3322
+				  compat_long_t addr, compat_long_t data)
Kyle McMartin 07d3322
+{
Kyle McMartin 07d3322
+	struct task_struct *child;
Kyle McMartin 07d3322
+	long ret;
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (request == PTRACE_TRACEME) {
Kyle McMartin 07d3322
+		ret = ptrace_traceme();
Kyle McMartin 07d3322
+		goto out;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	child = ptrace_get_task_struct(pid);
Kyle McMartin 07d3322
+	if (IS_ERR(child)) {
Kyle McMartin 07d3322
+		ret = PTR_ERR(child);
Kyle McMartin 07d3322
+		goto out;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	if (request == PTRACE_ATTACH) {
Kyle McMartin 07d3322
+		ret = ptrace_attach(child);
Kyle McMartin 07d3322
+		/*
Kyle McMartin 07d3322
+		 * Some architectures need to do book-keeping after
Kyle McMartin 07d3322
+		 * a ptrace attach.
Kyle McMartin 07d3322
+		 */
Kyle McMartin 07d3322
+		if (!ret)
Kyle McMartin 07d3322
+			arch_ptrace_attach(child);
Kyle McMartin 07d3322
+		goto out_put_task_struct;
Kyle McMartin 07d3322
+	}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+	ret = ptrace_check_attach(child, request == PTRACE_KILL);
Kyle McMartin 07d3322
+	if (!ret)
Kyle McMartin 07d3322
+		ret = compat_arch_ptrace(child, request, addr, data);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+ out_put_task_struct:
Kyle McMartin 07d3322
+	put_task_struct(child);
Kyle McMartin 07d3322
+ out:
Kyle McMartin 07d3322
+	return ret;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+#endif	/* CONFIG_COMPAT */
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+#ifndef CONFIG_UTRACE
Kyle McMartin 07d3322
 /*
Kyle McMartin 07d3322
  * ptrace a task: make the debugger its new parent and
Kyle McMartin 07d3322
  * move it to the ptrace list.
Kyle McMartin 71f70cd
@@ -117,61 +429,6 @@ int ptrace_check_attach(struct task_stru
Kyle McMartin 07d3322
 	return ret;
Kyle McMartin 07d3322
 }
Kyle McMartin 07d3322
 
Kyle McMartin 07d3322
-int __ptrace_may_access(struct task_struct *task, unsigned int mode)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	const struct cred *cred = current_cred(), *tcred;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	/* May we inspect the given task?
Kyle McMartin 07d3322
-	 * This check is used both for attaching with ptrace
Kyle McMartin 07d3322
-	 * and for allowing access to sensitive information in /proc.
Kyle McMartin 07d3322
-	 *
Kyle McMartin 07d3322
-	 * ptrace_attach denies several cases that /proc allows
Kyle McMartin 07d3322
-	 * because setting up the necessary parent/child relationship
Kyle McMartin 07d3322
-	 * or halting the specified task is impossible.
Kyle McMartin 07d3322
-	 */
Kyle McMartin 07d3322
-	int dumpable = 0;
Kyle McMartin 07d3322
-	/* Don't let security modules deny introspection */
Kyle McMartin 07d3322
-	if (task == current)
Kyle McMartin 07d3322
-		return 0;
Kyle McMartin 07d3322
-	rcu_read_lock();
Kyle McMartin 07d3322
-	tcred = __task_cred(task);
Kyle McMartin 07d3322
-	if ((cred->uid != tcred->euid ||
Kyle McMartin 07d3322
-	     cred->uid != tcred->suid ||
Kyle McMartin 07d3322
-	     cred->uid != tcred->uid  ||
Kyle McMartin 07d3322
-	     cred->gid != tcred->egid ||
Kyle McMartin 07d3322
-	     cred->gid != tcred->sgid ||
Kyle McMartin 07d3322
-	     cred->gid != tcred->gid) &&
Kyle McMartin 07d3322
-	    !capable(CAP_SYS_PTRACE)) {
Kyle McMartin 07d3322
-		rcu_read_unlock();
Kyle McMartin 07d3322
-		return -EPERM;
Kyle McMartin 07d3322
-	}
Kyle McMartin 07d3322
-	rcu_read_unlock();
Kyle McMartin 07d3322
-	smp_rmb();
Kyle McMartin 07d3322
-	if (task->mm)
Kyle McMartin 07d3322
-		dumpable = get_dumpable(task->mm);
Kyle McMartin 07d3322
-	if (!dumpable && !capable(CAP_SYS_PTRACE))
Kyle McMartin 07d3322
-		return -EPERM;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	return security_ptrace_access_check(task, mode);
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-bool ptrace_may_access(struct task_struct *task, unsigned int mode)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	int err;
Kyle McMartin 07d3322
-	task_lock(task);
Kyle McMartin 07d3322
-	err = __ptrace_may_access(task, mode);
Kyle McMartin 07d3322
-	task_unlock(task);
Kyle McMartin 07d3322
-	return !err;
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-/*
Kyle McMartin 07d3322
- * For experimental use of utrace, exclude ptrace on the same task.
Kyle McMartin 07d3322
- */
Kyle McMartin 07d3322
-static inline bool exclude_ptrace(struct task_struct *task)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	return unlikely(!!task_utrace_flags(task));
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
 int ptrace_attach(struct task_struct *task)
Kyle McMartin 07d3322
 {
Kyle McMartin 07d3322
 	int retval;
Kyle McMartin 71f70cd
@@ -195,8 +452,6 @@ int ptrace_attach(struct task_struct *ta
Kyle McMartin 07d3322
 
Kyle McMartin 07d3322
 	task_lock(task);
Kyle McMartin 07d3322
 	retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
Kyle McMartin 07d3322
-	if (!retval && exclude_ptrace(task))
Kyle McMartin 07d3322
-		retval = -EBUSY;
Kyle McMartin 07d3322
 	task_unlock(task);
Kyle McMartin 07d3322
 	if (retval)
Kyle McMartin 07d3322
 		goto unlock_creds;
Kyle McMartin 71f70cd
@@ -220,91 +475,37 @@ unlock_tasklist:
Kyle McMartin 71f70cd
 	write_unlock_irq(&tasklist_lock);
Kyle McMartin 71f70cd
 unlock_creds:
Kyle McMartin 71f70cd
 	mutex_unlock(&task->signal->cred_guard_mutex);
Kyle McMartin 07d3322
-out:
Kyle McMartin 07d3322
-	return retval;
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-/**
Kyle McMartin 07d3322
- * ptrace_traceme  --  helper for PTRACE_TRACEME
Kyle McMartin 07d3322
- *
Kyle McMartin 07d3322
- * Performs checks and sets PT_PTRACED.
Kyle McMartin 07d3322
- * Should be used by all ptrace implementations for PTRACE_TRACEME.
Kyle McMartin 07d3322
- */
Kyle McMartin 07d3322
-int ptrace_traceme(void)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	int ret = -EPERM;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	if (exclude_ptrace(current)) /* XXX locking */
Kyle McMartin 07d3322
-		return -EBUSY;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	write_lock_irq(&tasklist_lock);
Kyle McMartin 07d3322
-	/* Are we already being traced? */
Kyle McMartin 07d3322
-	if (!current->ptrace) {
Kyle McMartin 07d3322
-		ret = security_ptrace_traceme(current->parent);
Kyle McMartin 07d3322
-		/*
Kyle McMartin 07d3322
-		 * Check PF_EXITING to ensure ->real_parent has not passed
Kyle McMartin 07d3322
-		 * exit_ptrace(). Otherwise we don't report the error but
Kyle McMartin 07d3322
-		 * pretend ->real_parent untraces us right after return.
Kyle McMartin 07d3322
-		 */
Kyle McMartin 07d3322
-		if (!ret && !(current->real_parent->flags & PF_EXITING)) {
Kyle McMartin 07d3322
-			current->ptrace = PT_PTRACED;
Kyle McMartin 07d3322
-			__ptrace_link(current, current->real_parent);
Kyle McMartin 07d3322
-		}
Kyle McMartin 07d3322
-	}
Kyle McMartin 07d3322
-	write_unlock_irq(&tasklist_lock);
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	return ret;
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-/*
Kyle McMartin 07d3322
- * Called with irqs disabled, returns true if childs should reap themselves.
Kyle McMartin 07d3322
- */
Kyle McMartin 07d3322
-static int ignoring_children(struct sighand_struct *sigh)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	int ret;
Kyle McMartin 07d3322
-	spin_lock(&sigh->siglock);
Kyle McMartin 07d3322
-	ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
Kyle McMartin 07d3322
-	      (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
Kyle McMartin 07d3322
-	spin_unlock(&sigh->siglock);
Kyle McMartin 07d3322
-	return ret;
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-/*
Kyle McMartin 07d3322
- * Called with tasklist_lock held for writing.
Kyle McMartin 07d3322
- * Unlink a traced task, and clean it up if it was a traced zombie.
Kyle McMartin 07d3322
- * Return true if it needs to be reaped with release_task().
Kyle McMartin 07d3322
- * (We can't call release_task() here because we already hold tasklist_lock.)
Kyle McMartin 07d3322
- *
Kyle McMartin 07d3322
- * If it's a zombie, our attachedness prevented normal parent notification
Kyle McMartin 07d3322
- * or self-reaping.  Do notification now if it would have happened earlier.
Kyle McMartin 07d3322
- * If it should reap itself, return true.
Kyle McMartin 07d3322
+out:
Kyle McMartin 07d3322
+	return retval;
Kyle McMartin 07d3322
+}
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
+/**
Kyle McMartin 07d3322
+ * ptrace_traceme  --  helper for PTRACE_TRACEME
Kyle McMartin 07d3322
  *
Kyle McMartin 07d3322
- * If it's our own child, there is no notification to do. But if our normal
Kyle McMartin 07d3322
- * children self-reap, then this child was prevented by ptrace and we must
Kyle McMartin 07d3322
- * reap it now, in that case we must also wake up sub-threads sleeping in
Kyle McMartin 07d3322
- * do_wait().
Kyle McMartin 07d3322
+ * Performs checks and sets PT_PTRACED.
Kyle McMartin 07d3322
+ * Should be used by all ptrace implementations for PTRACE_TRACEME.
Kyle McMartin 07d3322
  */
Kyle McMartin 07d3322
-bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
Kyle McMartin 07d3322
+int ptrace_traceme(void)
Kyle McMartin 07d3322
 {
Kyle McMartin 07d3322
-	__ptrace_unlink(p);
Kyle McMartin 07d3322
+	int ret = -EPERM;
Kyle McMartin 07d3322
 
Kyle McMartin 07d3322
-	if (p->exit_state == EXIT_ZOMBIE) {
Kyle McMartin 07d3322
-		if (!task_detached(p) && thread_group_empty(p)) {
Kyle McMartin 07d3322
-			if (!same_thread_group(p->real_parent, tracer))
Kyle McMartin 07d3322
-				do_notify_parent(p, p->exit_signal);
Kyle McMartin 07d3322
-			else if (ignoring_children(tracer->sighand)) {
Kyle McMartin 07d3322
-				__wake_up_parent(p, tracer);
Kyle McMartin 07d3322
-				p->exit_signal = -1;
Kyle McMartin 07d3322
-			}
Kyle McMartin 07d3322
-		}
Kyle McMartin 07d3322
-		if (task_detached(p)) {
Kyle McMartin 07d3322
-			/* Mark it as in the process of being reaped. */
Kyle McMartin 07d3322
-			p->exit_state = EXIT_DEAD;
Kyle McMartin 07d3322
-			return true;
Kyle McMartin 07d3322
+	write_lock_irq(&tasklist_lock);
Kyle McMartin 07d3322
+	/* Are we already being traced? */
Kyle McMartin 07d3322
+	if (!current->ptrace) {
Kyle McMartin 07d3322
+		ret = security_ptrace_traceme(current->parent);
Kyle McMartin 07d3322
+		/*
Kyle McMartin 07d3322
+		 * Check PF_EXITING to ensure ->real_parent has not passed
Kyle McMartin 07d3322
+		 * exit_ptrace(). Otherwise we don't report the error but
Kyle McMartin 07d3322
+		 * pretend ->real_parent untraces us right after return.
Kyle McMartin 07d3322
+		 */
Kyle McMartin 07d3322
+		if (!ret && !(current->real_parent->flags & PF_EXITING)) {
Kyle McMartin 07d3322
+			current->ptrace = PT_PTRACED;
Kyle McMartin 07d3322
+			__ptrace_link(current, current->real_parent);
Kyle McMartin 07d3322
 		}
Kyle McMartin 07d3322
 	}
Kyle McMartin 07d3322
+	write_unlock_irq(&tasklist_lock);
Kyle McMartin 07d3322
 
Kyle McMartin 07d3322
-	return false;
Kyle McMartin 07d3322
+	return ret;
Kyle McMartin 07d3322
 }
Kyle McMartin 07d3322
 
Kyle McMartin 07d3322
 int ptrace_detach(struct task_struct *child, unsigned int data)
Kyle McMartin 71f70cd
@@ -368,57 +569,7 @@ void exit_ptrace(struct task_struct *tra
Kyle McMartin 07d3322
 	write_lock_irq(&tasklist_lock);
Kyle McMartin 07d3322
 }
Kyle McMartin 07d3322
 
Kyle McMartin 07d3322
-int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	int copied = 0;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	while (len > 0) {
Kyle McMartin 07d3322
-		char buf[128];
Kyle McMartin 07d3322
-		int this_len, retval;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
Kyle McMartin 07d3322
-		retval = access_process_vm(tsk, src, buf, this_len, 0);
Kyle McMartin 07d3322
-		if (!retval) {
Kyle McMartin 07d3322
-			if (copied)
Kyle McMartin 07d3322
-				break;
Kyle McMartin 07d3322
-			return -EIO;
Kyle McMartin 07d3322
-		}
Kyle McMartin 07d3322
-		if (copy_to_user(dst, buf, retval))
Kyle McMartin 07d3322
-			return -EFAULT;
Kyle McMartin 07d3322
-		copied += retval;
Kyle McMartin 07d3322
-		src += retval;
Kyle McMartin 07d3322
-		dst += retval;
Kyle McMartin 07d3322
-		len -= retval;
Kyle McMartin 07d3322
-	}
Kyle McMartin 07d3322
-	return copied;
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	int copied = 0;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	while (len > 0) {
Kyle McMartin 07d3322
-		char buf[128];
Kyle McMartin 07d3322
-		int this_len, retval;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
Kyle McMartin 07d3322
-		if (copy_from_user(buf, src, this_len))
Kyle McMartin 07d3322
-			return -EFAULT;
Kyle McMartin 07d3322
-		retval = access_process_vm(tsk, dst, buf, this_len, 1);
Kyle McMartin 07d3322
-		if (!retval) {
Kyle McMartin 07d3322
-			if (copied)
Kyle McMartin 07d3322
-				break;
Kyle McMartin 07d3322
-			return -EIO;
Kyle McMartin 07d3322
-		}
Kyle McMartin 07d3322
-		copied += retval;
Kyle McMartin 07d3322
-		src += retval;
Kyle McMartin 07d3322
-		dst += retval;
Kyle McMartin 07d3322
-		len -= retval;
Kyle McMartin 07d3322
-	}
Kyle McMartin 07d3322
-	return copied;
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 71f70cd
-static int ptrace_setoptions(struct task_struct *child, unsigned long data)
Kyle McMartin 71f70cd
+static int ptrace_setoptions(struct task_struct *child, long data)
Kyle McMartin 07d3322
 {
Kyle McMartin 07d3322
 	child->ptrace &= ~PT_TRACE_MASK;
Kyle McMartin 71f70cd
 
Kyle McMartin 71f70cd
@@ -533,47 +683,6 @@ static int ptrace_resume(struct task_str
Kyle McMartin 07d3322
 	return 0;
Kyle McMartin 07d3322
 }
Kyle McMartin 07d3322
 
Kyle McMartin 07d3322
-#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-static const struct user_regset *
Kyle McMartin 07d3322
-find_regset(const struct user_regset_view *view, unsigned int type)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	const struct user_regset *regset;
Kyle McMartin 07d3322
-	int n;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	for (n = 0; n < view->n; ++n) {
Kyle McMartin 07d3322
-		regset = view->regsets + n;
Kyle McMartin 07d3322
-		if (regset->core_note_type == type)
Kyle McMartin 07d3322
-			return regset;
Kyle McMartin 07d3322
-	}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	return NULL;
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-static int ptrace_regset(struct task_struct *task, int req, unsigned int type,
Kyle McMartin 07d3322
-			 struct iovec *kiov)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	const struct user_regset_view *view = task_user_regset_view(task);
Kyle McMartin 07d3322
-	const struct user_regset *regset = find_regset(view, type);
Kyle McMartin 07d3322
-	int regset_no;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	if (!regset || (kiov->iov_len % regset->size) != 0)
Kyle McMartin 07d3322
-		return -EINVAL;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	regset_no = regset - view->regsets;
Kyle McMartin 07d3322
-	kiov->iov_len = min(kiov->iov_len,
Kyle McMartin 07d3322
-			    (__kernel_size_t) (regset->n * regset->size));
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	if (req == PTRACE_GETREGSET)
Kyle McMartin 07d3322
-		return copy_regset_to_user(task, view, regset_no, 0,
Kyle McMartin 07d3322
-					   kiov->iov_len, kiov->iov_base);
Kyle McMartin 07d3322
-	else
Kyle McMartin 07d3322
-		return copy_regset_from_user(task, view, regset_no, 0,
Kyle McMartin 07d3322
-					     kiov->iov_len, kiov->iov_base);
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-#endif
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
 int ptrace_request(struct task_struct *child, long request,
Kyle McMartin 1aea232
 		   unsigned long addr, unsigned long data)
Kyle McMartin 07d3322
 {
Kyle McMartin 71f70cd
@@ -689,91 +798,7 @@ int ptrace_request(struct task_struct *c
Kyle McMartin 07d3322
 	return ret;
Kyle McMartin 07d3322
 }
Kyle McMartin 07d3322
 
Kyle McMartin 07d3322
-static struct task_struct *ptrace_get_task_struct(pid_t pid)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	struct task_struct *child;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	rcu_read_lock();
Kyle McMartin 07d3322
-	child = find_task_by_vpid(pid);
Kyle McMartin 07d3322
-	if (child)
Kyle McMartin 07d3322
-		get_task_struct(child);
Kyle McMartin 07d3322
-	rcu_read_unlock();
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	if (!child)
Kyle McMartin 07d3322
-		return ERR_PTR(-ESRCH);
Kyle McMartin 07d3322
-	return child;
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-#ifndef arch_ptrace_attach
Kyle McMartin 07d3322
-#define arch_ptrace_attach(child)	do { } while (0)
Kyle McMartin 07d3322
-#endif
Kyle McMartin 07d3322
-
Kyle McMartin 1aea232
-SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,
Kyle McMartin 1aea232
-		unsigned long, data)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	struct task_struct *child;
Kyle McMartin 07d3322
-	long ret;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	if (request == PTRACE_TRACEME) {
Kyle McMartin 07d3322
-		ret = ptrace_traceme();
Kyle McMartin 07d3322
-		if (!ret)
Kyle McMartin 07d3322
-			arch_ptrace_attach(current);
Kyle McMartin 07d3322
-		goto out;
Kyle McMartin 07d3322
-	}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	child = ptrace_get_task_struct(pid);
Kyle McMartin 07d3322
-	if (IS_ERR(child)) {
Kyle McMartin 07d3322
-		ret = PTR_ERR(child);
Kyle McMartin 07d3322
-		goto out;
Kyle McMartin 07d3322
-	}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	if (request == PTRACE_ATTACH) {
Kyle McMartin 07d3322
-		ret = ptrace_attach(child);
Kyle McMartin 07d3322
-		/*
Kyle McMartin 07d3322
-		 * Some architectures need to do book-keeping after
Kyle McMartin 07d3322
-		 * a ptrace attach.
Kyle McMartin 07d3322
-		 */
Kyle McMartin 07d3322
-		if (!ret)
Kyle McMartin 07d3322
-			arch_ptrace_attach(child);
Kyle McMartin 07d3322
-		goto out_put_task_struct;
Kyle McMartin 07d3322
-	}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	ret = ptrace_check_attach(child, request == PTRACE_KILL);
Kyle McMartin 07d3322
-	if (ret < 0)
Kyle McMartin 07d3322
-		goto out_put_task_struct;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	ret = arch_ptrace(child, request, addr, data);
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
- out_put_task_struct:
Kyle McMartin 07d3322
-	put_task_struct(child);
Kyle McMartin 07d3322
- out:
Kyle McMartin 07d3322
-	return ret;
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 1aea232
-int generic_ptrace_peekdata(struct task_struct *tsk, unsigned long addr,
Kyle McMartin 1aea232
-			    unsigned long data)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	unsigned long tmp;
Kyle McMartin 07d3322
-	int copied;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
Kyle McMartin 07d3322
-	if (copied != sizeof(tmp))
Kyle McMartin 07d3322
-		return -EIO;
Kyle McMartin 07d3322
-	return put_user(tmp, (unsigned long __user *)data);
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 1aea232
-int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr,
Kyle McMartin 1aea232
-			    unsigned long data)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	int copied;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
Kyle McMartin 07d3322
-	return (copied == sizeof(data)) ? 0 : -EIO;
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
 #if defined CONFIG_COMPAT
Kyle McMartin 07d3322
-#include <linux/compat.h>
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
 int compat_ptrace_request(struct task_struct *child, compat_long_t request,
Kyle McMartin 07d3322
 			  compat_ulong_t addr, compat_ulong_t data)
Kyle McMartin 07d3322
 {
Kyle McMartin 71f70cd
@@ -851,42 +876,5 @@ int compat_ptrace_request(struct task_st
Kyle McMartin 07d3322
 
Kyle McMartin 07d3322
 	return ret;
Kyle McMartin 07d3322
 }
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
Kyle McMartin 07d3322
-				  compat_long_t addr, compat_long_t data)
Kyle McMartin 07d3322
-{
Kyle McMartin 07d3322
-	struct task_struct *child;
Kyle McMartin 07d3322
-	long ret;
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	if (request == PTRACE_TRACEME) {
Kyle McMartin 07d3322
-		ret = ptrace_traceme();
Kyle McMartin 07d3322
-		goto out;
Kyle McMartin 07d3322
-	}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	child = ptrace_get_task_struct(pid);
Kyle McMartin 07d3322
-	if (IS_ERR(child)) {
Kyle McMartin 07d3322
-		ret = PTR_ERR(child);
Kyle McMartin 07d3322
-		goto out;
Kyle McMartin 07d3322
-	}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	if (request == PTRACE_ATTACH) {
Kyle McMartin 07d3322
-		ret = ptrace_attach(child);
Kyle McMartin 07d3322
-		/*
Kyle McMartin 07d3322
-		 * Some architectures need to do book-keeping after
Kyle McMartin 07d3322
-		 * a ptrace attach.
Kyle McMartin 07d3322
-		 */
Kyle McMartin 07d3322
-		if (!ret)
Kyle McMartin 07d3322
-			arch_ptrace_attach(child);
Kyle McMartin 07d3322
-		goto out_put_task_struct;
Kyle McMartin 07d3322
-	}
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
-	ret = ptrace_check_attach(child, request == PTRACE_KILL);
Kyle McMartin 07d3322
-	if (!ret)
Kyle McMartin 07d3322
-		ret = compat_arch_ptrace(child, request, addr, data);
Kyle McMartin 07d3322
-
Kyle McMartin 07d3322
- out_put_task_struct:
Kyle McMartin 07d3322
-	put_task_struct(child);
Kyle McMartin 07d3322
- out:
Kyle McMartin 07d3322
-	return ret;
Kyle McMartin 07d3322
-}
Kyle McMartin 07d3322
 #endif	/* CONFIG_COMPAT */
Kyle McMartin 07d3322
+#endif	/* CONFIG_UTRACE */
Kyle McMartin 07d3322
diff --git a/kernel/utrace.c b/kernel/utrace.c
Kyle McMartin 71f70cd
index 26d6faf..37dce16 100644  
Kyle McMartin 07d3322
--- a/kernel/utrace.c
Kyle McMartin 07d3322
+++ b/kernel/utrace.c
Kyle McMartin 71f70cd
@@ -816,6 +816,22 @@ relock:
Kyle McMartin 07d3322
 	spin_unlock_irq(&task->sighand->siglock);
Kyle McMartin 07d3322
 	spin_unlock(&utrace->lock);
Kyle McMartin 07d3322
 
Kyle McMartin 07d3322
+	/*
Kyle McMartin 07d3322
+	 * If ptrace is among the reasons for this stop, do its
Kyle McMartin 07d3322
+	 * notification now.  This could not just be done in
Kyle McMartin 07d3322
+	 * ptrace's own event report callbacks because it has to
Kyle McMartin 07d3322
+	 * be done after we are in TASK_TRACED.  This makes the
Kyle McMartin 07d3322
+	 * synchronization with ptrace_do_wait() work right.
Kyle McMartin 07d3322
+	 *
Kyle McMartin 07d3322
+	 * It's only because of the bad old overloading of the do_wait()
Kyle McMartin 07d3322
+	 * logic for handling ptrace stops that we need this special case
Kyle McMartin 07d3322
+	 * here.  One day we will clean up ptrace so it does not need to
Kyle McMartin 07d3322
+	 * work this way.  New things that are designed sensibly don't need
Kyle McMartin 07d3322
+	 * a wakeup that synchronizes with tasklist_lock and ->state, so
Kyle McMartin 07d3322
+	 * the proper utrace API does not try to support this weirdness.
Kyle McMartin 07d3322
+	 */
Kyle McMartin 07d3322
+	ptrace_notify_stop(task);
Kyle McMartin 07d3322
+
Kyle McMartin 07d3322
 	schedule();
Kyle McMartin 07d3322
 
Kyle McMartin 07d3322
 	utrace_finish_stop();