[RFC PATCH 4/4] utrace: ptrace compatibility

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



ptrace compatibility on top of <linux/utrace.h> interfaces.
This attempts to be precisely compatible with existing ptrace behavior.
It does not extend, improve, or change it.

The ptrace support is made an option, CONFIG_PTRACE.  For now, noone will
want to turn this off except maybe a bizarre embedded configuration.  But
it looks forward to a day when we can punt the ptrace system call completely.

---

 arch/powerpc/kernel/ptrace.c    |   11 
 arch/powerpc/kernel/signal_32.c |   52 ++
 arch/powerpc/lib/sstep.c        |    3 
 arch/x86_64/ia32/ia32entry.S    |    2 
 include/asm-i386/tracehook.h    |   25 +
 include/asm-powerpc/tracehook.h |  226 +++++++
 include/asm-x86_64/tracehook.h  |   53 ++
 include/linux/ptrace.h          |   65 +-
 include/linux/sched.h           |    5 
 init/Kconfig                    |   12 
 kernel/exit.c                   |   13 
 kernel/fork.c                   |    3 
 kernel/ptrace.c                 | 1208 +++++++++++++++++++++++++++++++++++----
 13 files changed, 1520 insertions(+), 158 deletions(-)

diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 6036f93..4f50020 100644  
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -410,8 +410,7 @@ static const struct utrace_regset native
 
 static const struct ptrace_uarea_segment native_uarea[] = {
 	{0, PT_FPR0 * sizeof(long), 0, 0},
-	{PT_FPR0 * sizeof(long),
-	 (PT_FPSCR + 1 - PT_FPR0) * sizeof(long), 1, 0},
+	{PT_FPR0 * sizeof(long), (PT_FPSCR + 1) * sizeof(long), 1, 0},
 	{0, 0, -1, 0}
 };
 
@@ -532,7 +531,7 @@ static const struct utrace_regset ppc32_
 
 static const struct ptrace_uarea_segment ppc32_uarea[] = {
 	{0, PT_FPR0 * sizeof(u32), 0, 0},
-	{PT_FPR0 * sizeof(u32), (PT_FPSCR + 1 - PT_FPR0) * sizeof(u32), 1, 0},
+	{PT_FPR0 * sizeof(u32), (PT_FPSCR + 1) * sizeof(u32), 1, 0},
 	{0, 0, -1, 0}
 };
 
@@ -543,12 +542,6 @@ const struct utrace_regset_view utrace_p
 	.uarea_segments = ppc32_uarea
 };
 EXPORT_SYMBOL_GPL(utrace_ppc32_view);
-
-long compat_sys_ptrace(int request, int pid, unsigned long addr,
-		       unsigned long data)
-{
-	return -ENOSYS;
-}
 #endif
 
 void do_syscall_trace_enter(struct pt_regs *regs)
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 1b8cd82..3d0447b 100644  
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -629,6 +629,58 @@ int copy_siginfo_to_user32(struct compat
 
 #define copy_siginfo_to_user	copy_siginfo_to_user32
 
+/* mostly stolen from arch/s390/kernel/compat_signal.c */
+int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
+{
+	int err;
+	u32 tmp;
+
+	if (!access_ok (VERIFY_READ, from, sizeof(compat_siginfo_t)))
+		return -EFAULT;
+
+	err = __get_user(to->si_signo, &from->si_signo);
+	err |= __get_user(to->si_errno, &from->si_errno);
+	err |= __get_user(to->si_code, &from->si_code);
+
+	if (to->si_code < 0)
+		err |= __copy_from_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE);
+	else {
+		switch (to->si_code >> 16) {
+		case __SI_RT >> 16: /* This is not generated by the kernel as of now.  */
+		case __SI_MESGQ >> 16:
+			err |= __get_user(to->si_int, &from->si_int);
+			/* fallthrough */
+		case __SI_KILL >> 16:
+			err |= __get_user(to->si_pid, &from->si_pid);
+			err |= __get_user(to->si_uid, &from->si_uid);
+			break;
+		case __SI_CHLD >> 16:
+			err |= __get_user(to->si_pid, &from->si_pid);
+			err |= __get_user(to->si_uid, &from->si_uid);
+			err |= __get_user(to->si_utime, &from->si_utime);
+			err |= __get_user(to->si_stime, &from->si_stime);
+			err |= __get_user(to->si_status, &from->si_status);
+			break;
+		case __SI_FAULT >> 16:
+			err |= __get_user(tmp, &from->si_addr);
+			to->si_addr = (void __user *)(u64) tmp;
+			break;
+		case __SI_POLL >> 16:
+			err |= __get_user(to->si_band, &from->si_band);
+			err |= __get_user(to->si_fd, &from->si_fd);
+			break;
+		case __SI_TIMER >> 16:
+			err |= __get_user(to->si_tid, &from->si_tid);
+			err |= __get_user(to->si_overrun, &from->si_overrun);
+			err |= __get_user(to->si_int, &from->si_int);
+			break;
+		default:
+			break;
+		}
+	}
+	return err;
+}
+
 /*
  * Note: it is necessary to treat pid and sig as unsigned ints, with the
  * corresponding cast to a signed int to insure that the proper conversion
diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c
index 666c2aa..400f81a 100644  
--- a/arch/powerpc/lib/sstep.c
+++ b/arch/powerpc/lib/sstep.c
@@ -13,6 +13,9 @@
 #include <linux/config.h>
 #include <asm/sstep.h>
 #include <asm/processor.h>
+#ifdef CONFIG_PPC64
+#include <asm/paca.h>
+#endif
 
 extern char system_call_common[];
 
diff --git a/arch/x86_64/ia32/ia32entry.S b/arch/x86_64/ia32/ia32entry.S
index 00dee17..4ad17e3 100644  
--- a/arch/x86_64/ia32/ia32entry.S
+++ b/arch/x86_64/ia32/ia32entry.S
@@ -399,7 +399,7 @@ ia32_sys_call_table:
 	.quad sys_setuid16
 	.quad sys_getuid16
 	.quad compat_sys_stime	/* stime */		/* 25 */
-	.quad sys32_ptrace	/* ptrace */
+	.quad compat_sys_ptrace	/* ptrace */
 	.quad sys_alarm
 	.quad sys_fstat	/* (old)fstat */
 	.quad sys_pause
diff --git a/include/asm-i386/tracehook.h b/include/asm-i386/tracehook.h
index 423ab18..09233b0 100644  
--- a/include/asm-i386/tracehook.h
+++ b/include/asm-i386/tracehook.h
@@ -45,4 +45,29 @@ utrace_native_view(struct task_struct *t
 }
 
 
+#ifdef CONFIG_PTRACE
+static inline int arch_ptrace(long request, struct task_struct *child,
+			      unsigned long addr, long data,
+			      const struct utrace_regset_view **view,
+			      int *regset, int *rw,
+			      void __user **uaddr, int *nregs)
+{
+	switch (request) {
+	case PTRACE_GETFPXREGS:
+	case PTRACE_SETFPXREGS:
+		*regset = 2;
+		*rw = request == PTRACE_SETFPXREGS;
+		break;
+	case PTRACE_GET_THREAD_AREA:
+	case PTRACE_SET_THREAD_AREA:
+		*regset = 3;
+		*nregs = -1;
+		*rw = request == PTRACE_SET_THREAD_AREA;
+		break;
+	}
+	return -ENOSYS;
+}
+#endif	/* CONFIG_PTRACE */
+
+
 #endif
diff --git a/include/asm-powerpc/tracehook.h b/include/asm-powerpc/tracehook.h
index 9c86d24..3811515 100644  
--- a/include/asm-powerpc/tracehook.h
+++ b/include/asm-powerpc/tracehook.h
@@ -62,10 +62,10 @@ static inline void tracehook_abort_sysca
 }
 
 
+extern const struct utrace_regset_view utrace_ppc_native_view;
 static inline const struct utrace_regset_view *
 utrace_native_view(struct task_struct *tsk)
 {
-	extern const struct utrace_regset_view utrace_ppc_native_view;
 #ifdef CONFIG_PPC64
 	extern const struct utrace_regset_view utrace_ppc32_view;
 
@@ -75,4 +75,228 @@ utrace_native_view(struct task_struct *t
 	return &utrace_ppc_native_view;
 }
 
+
+#ifdef CONFIG_PTRACE
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+
+static inline int arch_ptrace(long request, struct task_struct *child,
+			      unsigned long addr, long data,
+			      const struct utrace_regset_view **view,
+			      int *regset, int *rw,
+			      void __user **uaddr, int *nregs)
+{
+	switch (request) {
+#ifdef CONFIG_PPC64
+	case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
+	case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
+		*regset = 0;
+		*rw = request == PPC_PTRACE_SETREGS;
+		*nregs = 32;
+		*uaddr = (void __user *)addr;
+		break;
+	case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */
+	case PPC_PTRACE_SETFPREGS: /* Get FPRs 0 - 31. */
+		*regset = 1;
+		*rw = request == PPC_PTRACE_SETFPREGS;
+		*uaddr = (void __user *)addr;
+		*nregs = 32;
+		break;
+	case PTRACE_GET_DEBUGREG:
+	case PTRACE_SET_DEBUGREG:
+		*regset = 3;
+		*nregs = -1;
+		*rw = request == PTRACE_SET_DEBUGREG;
+		break;
+#endif /* CONFIG_PPC64 */
+#ifdef CONFIG_ALTIVEC
+	case PTRACE_GETVRREGS:
+	case PTRACE_SETVRREGS:
+		*regset = 2;
+		*rw = request == PTRACE_SETVRREGS;
+		break;
+#endif
+#ifdef CONFIG_SPE
+	case PTRACE_GETEVRREGS:
+	case PTRACE_SETEVRREGS:
+#ifdef CONFIG_ALTIVEC
+		*regset = 3;
+#else
+		*regset = 2;
+#endif
+		*rw = request == PTRACE_SETEVRREGS;
+		break;
+#endif
+
+	}
+	return -ENOSYS;
+}
+
+static inline int arch_compat_ptrace(long request, struct task_struct *child,
+				     unsigned long addr, long data,
+				     const struct utrace_regset_view **view,
+				     int *regset, int *rw,
+				     void __user **uaddr, int *nregs)
+{
+	int ret = -ENOSYS;
+
+	switch (request) {
+#ifdef CONFIG_PPC64
+	case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
+	case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
+		*regset = 0;
+		*rw = request == PPC_PTRACE_SETREGS;
+		*nregs = 32;
+		*uaddr = (void __user *)addr;
+		break;
+	case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */
+	case PPC_PTRACE_SETFPREGS: /* Get FPRs 0 - 31. */
+		*regset = 1;
+		*rw = request == PPC_PTRACE_SETFPREGS;
+		*uaddr = (void __user *)addr;
+		*nregs = 32;
+		break;
+	case PTRACE_GET_DEBUGREG:
+	case PTRACE_SET_DEBUGREG:
+		*regset = 3;
+		*nregs = -1;
+		*rw = request == PTRACE_SET_DEBUGREG;
+		break;
+
+	/*
+	 * Read 4 bytes of the other process' storage
+	 *  data is a pointer specifying where the user wants the
+	 *	4 bytes copied into
+	 *  addr is a pointer in the user's storage that contains an 8 byte
+	 *	address in the other process of the 4 bytes that is to be read
+	 * (this is run in a 32-bit process looking at a 64-bit process)
+	 * when I and D space are separate, these will need to be fixed.
+	 */
+	case PPC_PTRACE_PEEKTEXT_3264:
+	case PPC_PTRACE_PEEKDATA_3264: {
+		u32 tmp;
+		int copied;
+		u32 __user * addrOthers;
+
+		ret = -EIO;
+
+		/* Get the addr in the other process that we want to read */
+		if (get_user(addrOthers, (u32 __user * __user *)addr) != 0)
+			break;
+
+		copied = access_process_vm(child, (u64)addrOthers, &tmp,
+				sizeof(tmp), 0);
+		if (copied != sizeof(tmp))
+			break;
+		ret = put_user(tmp, (u32 __user *)data);
+		break;
+	}
+
+	/*
+	 * Write 4 bytes into the other process' storage
+	 *  data is the 4 bytes that the user wants written
+	 *  addr is a pointer in the user's storage that contains an
+	 *	8 byte address in the other process where the 4 bytes
+	 *	that is to be written
+	 * (this is run in a 32-bit process looking at a 64-bit process)
+	 * when I and D space are separate, these will need to be fixed.
+	 */
+	case PPC_PTRACE_POKETEXT_3264:
+	case PPC_PTRACE_POKEDATA_3264: {
+		u32 tmp = data;
+		u32 __user * addrOthers;
+
+		/* Get the addr in the other process that we want to write into */
+		ret = -EIO;
+		if (get_user(addrOthers, (u32 __user * __user *)addr) != 0)
+			break;
+		ret = 0;
+		if (access_process_vm(child, (u64)addrOthers, &tmp,
+					sizeof(tmp), 1) == sizeof(tmp))
+			break;
+		ret = -EIO;
+		break;
+	}
+
+	/*
+	 * This is like PTRACE_PEEKUSR on a 64-bit process,
+	 * but here we access only 4 bytes at a time.
+	 */
+	case PPC_PTRACE_PEEKUSR_3264: {
+		union
+		{
+			u64 whole;
+			u32 half[2];
+		} reg;
+		int setno;
+		const struct utrace_regset *regset;
+
+		ret = -EIO;
+		if ((addr & 3) || addr > PT_FPSCR*8)
+			break;
+
+		setno = 0;
+		if (addr >= PT_FPR0*8) {
+			setno = 1;
+			addr -= PT_FPR0*8;
+		}
+		regset = utrace_regset(child, NULL,
+				       &utrace_ppc_native_view, setno);
+		ret = (*regset->get)(child, regset, addr &~ 7,
+				     sizeof(reg.whole), &reg.whole, NULL);
+		if (ret == 0)
+			ret = put_user(reg.half[(addr >> 2) & 1],
+				       (u32 __user *)data);
+		break;
+	}
+
+	/*
+	 * This is like PTRACE_POKEUSR on a 64-bit process,
+	 * but here we access only 4 bytes at a time.
+	 */
+	case PPC_PTRACE_POKEUSR_3264: {
+		union
+		{
+			u64 whole;
+			u32 half[2];
+		} reg;
+		int setno;
+		const struct utrace_regset *regset;
+
+		ret = -EIO;
+		if ((addr & 3) || addr > PT_FPSCR*8)
+			break;
+
+		setno = 0;
+		if (addr >= PT_FPR0*8) {
+			setno = 1;
+			addr -= PT_FPR0*8;
+		}
+		regset = utrace_regset(child, NULL,
+				       &utrace_ppc_native_view, setno);
+		ret = (*regset->get)(child, regset, addr &~ 7,
+				     sizeof(reg.whole), &reg.whole, NULL);
+		BUG_ON(ret);
+		reg.half[(addr >> 2) & 1] = data;
+		ret = (*regset->set)(child, regset, addr &~ 7,
+				     sizeof(reg.whole), &reg.whole, NULL);
+		break;
+	}
+
+#endif /* CONFIG_PPC64 */
+#ifdef CONFIG_ALTIVEC
+	case PTRACE_GETVRREGS:
+	case PTRACE_SETVRREGS:
+		*regset = 2;
+		*rw = request == PTRACE_SETVRREGS;
+		break;
+#endif
+
+	}
+	return ret;
+}
+
+#endif	/* CONFIG_PTRACE */
+
+
 #endif
diff --git a/include/asm-x86_64/tracehook.h b/include/asm-x86_64/tracehook.h
index d710df0..e4f7afa 100644  
--- a/include/asm-x86_64/tracehook.h
+++ b/include/asm-x86_64/tracehook.h
@@ -7,6 +7,7 @@
 
 #include <linux/sched.h>
 #include <asm/ptrace.h>
+#include <asm/proto.h>
 
 /*
  * See linux/tracehook.h for the descriptions of what these need to do.
@@ -49,4 +50,56 @@ utrace_native_view(struct task_struct *t
 }
 
 
+#ifdef CONFIG_PTRACE
+static inline int arch_ptrace(long request, struct task_struct *child,
+			      unsigned long addr, long data,
+			      const struct utrace_regset_view **view,
+			      int *regset, int *rw,
+			      void __user **uaddr, int *nregs)
+{
+	switch (request) {
+#ifdef CONFIG_IA32_EMULATION
+	case PTRACE_GET_THREAD_AREA:
+	case PTRACE_SET_THREAD_AREA:
+		*view = &utrace_ia32_view;
+		*regset = 3;
+		*nregs = -1;
+		*rw = request == PTRACE_SET_THREAD_AREA;
+		break;
+#endif
+		/* normal 64bit interface to access TLS data.
+		   Works just like arch_prctl, except that the arguments
+		   are reversed. */
+	case PTRACE_ARCH_PRCTL:
+		return do_arch_prctl(child, data, addr);
+	}
+	return -ENOSYS;
+}
+
+#ifdef CONFIG_IA32_EMULATION
+static inline int arch_compat_ptrace(s32 request, struct task_struct *child,
+				     u32 addr, s32 data,
+				     const struct utrace_regset_view **view,
+				     int *regset, int *rw,
+				     void __user **uaddr, int *nregs)
+{
+	switch (request) {
+	case PTRACE_GETFPXREGS:
+	case PTRACE_SETFPXREGS:
+		*regset = 2;
+		*rw = request == PTRACE_SETFPXREGS;
+		break;
+	case PTRACE_GET_THREAD_AREA:
+	case PTRACE_SET_THREAD_AREA:
+		*regset = 3;
+		*nregs = -1;
+		*rw = request == PTRACE_SET_THREAD_AREA;
+		break;
+	}
+	return -ENOSYS;
+}
+#endif	/* CONFIG_IA32_EMULATION */
+#endif	/* CONFIG_PTRACE */
+
+
 #endif
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 270db62..a2c6b34 100644  
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -49,45 +49,38 @@
 #include <asm/ptrace.h>
 
 #ifdef __KERNEL__
+#include <linux/compiler.h>
+#include <linux/types.h>
+struct task_struct;
+struct siginfo;
+struct rusage;
+
+#ifdef CONFIG_PTRACE
 /*
- * Ptrace flags
+ * Called in do_exit, after setting PF_EXITING, no locks are held.
  */
+void ptrace_exit(struct task_struct *tsk);
 
-#define PT_PTRACED	0x00000001
-#define PT_DTRACE	0x00000002	/* delayed trace (used on m68k, i386) */
-#define PT_TRACESYSGOOD	0x00000004
-#define PT_PTRACE_CAP	0x00000008	/* ptracer can follow suid-exec */
-#define PT_TRACE_FORK	0x00000010
-#define PT_TRACE_VFORK	0x00000020
-#define PT_TRACE_CLONE	0x00000040
-#define PT_TRACE_EXEC	0x00000080
-#define PT_TRACE_VFORK_DONE	0x00000100
-#define PT_TRACE_EXIT	0x00000200
-
-#define PT_TRACE_MASK	0x000003f4
-
-/* single stepping state bits (used on ARM and PA-RISC) */
-#define PT_SINGLESTEP_BIT	31
-#define PT_SINGLESTEP		(1<<PT_SINGLESTEP_BIT)
-#define PT_BLOCKSTEP_BIT	30
-#define PT_BLOCKSTEP		(1<<PT_BLOCKSTEP_BIT)
-
-#include <linux/compiler.h>		/* For unlikely.  */
-#include <linux/sched.h>		/* For struct task_struct.  */
-
-
-extern long arch_ptrace(struct task_struct *child, long request, long addr, long data);
-extern struct task_struct *ptrace_get_task_struct(pid_t pid);
-extern int ptrace_traceme(void);
-extern int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len);
-extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len);
-extern int ptrace_attach(struct task_struct *tsk);
-extern int ptrace_detach(struct task_struct *, unsigned int);
-extern void __ptrace_detach(struct task_struct *, unsigned int);
-extern void ptrace_disable(struct task_struct *);
-extern int ptrace_check_attach(struct task_struct *task, int kill);
-extern int ptrace_request(struct task_struct *child, long request, long addr, long data);
-extern int ptrace_may_attach(struct task_struct *task);
+/*
+ * Called in do_wait, with tasklist_lock held for reading.
+ * This reports any ptrace-child that is ready as do_wait would a normal child.
+ * If there are no ptrace children, returns -ECHILD.
+ * If there are some ptrace children but none reporting now, returns 0.
+ */
+int ptrace_do_wait(struct task_struct *tsk,
+		   pid_t pid, int options, struct siginfo __user *infop,
+		   int __user *stat_addr, struct rusage __user *rusagep);
+#else
+static inline void ptrace_exit(struct task_struct *tsk) { }
+static inline int ptrace_do_wait(struct task_struct *tsk,
+				 pid_t pid, int options,
+				 struct siginfo __user *infop,
+				 int __user *stat_addr,
+				 struct rusage __user *rusagep)
+{
+	return -ECHILD;
+}
+#endif
 
 
 #ifndef force_successful_syscall_return
diff --git a/include/linux/sched.h b/include/linux/sched.h
index b5ae996..4d0debb 100644  
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -864,7 +864,12 @@ struct task_struct {
 	int cpuset_mems_generation;
 #endif
 	atomic_t fs_excl;	/* holding fs exclusive resources */
+
 	struct rcu_head rcu;
+
+#ifdef CONFIG_PTRACE
+	struct list_head ptracees;
+#endif
 };
 
 static inline pid_t process_group(struct task_struct *tsk)
diff --git a/init/Kconfig b/init/Kconfig
index 21d6806..73d5021 100644  
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -526,6 +526,18 @@ config UTRACE
 	  applications.  Unless you are making a specially stripped-down
 	  kernel and are very sure you don't need these facilitiies,
 	  say Y.
+
+config PTRACE
+	bool "Legacy ptrace system call interface"
+	default y
+	depends on UTRACE
+	help
+	  Enable the ptrace system call.
+	  This is traditionally used by debuggers like GDB,
+	  and is used by UML and some other applications.
+	  Unless you are very sure you won't run anything that needs it,
+	  say Y.
+
 endmenu
 
 menu "Block layer"
diff --git a/kernel/exit.c b/kernel/exit.c
index b342369..3598b59 100644  
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -21,6 +21,7 @@
 #include <linux/acct.h>
 #include <linux/file.h>
 #include <linux/binfmts.h>
+#include <linux/ptrace.h>
 #include <linux/tracehook.h>
 #include <linux/profile.h>
 #include <linux/mount.h>
@@ -736,6 +737,8 @@ fastcall NORET_TYPE void do_exit(long co
 
 	tsk->flags |= PF_EXITING;
 
+	ptrace_exit(tsk);
+
 	/*
 	 * Make sure we don't try to process any timer firings
 	 * while we are already exiting.
@@ -1326,8 +1329,14 @@ check_continued:
 				break;
 			}
 		}
-		if (!flag) {
-			// XXX set flag if we have ptracees
+		retval = ptrace_do_wait(tsk, pid, options,
+					infop, stat_addr, ru);
+		if (retval != -ECHILD) {
+			flag = 1;
+			if (retval) {
+				read_unlock(&tasklist_lock);
+				goto end;
+			}
 		}
 		if (options & __WNOTHREAD)
 			break;
diff --git a/kernel/fork.c b/kernel/fork.c
index 970e076..5296cc0 100644  
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -985,6 +985,9 @@ static task_t *copy_process(unsigned lon
 	p->vfork_done = NULL;
 	spin_lock_init(&p->alloc_lock);
 	spin_lock_init(&p->proc_lock);
+#ifdef CONFIG_PTRACE
+	INIT_LIST_HEAD(&p->ptracees);
+#endif
 
 	clear_tsk_thread_flag(p, TIF_SIGPENDING);
 	init_sigpending(&p->pending);
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 2b8eae1..1f73865 100644  
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -22,14 +22,15 @@
 #include <asm/pgtable.h>
 #include <asm/uaccess.h>
 
+#ifdef CONFIG_PTRACE
+#include <linux/utrace.h>
+#include <linux/tracehook.h>
+#include <asm/tracehook.h>
+#endif
 
-/*
- * Check that we have indeed attached to the thing..
- */
-int ptrace_check_attach(struct task_struct *child, int kill)
-{
-	return -ENOSYS;
-}
+int getrusage(struct task_struct *, int, struct rusage __user *);
+
+//#define PTRACE_DEBUG
 
 static int may_attach(struct task_struct *task)
 {
@@ -58,36 +59,6 @@ int ptrace_may_attach(struct task_struct
 	return !err;
 }
 
-int ptrace_attach(struct task_struct *task)
-{
-	int retval;
-
-	retval = -EPERM;
-	if (task->pid <= 1)
-		goto out;
-	if (task->tgid == current->tgid)
-		goto bad;
-	retval = may_attach(task);
-	if (retval)
-		goto bad;
-
-	retval = -ENOSYS;
-
-bad:
-	write_unlock_irq(&tasklist_lock);
-	task_unlock(task);
-out:
-	return retval;
-}
-
-int ptrace_detach(struct task_struct *child, unsigned int data)
-{
-	if (!valid_signal(data))
-		return -EIO;
-
-	return -ENOSYS;
-}
-
 /*
  * Access another process' address space.
  * Source/target buffer must be kernel space,
@@ -142,110 +113,1129 @@ int access_process_vm(struct task_struct
 	return buf - old_buf;
 }
 
-int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
+
+#ifndef CONFIG_PTRACE
+
+asmlinkage long sys_ptrace(long request, long pid, long addr, long data)
 {
-	int copied = 0;
+	return -ENOSYS;
+}
 
-	while (len > 0) {
-		char buf[128];
-		int this_len, retval;
-
-		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
-		retval = access_process_vm(tsk, src, buf, this_len, 0);
-		if (!retval) {
-			if (copied)
-				break;
-			return -EIO;
-		}
-		if (copy_to_user(dst, buf, retval))
-			return -EFAULT;
-		copied += retval;
-		src += retval;
-		dst += retval;
-		len -= retval;
+#else
+
+struct ptrace_state
+{
+	union {
+		struct rcu_head dead;
+		struct {
+			struct utrace_attached_engine *engine;
+			struct task_struct *task; /* Target task.  */
+			struct task_struct *parent; /* Whom we report to.  */
+
+			u8 options; /* PTRACE_SETOPTIONS bits.  */
+			unsigned int stopped:1;	/* Stopped for report.  */
+			unsigned int reported:1; /* wait already reported.  */
+			unsigned int syscall:1;	/* Reporting for syscall.  */
+#ifdef PTRACE_SYSEMU
+			unsigned int sysemu:1; /* PTRACE_SYSEMU in progress. */
+#endif
+			unsigned int have_eventmsg:1; /* u.eventmsg valid. */
+
+			union
+			{
+				unsigned long eventmsg;
+				siginfo_t *siginfo;
+			} u;
+		} live;
+	} u;
+	struct list_head entry;	/* Entry on parent->ptracees list.  */
+};
+
+static const struct utrace_engine_ops ptrace_utrace_ops; /* Initialized below. */
+
+
+static void
+ptrace_state_link(struct ptrace_state *state)
+{
+	task_lock(state->u.live.parent);
+	list_add_rcu(&state->entry, &state->u.live.parent->ptracees);
+	task_unlock(state->u.live.parent);
+}
+
+static void
+ptrace_state_unlink(struct ptrace_state *state)
+{
+	task_lock(state->u.live.parent);
+	list_del_rcu(&state->entry);
+	task_unlock(state->u.live.parent);
+}
+
+static int
+ptrace_setup(struct task_struct *target, struct utrace_attached_engine *engine,
+	     struct task_struct *parent, u8 options)
+{
+	struct ptrace_state *state = kzalloc(sizeof *state, GFP_USER);
+	if (unlikely(state == NULL))
+		return -ENOMEM;
+
+	state->u.live.engine = engine;
+	state->u.live.task = target;
+	state->u.live.parent = parent;
+	state->u.live.options = options;
+	ptrace_state_link(state);
+
+	BUG_ON(engine->data != 0);
+	rcu_assign_pointer(engine->data, (unsigned long) state);
+
+	return 0;
+}
+
+static void
+ptrace_state_free(struct rcu_head *rhead)
+{
+	struct ptrace_state *state = container_of(rhead,
+						  struct ptrace_state, u.dead);
+	kfree(state);
+}
+
+static void
+ptrace_done(struct ptrace_state *state)
+{
+	INIT_RCU_HEAD(&state->u.dead);
+	call_rcu(&state->u.dead, ptrace_state_free);
+}
+
+/*
+ * Update the tracing engine state to match the new ptrace state.
+ */
+static void
+ptrace_update(struct task_struct *target, struct utrace_attached_engine *engine,
+	      unsigned long flags)
+{
+	struct ptrace_state *state = (struct ptrace_state *) engine->data;
+
+	/*
+	 * These events are always reported.
+	 */
+	flags |= (UTRACE_EVENT(DEATH) | UTRACE_EVENT(EXEC)
+		  | UTRACE_EVENT_SIGNAL_ALL);
+
+	/*
+	 * We always have to examine clone events to check for CLONE_PTRACE.
+	 */
+	flags |= UTRACE_EVENT(CLONE);
+
+	/*
+	 * PTRACE_SETOPTIONS can request more events.
+	 */
+	if (state->u.live.options & PTRACE_O_TRACEEXIT)
+		flags |= UTRACE_EVENT(EXIT);
+	if (state->u.live.options & PTRACE_O_TRACEVFORKDONE)
+		flags |= UTRACE_EVENT(VFORK_DONE);
+
+	/*
+	 * ptrace always inhibits normal parent reaping.
+	 */
+	flags |= UTRACE_ACTION_NOREAP;
+
+	state->u.live.stopped = (flags & UTRACE_ACTION_QUIESCE) != 0;
+	if (!state->u.live.stopped) {
+		if (!state->u.live.have_eventmsg)
+			state->u.live.u.siginfo = NULL;
+		if (!(target->flags & PF_EXITING))
+			target->exit_code = 0;
 	}
-	return copied;
+	utrace_set_flags(target, engine, flags);
 }
 
-int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
+static int ptrace_traceme(void)
 {
-	int copied = 0;
+	struct utrace_attached_engine *engine;
+	int retval;
 
-	while (len > 0) {
-		char buf[128];
-		int this_len, retval;
+	engine = utrace_attach(current, (UTRACE_ATTACH_CREATE
+					 | UTRACE_ATTACH_EXCLUSIVE
+					 | UTRACE_ATTACH_MATCH_OPS),
+			       &ptrace_utrace_ops, 0UL);
+
+	if (IS_ERR(engine)) {
+		retval = PTR_ERR(engine);
+		if (retval == -EEXIST)
+			retval = -EPERM;
+	}
+	else {
+		retval = security_ptrace(current->parent, current);
+		if (!retval)
+			retval = ptrace_setup(current, engine,
+					      current->parent, 0);
+		if (retval)
+			utrace_detach(current, engine);
+		else
+			ptrace_update(current, engine, 0);
+	}
 
-		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
-		if (copy_from_user(buf, src, this_len))
-			return -EFAULT;
-		retval = access_process_vm(tsk, dst, buf, this_len, 1);
-		if (!retval) {
-			if (copied)
-				break;
-			return -EIO;
-		}
-		copied += retval;
-		src += retval;
-		dst += retval;
-		len -= retval;
+	return retval;
+}
+
+static int ptrace_attach(struct task_struct *task)
+{
+	struct utrace_attached_engine *engine;
+	int retval;
+
+	retval = -EPERM;
+	if (task->pid <= 1)
+		goto bad;
+	if (task->tgid == current->tgid)
+		goto bad;
+
+	engine = utrace_attach(task, (UTRACE_ATTACH_CREATE
+				      | UTRACE_ATTACH_EXCLUSIVE
+				      | UTRACE_ATTACH_MATCH_OPS),
+			       &ptrace_utrace_ops, 0);
+	if (IS_ERR(engine)) {
+		retval = PTR_ERR(engine);
+		if (retval == -EEXIST)
+			retval = -EPERM;
+		goto bad;
+	}
+
+	if (ptrace_may_attach(task))
+		retval = ptrace_setup(task, engine, current, 0);
+	if (retval)
+		utrace_detach(task, engine);
+	else {
+		/* Go */
+		ptrace_update(task, engine, 0);
+		force_sig_specific(SIGSTOP, task);
 	}
-	return copied;
+
+bad:
+	return retval;
 }
 
-int ptrace_request(struct task_struct *child, long request,
-		   long addr, long data)
+static int ptrace_detach(struct task_struct *task,
+			 struct utrace_attached_engine *engine)
 {
-	return -ENOSYS;
+	struct ptrace_state *state = (struct ptrace_state *) engine->data;
+	utrace_detach(task, engine);
+	ptrace_state_unlink(state);
+	ptrace_done(state);
+	return 0;
 }
 
-/**
- * ptrace_traceme  --  helper for PTRACE_TRACEME
- *
- * Performs checks and sets PT_PTRACED.
- * Should be used by all ptrace implementations for PTRACE_TRACEME.
+
+/*
+ * This is called when we are exiting.  We must stop all our ptracing.
  */
-int ptrace_traceme(void)
+void
+ptrace_exit(struct task_struct *tsk)
 {
-	int ret = -EPERM;
+	struct ptrace_state *state, *next;
+	task_lock(tsk);
+	list_for_each_entry_safe(state, next, &tsk->ptracees, entry) {
+		list_del_rcu(&state->entry);
+		utrace_detach(state->u.live.task, state->u.live.engine);
+		ptrace_done(state);
+	}
+	task_unlock(tsk);
+	BUG_ON(!list_empty(&tsk->ptracees));
+}
 
-	ret = security_ptrace(current->parent, current);
-	if (ret)
-		return -EPERM;
+static int
+ptrace_induce_signal(struct task_struct *target,
+		     struct utrace_attached_engine *engine,
+		     long signr)
+{
+	struct ptrace_state *state = (struct ptrace_state *) engine->data;
 
-	return -ENOSYS;
+	if (signr == 0)
+		return 0;
+
+	if (!valid_signal(signr))
+		return -EIO;
+
+	if (state->u.live.syscall) {
+		/*
+		 * This is the traditional ptrace behavior when given
+		 * a signal to resume from a syscall tracing stop.
+		 */
+		send_sig(signr, target, 1);
+	}
+	else if (!state->u.live.have_eventmsg && state->u.live.u.siginfo) {
+		siginfo_t *info = state->u.live.u.siginfo;
+
+		/* Update the siginfo structure if the signal has
+		   changed.  If the debugger wanted something
+		   specific in the siginfo structure then it should
+		   have updated *info via PTRACE_SETSIGINFO.  */
+		if (signr != info->si_signo) {
+			info->si_signo = signr;
+			info->si_errno = 0;
+			info->si_code = SI_USER;
+			info->si_pid = current->pid;
+			info->si_uid = current->uid;
+		}
+
+		return utrace_inject_signal(target, engine,
+					    UTRACE_ACTION_RESUME, info, NULL);
+	}
+
+	return 0;
 }
 
-/**
- * ptrace_get_task_struct  --  grab a task struct reference for ptrace
- * @pid:       process id to grab a task_struct reference of
- *
- * This function is a helper for ptrace implementations.  It checks
- * permissions and then grabs a task struct for use of the actual
- * ptrace implementation.
- *
- * Returns the task_struct for @pid or an ERR_PTR() on failure.
- */
-struct task_struct *ptrace_get_task_struct(pid_t pid)
+static int
+ptrace_regset(struct task_struct *target,
+	      struct utrace_attached_engine *engine,
+	      const struct utrace_regset_view *view, int which,
+	      int write, int nregs, unsigned long addr, void __user *datap)
+{
+	const struct utrace_regset *regset = utrace_regset(target, engine,
+							   view, which);
+	int ret;
+	unsigned int pos = 0, n;
+
+#ifdef PTRACE_DEBUG
+	printk("%d ptrace_regset on %d view %p regset %p (%d/%d) +%lx\n",
+	       current->pid, target->pid, view, regset, write, nregs, addr);
+#endif
+
+	if (unlikely(regset == NULL))
+		return -EIO;
+
+	if (nregs < 0) {
+		if (addr < regset->bias || addr >= regset->bias + regset->n)
+			return -EINVAL;
+		pos = (addr - regset->bias) * regset->size;
+		n = 1;
+	}
+	else
+		n = nregs == 0 ? regset->n : nregs;
+
+	n *= regset->size;
+	if (write) {
+		if (!access_ok(VERIFY_READ, datap, n))
+			ret = -EIO;
+		else
+			ret = (*regset->set)(target, regset, pos, n,
+					     NULL, datap);
+	}
+	else {
+		if (!access_ok(VERIFY_WRITE, datap, n))
+			ret = -EIO;
+		else
+			ret = (*regset->get)(target, regset, pos, n,
+					     NULL, datap);
+	}
+
+	return ret;
+}
+
+static int
+ptrace_uarea(struct task_struct *target,
+	     struct utrace_attached_engine *engine,
+	     int write, unsigned long addr, long data)
+{
+	const struct utrace_regset_view *view = utrace_native_view(current);
+	const struct ptrace_uarea_segment *seg = view->uarea_segments;
+	const struct utrace_regset *regset;
+	int ret = -EIO;
+
+	if (unlikely(seg == NULL))
+		goto out;
+
+	while (addr >= seg->end && seg->end != 0)
+		++seg;
+
+	if (addr < seg->start || addr >= seg->end)
+		goto out;
+	addr -= seg->start;
+	addr += seg->offset;
+
+	regset = utrace_regset(target, engine, view, seg->regset);
+	if (unlikely(regset == NULL))
+		goto out;
+
+	if ((addr & (regset->align - 1))
+	    || addr > (regset->n - 1) * regset->size)
+		goto out;
+
+	if (write) {
+		if (likely(regset->size == sizeof(data)))
+			ret = (*regset->set)(target, regset,
+					     addr, sizeof(data), &data, NULL);
+		else
+			switch (regset->size) {
+				s32 data4;
+				s64 data8;
+			case 4:
+				data4 = data;
+				ret = (*regset->set)(target, regset,
+						     addr, 4, &data4, NULL);
+				break;
+			case 8:
+				data8 = data;
+				ret = (*regset->set)(target, regset,
+						     addr, 8, &data8, NULL);
+				break;
+			default:
+				BUG();
+			}
+	}
+	else {
+		void *datap = (void *) data;
+		if (likely(access_ok(VERIFY_WRITE, datap, regset->size)))
+			ret = (*regset->get)(target, regset,
+					     addr, regset->size, NULL, datap);
+	}
+
+out:
+	return ret;
+}
+
+static int
+ptrace_start(long pid, long request,
+	     struct task_struct **childp,
+	     struct utrace_attached_engine **enginep,
+	     struct ptrace_state **statep)
+
 {
 	struct task_struct *child;
+	struct utrace_attached_engine *engine;
+	struct ptrace_state *state;
+	int ret;
 
-	/*
-	 * Tracing init is not allowed.
-	 */
-	if (pid == 1)
-		return ERR_PTR(-EPERM);
+	if (request == PTRACE_TRACEME)
+		return ptrace_traceme();
 
+	ret = -ESRCH;
 	read_lock(&tasklist_lock);
 	child = find_task_by_pid(pid);
 	if (child)
 		get_task_struct(child);
 	read_unlock(&tasklist_lock);
+#ifdef PTRACE_DEBUG
+	printk("ptrace pid %ld => %p\n", pid, child);
+#endif
 	if (!child)
-		return ERR_PTR(-ESRCH);
-	return child;
+		goto out;
+
+	ret = -EPERM;
+	if (pid == 1)		/* you may not mess with init */
+		goto out_tsk;
+
+	if (request == PTRACE_ATTACH) {
+		ret = ptrace_attach(child);
+		goto out_tsk;
+	}
+
+	engine = utrace_attach(child, UTRACE_ATTACH_MATCH_OPS,
+			       &ptrace_utrace_ops, 0);
+	ret = -ESRCH;
+	if (IS_ERR(engine) || engine == NULL)
+		goto out_tsk;
+	rcu_read_lock();
+	state = rcu_dereference((struct ptrace_state *) engine->data);
+	if (state == NULL || state->u.live.parent != current) {
+		rcu_read_unlock();
+		goto out_tsk;
+	}
+	rcu_read_unlock();
+
+	/*
+	 * Traditional ptrace behavior demands that the target already be
+	 * quiescent, but not dead.
+	 */
+	if (request != PTRACE_KILL && !state->u.live.stopped) {
+#ifdef PTRACE_DEBUG
+		printk("%d not stopped (%lx)\n", child->pid, child->state);
+#endif
+		if (child->state != TASK_STOPPED)
+			goto out_tsk;
+		utrace_set_flags(child, engine,
+				 engine->flags | UTRACE_ACTION_QUIESCE);
+	}
+	if (child->exit_state)
+		goto out_tsk;
+
+	*childp = child;
+	*enginep = engine;
+	*statep = state;
+	return -EIO;
+
+out_tsk:
+	put_task_struct(child);
+out:
+	return ret;
+}
+
+static int
+ptrace_common(long request, struct task_struct *child,
+	      struct utrace_attached_engine *engine,
+	      struct ptrace_state *state,
+	      unsigned long addr, long data)
+{
+	unsigned long flags;
+	int ret = -EIO;
+
+	switch (request) {
+	case PTRACE_DETACH:
+		/*
+		 * Detach a process that was attached.
+		 */
+		ret = ptrace_induce_signal(child, engine, data);
+		if (!ret)
+			ret = ptrace_detach(child, engine);
+		break;
+
+		/*
+		 * These are the operations that resume the child running.
+		 */
+	case PTRACE_KILL:
+		data = SIGKILL;
+	case PTRACE_CONT:
+	case PTRACE_SYSCALL:
+	case PTRACE_SINGLESTEP:
+#ifdef PTRACE_SINGLEBLOCK
+	case PTRACE_SINGLEBLOCK:
+#endif
+#ifdef PTRACE_SYSEMU
+	case PTRACE_SYSEMU:
+	case PTRACE_SYSEMU_SINGLESTEP:
+#endif
+		ret = ptrace_induce_signal(child, engine, data);
+		if (ret)
+			break;
+
+		/*
+		 * Reset the action flags without QUIESCE, so it resumes.
+		 */
+		flags = 0;
+#ifdef PTRACE_SYSEMU
+		state->u.live.sysemu = (request == PTRACE_SYSEMU_SINGLESTEP
+					|| request == PTRACE_SYSEMU);
+#endif
+		if (request == PTRACE_SINGLESTEP
+#ifdef PTRACE_SYSEMU
+		    || request == PTRACE_SYSEMU_SINGLESTEP
+#endif
+			)
+			flags |= UTRACE_ACTION_SINGLESTEP;
+#ifdef PTRACE_SINGLEBLOCK
+		else if (request == PTRACE_SINGLEBLOCK)
+			flags |= UTRACE_ACTION_BLOCKSTEP;
+#endif
+		if (request == PTRACE_SYSCALL)
+			flags |= UTRACE_EVENT_SYSCALL;
+#ifdef PTRACE_SYSEMU
+		else if (request == PTRACE_SYSEMU
+			 || request == PTRACE_SYSEMU_SINGLESTEP)
+			flags |= UTRACE_EVENT(SYSCALL_ENTRY);
+#endif
+		ptrace_update(child, engine, flags);
+		ret = 0;
+		break;
+
+#ifdef PTRACE_OLDSETOPTIONS
+	case PTRACE_OLDSETOPTIONS:
+#endif
+	case PTRACE_SETOPTIONS:
+		ret = -EINVAL;
+		if (data & ~PTRACE_O_MASK)
+			break;
+		state->u.live.options = data;
+		ptrace_update(child, engine, UTRACE_ACTION_QUIESCE);
+		ret = 0;
+		break;
+
+#ifdef PTRACE_GETREGS
+	case PTRACE_GETREGS:
+		ret = ptrace_regset(child, engine, utrace_native_view(current),
+				    0, 0, 0, addr, (void __user *)data);
+		break;
+	case PTRACE_SETREGS:
+		ret = ptrace_regset(child, engine, utrace_native_view(current),
+				    0, 1, 0, addr, (void __user *)data);
+		break;
+#endif
+#ifdef PTRACE_GETFPREGS
+	case PTRACE_GETFPREGS:
+		ret = ptrace_regset(child, engine, utrace_native_view(current),
+				    1, 0, 0, addr, (void __user *)data);
+		break;
+	case PTRACE_SETFPREGS:
+		ret = ptrace_regset(child, engine, utrace_native_view(current),
+				    1, 1, 0, addr, (void __user *)data);
+		break;
+#endif
+
+	case PTRACE_PEEKUSR:
+		ret = ptrace_uarea(child, engine, 0, addr, data);
+		break;
+	case PTRACE_POKEUSR:
+		ret = ptrace_uarea(child, engine, 1, addr, data);
+		break;
+	}
+
+	return ret;
 }
 
+
 asmlinkage long sys_ptrace(long request, long pid, long addr, long data)
 {
-	return -ENOSYS;
+	struct task_struct *child;
+	struct utrace_attached_engine *engine;
+	struct ptrace_state *state;
+	int ret;
+	const struct utrace_regset_view *view;
+	int regset, nregs, rw;
+	void __user *uaddr;
+
+#ifdef PTRACE_DEBUG
+	printk("%d sys_ptrace(%ld, %ld, %lx, %lx)\n",
+	       current->pid, request, pid, addr, data);
+#endif
+
+	ret = ptrace_start(pid, request, &child, &engine, &state);
+	if (ret != -EIO)
+		goto out;
+
+	view = utrace_native_view(current);
+	regset = rw = -1;
+	nregs = 0;
+	uaddr = (void __user *) data;
+	ret = arch_ptrace(request, child, addr, data,
+			  &view, &regset, &rw, &uaddr, &nregs);
+	if (ret != -ENOSYS)
+		goto out_tsk;
+	if (regset >= 0) {
+		ret = ptrace_regset(child, engine, view, regset,
+				    rw, nregs, addr, uaddr);
+		goto out_tsk;
+	}
+
+	switch (request) {
+	default:
+		ret = ptrace_common(request, child, engine, state, addr, data);
+		break;
+
+	case PTRACE_PEEKTEXT: /* read word at location addr. */
+	case PTRACE_PEEKDATA: {
+		unsigned long tmp;
+		int copied;
+
+		copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+		ret = -EIO;
+		if (copied != sizeof(tmp))
+			break;
+		ret = put_user(tmp, (unsigned long __user *) data);
+		break;
+	}
+
+	case PTRACE_POKETEXT: /* write the word at location addr. */
+	case PTRACE_POKEDATA:
+		ret = 0;
+		if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
+			break;
+		ret = -EIO;
+		break;
+
+	case PTRACE_GETEVENTMSG:
+		ret = put_user(state->u.live.have_eventmsg
+			       ? state->u.live.u.eventmsg : 0L,
+			       (unsigned long __user *) data);
+		break;
+	case PTRACE_GETSIGINFO:
+		ret = -EINVAL;
+		if (!state->u.live.have_eventmsg && state->u.live.u.siginfo)
+			ret = copy_siginfo_to_user((siginfo_t __user *) data,
+						   state->u.live.u.siginfo);
+		break;
+	case PTRACE_SETSIGINFO:
+		ret = -EINVAL;
+		if (!state->u.live.have_eventmsg && state->u.live.u.siginfo
+		    && copy_from_user(state->u.live.u.siginfo,
+				      (siginfo_t __user *) data,
+				      sizeof(siginfo_t)))
+			ret = -EFAULT;
+		break;
+	}
+
+out_tsk:
+	put_task_struct(child);
+out:
+#ifdef PTRACE_DEBUG
+	printk("%d ptrace -> %x\n", current->pid, ret);
+#endif
+	return ret;
+}
+
+
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+
+asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
+				  compat_ulong_t addr, compat_long_t cdata)
+{
+	const unsigned long data = (unsigned long) (compat_ulong_t) cdata;
+	struct task_struct *child;
+	struct utrace_attached_engine *engine;
+	struct ptrace_state *state;
+	int ret;
+	const struct utrace_regset_view *view;
+	int regset, nregs, rw;
+	void __user *uaddr;
+
+#ifdef PTRACE_DEBUG
+	printk("%d compat_sys_ptrace(%d, %d, %x, %x)\n",
+	       current->pid, request, pid, addr, cdata);
+#endif
+	ret = ptrace_start(pid, request, &child, &engine, &state);
+	if (ret != -EIO)
+		goto out;
+
+	view = utrace_native_view(current);
+	regset = rw = -1;
+	nregs = 0;
+	uaddr = (void __user *) (unsigned long) cdata;
+	ret = arch_compat_ptrace(request, child, addr, cdata,
+				 &view, &regset, &rw, &uaddr, &nregs);
+	if (ret != -ENOSYS)
+		goto out_tsk;
+	if (regset >= 0) {
+		ret = ptrace_regset(child, engine, view, regset,
+				    rw, nregs, addr, uaddr);
+		goto out_tsk;
+	}
+
+	switch (request) {
+	default:
+		ret = ptrace_common(request, child, engine, state, addr, data);
+		break;
+
+	case PTRACE_PEEKTEXT: /* read word at location addr. */
+	case PTRACE_PEEKDATA: {
+		compat_ulong_t tmp;
+		int copied;
+
+		copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+		ret = -EIO;
+		if (copied != sizeof(tmp))
+			break;
+		ret = put_user(tmp, (compat_ulong_t __user *) data);
+		break;
+	}
+
+	case PTRACE_POKETEXT: /* write the word at location addr. */
+	case PTRACE_POKEDATA:
+		ret = 0;
+		if (access_process_vm(child, addr, &cdata, sizeof(cdata), 1) == sizeof(cdata))
+			break;
+		ret = -EIO;
+		break;
+
+	case PTRACE_GETEVENTMSG:
+		ret = put_user(state->u.live.have_eventmsg
+			       ? state->u.live.u.eventmsg : 0L,
+			       (compat_long_t __user *) data);
+		break;
+	case PTRACE_GETSIGINFO:
+		ret = -EINVAL;
+		if (!state->u.live.have_eventmsg && state->u.live.u.siginfo)
+			ret = copy_siginfo_to_user32(
+				(struct compat_siginfo __user *) data,
+				state->u.live.u.siginfo);
+		break;
+	case PTRACE_SETSIGINFO:
+		ret = -EINVAL;
+		if (!state->u.live.have_eventmsg && state->u.live.u.siginfo
+		    && copy_siginfo_from_user32(
+			    state->u.live.u.siginfo,
+			    (struct compat_siginfo __user *) data))
+			ret = -EFAULT;
+		break;
+	}
+
+out_tsk:
+	put_task_struct(child);
+out:
+#ifdef PTRACE_DEBUG
+	printk("%d ptrace -> %x\n", current->pid, ret);
+#endif
+	return ret;
+}
+#endif
+
+
+int
+ptrace_do_wait(task_t *tsk,
+	       pid_t pid, int options, struct siginfo __user *infop,
+	       int __user *stat_addr, struct rusage __user *rusagep)
+{
+	struct ptrace_state *state;
+	task_t *p;
+	int err = -ECHILD;
+	int why, status;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(state, &tsk->ptracees, entry) {
+		p = state->u.live.task;
+
+		if (pid > 0) {
+			if (p->pid != pid)
+				continue;
+		} else if (!pid) {
+			if (process_group(p) != process_group(current))
+				continue;
+		} else if (pid != -1) {
+			if (process_group(p) != -pid)
+				continue;
+		}
+		if (((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0))
+		    && !(options & __WALL))
+			continue;
+		if (security_task_wait(p))
+			continue;
+
+		err = 0;
+		if (state->u.live.reported)
+			continue;
+
+		if (state->u.live.stopped)
+			goto found;
+		if ((p->state & (TASK_TRACED | TASK_STOPPED))
+		    && (p->signal->flags & SIGNAL_STOP_STOPPED))
+			goto found;
+		if (p->exit_state == EXIT_ZOMBIE) {
+			if (!likely(options & WEXITED))
+				continue;
+			goto found;
+		}
+	}
+	rcu_read_unlock();
+	return err;
+
+found:
+	rcu_read_unlock();
+
+	if (p->exit_state) {
+		if ((p->exit_code & 0x7f) == 0) {
+			why = CLD_EXITED;
+			status = p->exit_code >> 8;
+		} else {
+			why = (p->exit_code & 0x80) ? CLD_DUMPED : CLD_KILLED;
+			status = p->exit_code & 0xff;
+		}
+	}
+	else {
+		why = CLD_TRAPPED;
+		status = (p->exit_code << 8) | 0x7f;
+	}
+
+	if (rusagep)
+		err = getrusage(p, RUSAGE_BOTH, rusagep);
+	if (infop) {
+		if (!err)
+			err = put_user(SIGCHLD, &infop->si_signo);
+		if (!err)
+			err = put_user(0, &infop->si_errno);
+		if (!err)
+			err = put_user((short)why, &infop->si_code);
+		if (!err)
+			err = put_user(p->pid, &infop->si_pid);
+		if (!err)
+			err = put_user(p->uid, &infop->si_uid);
+		if (!err)
+			err = put_user(status, &infop->si_status);
+	}
+	if (!err && stat_addr)
+		err = put_user(status, stat_addr);
+
+	if (!err) {
+		err = p->pid;
+
+		utrace_lock(p->utrace);
+		if (unlikely(state->u.live.reported))
+			err = -ERESTARTNOINTR;
+		state->u.live.reported = 1;
+		utrace_unlock(p->utrace);
+	}
+
+	if (why != CLD_TRAPPED)
+		ptrace_detach(p, state->u.live.engine);
+
+	return err;
 }
+
+static void
+do_notify(struct task_struct *tsk, struct task_struct *parent, int exit)
+{
+	struct siginfo info;
+	unsigned long flags;
+	struct sighand_struct *sighand;
+
+	info.si_signo = SIGCHLD;
+	info.si_errno = 0;
+	info.si_pid = tsk->pid;
+	info.si_uid = tsk->uid;
+
+	/* FIXME: find out whether or not this is supposed to be c*time. */
+	info.si_utime = cputime_to_jiffies(tsk->utime);
+	info.si_stime = cputime_to_jiffies(tsk->stime);
+
+ 	info.si_code = CLD_TRAPPED;
+	info.si_status = tsk->exit_code & 0x7f;
+	if (exit) {
+		if (tsk->exit_code & 0x80)
+			info.si_code = CLD_DUMPED;
+		else if (tsk->exit_code & 0x7f)
+			info.si_code = CLD_KILLED;
+		else {
+			info.si_code = CLD_EXITED;
+			info.si_status = tsk->exit_code >> 8;
+		}
+	}
+
+	sighand = parent->sighand;
+	spin_lock_irqsave(&sighand->siglock, flags);
+	if (sighand->action[SIGCHLD-1].sa.sa_handler != SIG_IGN &&
+	    !(sighand->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP))
+		__group_send_sig_info(SIGCHLD, &info, parent);
+	/*
+	 * Even if SIGCHLD is not generated, we must wake up wait4 calls.
+	 */
+	wake_up_interruptible_sync(&parent->signal->wait_chldexit);
+	spin_unlock_irqrestore(&sighand->siglock, flags);
+}
+
+static u32
+ptrace_report(struct utrace_attached_engine *engine, struct task_struct *tsk,
+	      int code)
+{
+	struct ptrace_state *state = (struct ptrace_state *) engine->data;
+
+#ifdef PTRACE_DEBUG
+	printk("%d ptrace_report %d engine %p state %p code %x parent %d (%p)\n",
+	       current->pid, tsk->pid, engine, state, code,
+	       state->u.live.parent->pid, state->u.live.parent);
+	if (!state->u.live.have_eventmsg && state->u.live.u.siginfo) {
+		const siginfo_t *si = state->u.live.u.siginfo;
+		printk("  si %d code %x errno %d addr %p\n",
+		       si->si_signo, si->si_code, si->si_errno,
+		       si->si_addr);
+	}
+#endif
+
+	state->u.live.stopped = 1;
+	state->u.live.reported = 0;
+	tsk->exit_code = code;
+	do_notify(tsk, state->u.live.parent, 0);
+
+#ifdef PTRACE_DEBUG
+	printk("%d ptrace_report quiescing exit_code %x\n",
+	       current->pid, current->exit_code);
+#endif
+
+	return (UTRACE_ACTION_NEWSTATE
+		| UTRACE_ACTION_QUIESCE | UTRACE_ACTION_NOREAP);
+}
+
+static inline u32
+ptrace_event(struct utrace_attached_engine *engine, struct task_struct *tsk,
+	     int event)
+{
+	struct ptrace_state *state = (struct ptrace_state *) engine->data;
+	state->u.live.syscall = 0;
+	return ptrace_report(engine, tsk, (event << 8) | SIGTRAP);
+}
+
+
+static u32
+ptrace_report_death(struct utrace_attached_engine *engine,
+		    struct task_struct *tsk)
+{
+	struct ptrace_state *state = (struct ptrace_state *) engine->data;
+
+	if (tsk->parent == state->u.live.parent) {
+		ptrace_state_unlink(state);
+		ptrace_done(state);
+		return UTRACE_ACTION_DETACH;
+	}
+
+	state->u.live.reported = 0;
+	do_notify(tsk, state->u.live.parent, 1);
+	return UTRACE_ACTION_RESUME;
+}
+
+
+static u32
+ptrace_report_clone(struct utrace_attached_engine *engine,
+		    struct task_struct *parent,
+		    unsigned long clone_flags, struct task_struct *child)
+{
+	struct ptrace_state *state = (struct ptrace_state *) engine->data;
+	struct utrace_attached_engine *child_engine;
+	int event = PTRACE_EVENT_FORK;
+	int option = PTRACE_O_TRACEFORK;
+
+#ifdef PTRACE_DEBUG
+	printk("%d (%p) engine %p ptrace_report_clone child %d (%p) fl %lx\n",
+	       parent->pid, parent, engine, child->pid, child, clone_flags);
+#endif
+
+	if (clone_flags & CLONE_UNTRACED)
+		goto out;
+
+	if (clone_flags & CLONE_VFORK) {
+		event = PTRACE_EVENT_VFORK;
+		option = PTRACE_O_TRACEVFORK;
+	}
+	else if ((clone_flags & CSIGNAL) != SIGCHLD) {
+		event = PTRACE_EVENT_CLONE;
+		option = PTRACE_O_TRACECLONE;
+	}
+
+	if (!(clone_flags & CLONE_PTRACE) && !(state->u.live.options & option))
+		goto out;
+
+	child_engine = utrace_attach(child, (UTRACE_ATTACH_CREATE
+					     | UTRACE_ATTACH_EXCLUSIVE
+					     | UTRACE_ATTACH_MATCH_OPS),
+				     &ptrace_utrace_ops, 0UL);
+	if (unlikely(IS_ERR(child_engine))) {
+		printk("XXX ptrace lost child %d (attach): %ld\n",
+		       child->pid, PTR_ERR(child_engine));
+	}
+	else {
+		int ret = ptrace_setup(child, child_engine,
+				       state->u.live.parent,
+				       state->u.live.options);
+		if (ret != 0) {
+			printk("XXX ptrace lost child %d (setup): %d\n",
+			       child->pid, ret);
+			utrace_detach(child, child_engine);
+		}
+		else {
+			sigaddset(&child->pending.signal, SIGSTOP);
+			set_tsk_thread_flag(child, TIF_SIGPENDING);
+			ptrace_update(child, child_engine, 0);
+		}
+	}
+
+	if (state->u.live.options & option) {
+		state->u.live.have_eventmsg = 1;
+		state->u.live.u.eventmsg = child->pid;
+		return ptrace_event(engine, parent, event);
+	}
+
+out:
+	return UTRACE_ACTION_RESUME;
+}
+
+
+static u32
+ptrace_report_vfork_done(struct utrace_attached_engine *engine,
+			 struct task_struct *parent, struct task_struct *child)
+{
+	return ptrace_event(engine, parent, PTRACE_EVENT_VFORK_DONE);
+}
+
+
+static u32
+ptrace_report_signal(struct utrace_attached_engine *engine,
+		     struct task_struct *tsk, struct pt_regs *regs,
+		     u32 action, siginfo_t *info,
+		     const struct k_sigaction *orig_ka,
+		     struct k_sigaction *return_ka)
+{
+	struct ptrace_state *state = (struct ptrace_state *) engine->data;
+	int signo = info == NULL ? SIGTRAP : info->si_signo;
+	state->u.live.syscall = 0;
+	state->u.live.have_eventmsg = 0;
+	state->u.live.u.siginfo = info;
+	return ptrace_report(engine, tsk, signo) | UTRACE_SIGNAL_IGN;
+}
+
+static u32
+ptrace_report_jctl(struct utrace_attached_engine *engine,
+		   struct task_struct *tsk, int type)
+{
+	return UTRACE_ACTION_RESUME; /* XXX */
+}
+
+static u32
+ptrace_report_exec(struct utrace_attached_engine *engine,
+		   struct task_struct *tsk,
+		   const struct linux_binprm *bprm,
+		   struct pt_regs *regs)
+{
+	struct ptrace_state *state = (struct ptrace_state *) engine->data;
+	if (state->u.live.options & PTRACE_O_TRACEEXEC)
+		return ptrace_event(engine, tsk, PTRACE_EVENT_EXEC);
+	state->u.live.syscall = 0;
+	return ptrace_report(engine, tsk, SIGTRAP);
+}
+
+static u32
+ptrace_report_syscall(struct utrace_attached_engine *engine,
+		      struct task_struct *tsk, struct pt_regs *regs)
+{
+	struct ptrace_state *state = (struct ptrace_state *) engine->data;
+#ifdef PTRACE_SYSEMU
+	if (state->u.live.sysemu)
+		tracehook_abort_syscall(regs);
+#endif
+	state->u.live.syscall = 1;
+	return ptrace_report(engine, tsk,
+			     ((state->u.live.options & PTRACE_O_TRACESYSGOOD)
+			      ? 0x80 : 0) | SIGTRAP);
+}
+
+static u32
+ptrace_report_exit(struct utrace_attached_engine *engine,
+		   struct task_struct *tsk, long orig_code, long *code)
+{
+	struct ptrace_state *state = (struct ptrace_state *) engine->data;
+	state->u.live.have_eventmsg = 1;
+	state->u.live.u.eventmsg = *code;
+	return ptrace_event(engine, tsk, PTRACE_EVENT_EXIT);
+}
+
+static pid_t
+ptrace_tracer_pid(struct utrace_attached_engine *engine,
+		  struct task_struct *target)
+{
+	struct ptrace_state *state = (struct ptrace_state *) engine->data;
+	/* XXX detach race */
+	return state->u.live.parent->pid;
+}
+
+static int
+ptrace_allow_access_process_vm(struct utrace_attached_engine *engine,
+			       struct task_struct *target,
+			       struct task_struct *caller)
+{
+	struct ptrace_state *state = (struct ptrace_state *) engine->data;
+	/* XXX detach race */
+	return (((engine->flags & UTRACE_ACTION_QUIESCE)
+		 || (target->state == TASK_STOPPED))
+		&& state->u.live.parent == caller
+		&& security_ptrace(caller, target) == 0);
+}
+
+
+static const struct utrace_engine_ops ptrace_utrace_ops =
+{
+	.report_syscall_entry = ptrace_report_syscall,
+	.report_syscall_exit = ptrace_report_syscall,
+	.report_exec = ptrace_report_exec,
+	.report_jctl = ptrace_report_jctl,
+	.report_signal = ptrace_report_signal,
+	.report_vfork_done = ptrace_report_vfork_done,
+	.report_clone = ptrace_report_clone,
+	.report_exit = ptrace_report_exit,
+	.report_death = ptrace_report_death,
+	.tracer_pid = ptrace_tracer_pid,
+	.allow_access_process_vm = ptrace_allow_access_process_vm,
+};
+
+#endif
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux