File linux-2.6-utrace.patch of Package kernel
Documentation/utrace.txt | 455 ++++++++
arch/alpha/kernel/asm-offsets.c | 2
arch/alpha/kernel/entry.S | 4
arch/arm/kernel/ptrace.c | 36 -
arch/arm26/kernel/ptrace.c | 32 -
arch/frv/kernel/ptrace.c | 15
arch/i386/kernel/entry.S | 7
arch/i386/kernel/i387.c | 143 +--
arch/i386/kernel/process.c | 3
arch/i386/kernel/ptrace.c | 863 +++++++++-------
arch/i386/kernel/signal.c | 39 -
arch/i386/kernel/vm86.c | 7
arch/ia64/ia32/ia32_entry.S | 2
arch/ia64/ia32/sys_ia32.c | 23
arch/ia64/kernel/asm-offsets.c | 2
arch/ia64/kernel/fsys.S | 16
arch/ia64/kernel/mca.c | 2
arch/ia64/kernel/ptrace.c | 1670 +++++++++++++++----------------
arch/ia64/kernel/signal.c | 4
arch/mips/kernel/ptrace.c | 21
arch/mips/kernel/sysirix.c | 2
arch/powerpc/kernel/Makefile | 4
arch/powerpc/kernel/asm-offsets.c | 2
arch/powerpc/kernel/process.c | 5
arch/powerpc/kernel/ptrace-common.h | 161 ---
arch/powerpc/kernel/ptrace.c | 959 +++++++++++-------
arch/powerpc/kernel/ptrace32.c | 436 --------
arch/powerpc/kernel/signal_32.c | 56 +
arch/powerpc/kernel/signal_64.c | 4
arch/powerpc/kernel/sys_ppc32.c | 5
arch/powerpc/lib/sstep.c | 3
arch/powerpc/platforms/cell/spufs/run.c | 2
arch/ppc/kernel/asm-offsets.c | 2
arch/s390/kernel/Makefile | 2
arch/s390/kernel/compat_linux.c | 3
arch/s390/kernel/compat_signal.c | 5
arch/s390/kernel/process.c | 3
arch/s390/kernel/ptrace.c | 651 +++++++++++-
arch/s390/kernel/signal.c | 4
arch/s390/kernel/traps.c | 6
arch/sparc64/Makefile | 0
arch/sparc64/kernel/Makefile | 2
arch/sparc64/kernel/binfmt_aout32.c | 2
arch/sparc64/kernel/entry.S | 6
arch/sparc64/kernel/process.c | 3
arch/sparc64/kernel/ptrace.c | 1221 ++++++++++++-----------
arch/sparc64/kernel/signal.c | 2
arch/sparc64/kernel/signal32.c | 2
arch/sparc64/kernel/sys_sparc32.c | 3
arch/sparc64/kernel/systbls.S | 4
arch/x86_64/ia32/fpu32.c | 92 +-
arch/x86_64/ia32/ia32_aout.c | 6
arch/x86_64/ia32/ia32_signal.c | 8
arch/x86_64/ia32/ia32entry.S | 2
arch/x86_64/ia32/ptrace32.c | 715 +++++++++----
arch/x86_64/ia32/sys_ia32.c | 5
arch/x86_64/kernel/process.c | 5
arch/x86_64/kernel/ptrace.c | 648 +++++++-----
arch/x86_64/kernel/signal.c | 30 -
arch/x86_64/kernel/traps.c | 8
arch/x86_64/mm/fault.c | 4
drivers/connector/cn_proc.c | 4
fs/binfmt_aout.c | 6
fs/binfmt_elf.c | 6
fs/binfmt_elf_fdpic.c | 7
fs/binfmt_flat.c | 3
fs/binfmt_som.c | 2
fs/exec.c | 11
fs/proc/array.c | 14
fs/proc/base.c | 17
include/asm-i386/i387.h | 13
include/asm-i386/signal.h | 4
include/asm-i386/thread_info.h | 7
include/asm-i386/tracehook.h | 49 +
include/asm-ia64/elf.h | 24
include/asm-ia64/tracehook.h | 81 ++
include/asm-powerpc/tracehook.h | 80 +
include/asm-s390/tracehook.h | 53 +
include/asm-sparc64/tracehook.h | 44 +
include/asm-x86_64/fpu32.h | 3
include/asm-x86_64/thread_info.h | 2
include/asm-x86_64/tracehook.h | 54 +
include/linux/init_task.h | 3
include/linux/ptrace.h | 224 +++-
include/linux/sched.h | 25
include/linux/tracehook.h | 707 +++++++++++++
include/linux/utrace.h | 484 +++++++++
init/Kconfig | 29 +
kernel/Makefile | 1
kernel/exit.c | 254 +----
kernel/fork.c | 62 -
kernel/ptrace.c | 1632 ++++++++++++++++++++++++------
kernel/signal.c | 211 +---
kernel/sys.c | 2
kernel/timer.c | 4
kernel/utrace.c | 1590 ++++++++++++++++++++++++++++++
security/selinux/hooks.c | 54 +
security/selinux/include/objsec.h | 1
98 files changed, 9630 insertions(+), 4566 deletions(-)
create mode 100644 Documentation/utrace.txt
delete arch/powerpc/kernel/ptrace-common.h
delete arch/powerpc/kernel/ptrace32.c
create mode 100644 include/asm-i386/tracehook.h
create mode 100644 include/asm-ia64/tracehook.h
create mode 100644 include/asm-powerpc/tracehook.h
create mode 100644 include/asm-s390/tracehook.h
create mode 100644 include/asm-sparc64/tracehook.h
create mode 100644 include/asm-x86_64/tracehook.h
create mode 100644 include/linux/tracehook.h
create mode 100644 include/linux/utrace.h
create mode 100644 kernel/utrace.c
--- linux-2.6/include/asm-powerpc/tracehook.h.utrace-ptrace-compat
+++ linux-2.6/include/asm-powerpc/tracehook.h
@@ -0,0 +1,80 @@
+/*
+ * Tracing hooks, PowerPC CPU support
+ */
+
+#ifndef _ASM_TRACEHOOK_H
+#define _ASM_TRACEHOOK_H 1
+
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+
+/*
+ * See linux/tracehook.h for the descriptions of what these need to do.
+ */
+
+#define ARCH_HAS_SINGLE_STEP (1)
+
+static inline void tracehook_enable_single_step(struct task_struct *task)
+{
+ struct pt_regs *regs = task->thread.regs;
+ if (regs != NULL) {
+#if defined(CONFIG_PPC32) && (defined(CONFIG_40x) || defined(CONFIG_BOOKE))
+ task->thread.dbcr0 = DBCR0_IDM | DBCR0_IC;
+ regs->msr |= MSR_DE;
+#else
+ regs->msr |= MSR_SE;
+#endif
+ }
+ set_tsk_thread_flag(task, TIF_SINGLESTEP);
+}
+
+static inline void tracehook_disable_single_step(struct task_struct *task)
+{
+ struct pt_regs *regs = task->thread.regs;
+ if (regs != NULL) {
+#if defined(CONFIG_PPC32) && (defined(CONFIG_40x) || defined(CONFIG_BOOKE))
+ task->thread.dbcr0 = 0;
+ regs->msr &= ~MSR_DE;
+#else
+ regs->msr &= ~MSR_SE;
+#endif
+ }
+ clear_tsk_thread_flag(task, TIF_SINGLESTEP);
+}
+
+static inline int tracehook_single_step_enabled(struct task_struct *tsk)
+{
+ return test_tsk_thread_flag(tsk, TIF_SINGLESTEP);
+}
+
+static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
+{
+ set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
+{
+ clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_abort_syscall(struct pt_regs *regs)
+{
+ regs->orig_gpr3 = -1L;
+}
+
+
+extern const struct utrace_regset_view utrace_ppc_native_view;
+static inline const struct utrace_regset_view *
+utrace_native_view(struct task_struct *tsk)
+{
+#ifdef CONFIG_PPC64
+ extern const struct utrace_regset_view utrace_ppc32_view;
+
+ if (test_tsk_thread_flag(tsk, TIF_32BIT))
+ return &utrace_ppc32_view;
+#endif
+ return &utrace_ppc_native_view;
+}
+
+
+#endif
--- linux-2.6/include/asm-ia64/elf.h.utrace-ptrace-compat
+++ linux-2.6/include/asm-ia64/elf.h
@@ -154,6 +154,30 @@ extern void ia64_init_addr_space (void);
#define ELF_NGREG 128 /* we really need just 72 but let's leave some headroom... */
#define ELF_NFPREG 128 /* f0 and f1 could be omitted, but so what... */
+/* elf_gregset_t register offsets */
+#define ELF_GR_0_OFFSET 0
+#define ELF_NAT_OFFSET (32 * sizeof(elf_greg_t))
+#define ELF_PR_OFFSET (33 * sizeof(elf_greg_t))
+#define ELF_BR_0_OFFSET (34 * sizeof(elf_greg_t))
+#define ELF_CR_IIP_OFFSET (42 * sizeof(elf_greg_t))
+#define ELF_CFM_OFFSET (43 * sizeof(elf_greg_t))
+#define ELF_CR_IPSR_OFFSET (44 * sizeof(elf_greg_t))
+#define ELF_GR_OFFSET(i) (ELF_GR_0_OFFSET + i * sizeof(elf_greg_t))
+#define ELF_BR_OFFSET(i) (ELF_BR_0_OFFSET + i * sizeof(elf_greg_t))
+#define ELF_AR_RSC_OFFSET (45 * sizeof(elf_greg_t))
+#define ELF_AR_BSP_OFFSET (46 * sizeof(elf_greg_t))
+#define ELF_AR_BSPSTORE_OFFSET (47 * sizeof(elf_greg_t))
+#define ELF_AR_RNAT_OFFSET (48 * sizeof(elf_greg_t))
+#define ELF_AR_CCV_OFFSET (49 * sizeof(elf_greg_t))
+#define ELF_AR_UNAT_OFFSET (50 * sizeof(elf_greg_t))
+#define ELF_AR_FPSR_OFFSET (51 * sizeof(elf_greg_t))
+#define ELF_AR_PFS_OFFSET (52 * sizeof(elf_greg_t))
+#define ELF_AR_LC_OFFSET (53 * sizeof(elf_greg_t))
+#define ELF_AR_EC_OFFSET (54 * sizeof(elf_greg_t))
+#define ELF_AR_CSD_OFFSET (55 * sizeof(elf_greg_t))
+#define ELF_AR_SSD_OFFSET (56 * sizeof(elf_greg_t))
+#define ELF_AR_END_OFFSET (57 * sizeof(elf_greg_t))
+
typedef unsigned long elf_fpxregset_t;
typedef unsigned long elf_greg_t;
--- linux-2.6/include/asm-ia64/tracehook.h.utrace-ptrace-compat
+++ linux-2.6/include/asm-ia64/tracehook.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C)2006 Intel Co
+ * Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ * and Bibo Mao <bibo.mao@intel.com> adapted from i386.
+ *
+ * Tracing hooks, ia64 CPU support
+ */
+
+#ifndef _ASM_TRACEHOOK_H
+#define _ASM_TRACEHOOK_H 1
+
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+
+/*
+ * See linux/tracehook.h for the descriptions of what these need to do.
+ */
+
+#define ARCH_HAS_SINGLE_STEP (1)
+#define ARCH_HAS_BLOCK_STEP (1)
+
+static inline void tracehook_enable_single_step(struct task_struct *tsk)
+{
+ struct pt_regs *pt = task_pt_regs(tsk);
+ ia64_psr(pt)->ss = 1;
+}
+
+static inline void tracehook_disable_single_step(struct task_struct *tsk)
+{
+ struct pt_regs *pt = task_pt_regs(tsk);
+ ia64_psr(pt)->ss = 0;
+}
+
+static inline void tracehook_enable_block_step(struct task_struct *tsk)
+{
+ struct pt_regs *pt = task_pt_regs(tsk);
+ ia64_psr(pt)->tb = 1;
+}
+
+static inline void tracehook_disable_block_step(struct task_struct *tsk)
+{
+ struct pt_regs *pt = task_pt_regs(tsk);
+ ia64_psr(pt)->tb = 0;
+}
+
+static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
+{
+ set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
+{
+ clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline int tracehook_single_step_enabled(struct task_struct *tsk)
+{
+ struct pt_regs *pt = task_pt_regs(tsk);
+ return ia64_psr(pt)->ss;
+}
+
+static inline void tracehook_abort_syscall(struct pt_regs *regs)
+{
+ regs->r15 = -1L;
+}
+
+extern const struct utrace_regset_view utrace_ia64_native;
+static inline const struct utrace_regset_view *
+utrace_native_view(struct task_struct *tsk)
+{
+#if 0 //CONFIG_IA32_SUPPORT
+ extern const struct utrace_regset_view utrace_ia32_view;
+
+ struct pt_regs *pt = task_pt_regs(tsk);
+ if (pt->cr_ipsr & IA64_PSR_IS)
+ return &utrace_ia32_view;
+#endif
+ return &utrace_ia64_native;
+}
+
+#endif
--- linux-2.6/include/asm-i386/i387.h.utrace-ptrace-compat
+++ linux-2.6/include/asm-i386/i387.h
@@ -126,17 +126,12 @@ extern int save_i387( struct _fpstate __
extern int restore_i387( struct _fpstate __user *buf );
/*
- * ptrace request handers...
+ * ptrace request handlers...
*/
-extern int get_fpregs( struct user_i387_struct __user *buf,
- struct task_struct *tsk );
-extern int set_fpregs( struct task_struct *tsk,
- struct user_i387_struct __user *buf );
+extern int get_fpregs(struct user_i387_struct *, struct task_struct *);
+extern int set_fpregs(struct task_struct *, const struct user_i387_struct *);
+extern void updated_fpxregs(struct task_struct *tsk);
-extern int get_fpxregs( struct user_fxsr_struct __user *buf,
- struct task_struct *tsk );
-extern int set_fpxregs( struct task_struct *tsk,
- struct user_fxsr_struct __user *buf );
/*
* FPU state for core dumps...
--- linux-2.6/include/asm-i386/signal.h.utrace-ptrace-compat
+++ linux-2.6/include/asm-i386/signal.h
@@ -221,10 +221,8 @@ struct pt_regs;
#define ptrace_signal_deliver(regs, cookie) \
do { \
- if (current->ptrace & PT_DTRACE) { \
- current->ptrace &= ~PT_DTRACE; \
+ if (test_and_clear_thread_flag(TIF_FORCED_TF)) \
(regs)->eflags &= ~TF_MASK; \
- } \
} while (0)
#endif /* __KERNEL__ */
--- linux-2.6/include/asm-i386/thread_info.h.utrace-ptrace-compat
+++ linux-2.6/include/asm-i386/thread_info.h
@@ -135,13 +135,13 @@ static inline struct thread_info *curren
#define TIF_NEED_RESCHED 3 /* rescheduling necessary */
#define TIF_SINGLESTEP 4 /* restore singlestep on return to user mode */
#define TIF_IRET 5 /* return with iret */
-#define TIF_SYSCALL_EMU 6 /* syscall emulation active */
#define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */
#define TIF_SECCOMP 8 /* secure computing */
#define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal() */
#define TIF_MEMDIE 16
#define TIF_DEBUG 17 /* uses debug registers */
#define TIF_IO_BITMAP 18 /* uses I/O bitmap */
+#define TIF_FORCED_TF 19 /* true if TF in eflags artificially */
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
@@ -149,17 +149,16 @@ static inline struct thread_info *curren
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
#define _TIF_SINGLESTEP (1<<TIF_SINGLESTEP)
#define _TIF_IRET (1<<TIF_IRET)
-#define _TIF_SYSCALL_EMU (1<<TIF_SYSCALL_EMU)
#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
#define _TIF_SECCOMP (1<<TIF_SECCOMP)
#define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK)
#define _TIF_DEBUG (1<<TIF_DEBUG)
#define _TIF_IO_BITMAP (1<<TIF_IO_BITMAP)
+#define _TIF_FORCED_TF (1<<TIF_FORCED_TF)
/* work to do on interrupt/exception return */
#define _TIF_WORK_MASK \
- (0x0000FFFF & ~(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
- _TIF_SECCOMP | _TIF_SYSCALL_EMU))
+ (0x0000FFFF & ~(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP))
/* work to do on any return to u-space */
#define _TIF_ALLWORK_MASK (0x0000FFFF & ~_TIF_SECCOMP)
--- linux-2.6/include/asm-i386/tracehook.h.utrace-ptrace-compat
+++ linux-2.6/include/asm-i386/tracehook.h
@@ -0,0 +1,49 @@
+/*
+ * Tracing hooks, i386 CPU support
+ */
+
+#ifndef _ASM_TRACEHOOK_H
+#define _ASM_TRACEHOOK_H 1
+
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+
+/*
+ * See linux/tracehook.h for the descriptions of what these need to do.
+ */
+
+#define ARCH_HAS_SINGLE_STEP (1)
+
+/* These two are defined in arch/i386/kernel/ptrace.c. */
+void tracehook_enable_single_step(struct task_struct *tsk);
+void tracehook_disable_single_step(struct task_struct *tsk);
+
+static inline int tracehook_single_step_enabled(struct task_struct *tsk)
+{
+ return test_tsk_thread_flag(tsk, TIF_SINGLESTEP);
+}
+
+static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
+{
+ set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
+{
+ clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_abort_syscall(struct pt_regs *regs)
+{
+ regs->orig_eax = -1;
+}
+
+extern const struct utrace_regset_view utrace_i386_native;
+static inline const struct utrace_regset_view *
+utrace_native_view(struct task_struct *tsk)
+{
+ return &utrace_i386_native;
+}
+
+
+#endif
--- linux-2.6/include/asm-s390/tracehook.h.utrace-ptrace-compat
+++ linux-2.6/include/asm-s390/tracehook.h
@@ -0,0 +1,53 @@
+/*
+ * Tracing hooks, s390/s390x support.
+ */
+
+#ifndef _ASM_TRACEHOOK_H
+#define _ASM_TRACEHOOK_H 1
+
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+
+/*
+ * See linux/tracehook.h for the descriptions of what these need to do.
+ */
+
+#define ARCH_HAS_SINGLE_STEP (1)
+
+/* These three are defined in arch/s390/kernel/ptrace.c. */
+void tracehook_enable_single_step(struct task_struct *tsk);
+void tracehook_disable_single_step(struct task_struct *tsk);
+int tracehook_single_step_enabled(struct task_struct *tsk);
+
+
+static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
+{
+ set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
+{
+ clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_abort_syscall(struct pt_regs *regs)
+{
+ regs->gprs[2] = -1L;
+}
+
+
+extern const struct utrace_regset_view utrace_s390_native_view;
+static inline const struct utrace_regset_view *
+utrace_native_view(struct task_struct *tsk)
+{
+#ifdef CONFIG_COMPAT
+ extern const struct utrace_regset_view utrace_s390_compat_view;
+
+ if (test_tsk_thread_flag(tsk, TIF_31BIT))
+ return &utrace_s390_compat_view;
+#endif
+ return &utrace_s390_native_view;
+}
+
+
+#endif
--- linux-2.6/include/asm-x86_64/fpu32.h.utrace-ptrace-compat
+++ linux-2.6/include/asm-x86_64/fpu32.h
@@ -7,4 +7,7 @@ int restore_i387_ia32(struct task_struct
int save_i387_ia32(struct task_struct *tsk, struct _fpstate_ia32 __user *buf,
struct pt_regs *regs, int fsave);
+int get_fpregs32(struct user_i387_ia32_struct *, struct task_struct *);
+int set_fpregs32(struct task_struct *, const struct user_i387_ia32_struct *);
+
#endif
--- linux-2.6/include/asm-x86_64/thread_info.h.utrace-ptrace-compat
+++ linux-2.6/include/asm-x86_64/thread_info.h
@@ -119,6 +119,7 @@ static inline struct thread_info *stack_
#define TIF_FORK 18 /* ret_from_fork */
#define TIF_ABI_PENDING 19
#define TIF_MEMDIE 20
+#define TIF_FORCED_TF 21 /* true if TF in eflags artificially */
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
@@ -131,6 +132,7 @@ static inline struct thread_info *stack_
#define _TIF_IA32 (1<<TIF_IA32)
#define _TIF_FORK (1<<TIF_FORK)
#define _TIF_ABI_PENDING (1<<TIF_ABI_PENDING)
+#define _TIF_FORCED_TF (1<<TIF_FORCED_TF)
/* work to do on interrupt/exception return */
#define _TIF_WORK_MASK \
--- linux-2.6/include/asm-x86_64/tracehook.h.utrace-ptrace-compat
+++ linux-2.6/include/asm-x86_64/tracehook.h
@@ -0,0 +1,54 @@
+/*
+ * Tracing hooks, x86-64 CPU support
+ */
+
+#ifndef _ASM_TRACEHOOK_H
+#define _ASM_TRACEHOOK_H 1
+
+#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.
+ */
+
+#define ARCH_HAS_SINGLE_STEP (1)
+
+/* These two are defined in arch/x86_64/kernel/ptrace.c. */
+void tracehook_enable_single_step(struct task_struct *tsk);
+void tracehook_disable_single_step(struct task_struct *tsk);
+
+static inline int tracehook_single_step_enabled(struct task_struct *tsk)
+{
+ return test_tsk_thread_flag(tsk, TIF_SINGLESTEP);
+}
+
+static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
+{
+ set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
+{
+ clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_abort_syscall(struct pt_regs *regs)
+{
+ regs->orig_rax = -1L;
+}
+
+extern const struct utrace_regset_view utrace_x86_64_native, utrace_ia32_view;
+static inline const struct utrace_regset_view *
+utrace_native_view(struct task_struct *tsk)
+{
+#ifdef CONFIG_IA32_EMULATION
+ if (test_tsk_thread_flag(tsk, TIF_IA32))
+ return &utrace_ia32_view;
+#endif
+ return &utrace_x86_64_native;
+}
+
+
+#endif
--- linux-2.6/include/asm-sparc64/tracehook.h.utrace-ptrace-compat
+++ linux-2.6/include/asm-sparc64/tracehook.h
@@ -0,0 +1,44 @@
+/*
+ * Tracing hooks, SPARC64 CPU support
+ */
+
+#ifndef _ASM_TRACEHOOK_H
+#define _ASM_TRACEHOOK_H 1
+
+
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+
+/*
+ * See linux/tracehook.h for the descriptions of what these need to do.
+ */
+
+
+static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
+{
+ set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
+{
+ clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_abort_syscall(struct pt_regs *regs)
+{
+ regs->u_regs[UREG_G1] = -1L;
+}
+
+extern const struct utrace_regset_view utrace_sparc64_native_view;
+static inline const struct utrace_regset_view *
+utrace_native_view(struct task_struct *tsk)
+{
+#ifdef CONFIG_COMPAT
+ extern const struct utrace_regset_view utrace_sparc32_view;
+ if (test_tsk_thread_flag(tsk, TIF_32BIT))
+ return &utrace_sparc32_view;
+#endif
+ return &utrace_sparc64_native_view;
+}
+
+#endif
--- linux-2.6/include/linux/sched.h.utrace-ptrace-compat
+++ linux-2.6/include/linux/sched.h
@@ -769,7 +769,6 @@ struct task_struct {
struct thread_info *thread_info;
atomic_t usage;
unsigned long flags; /* per process flags, defined below */
- unsigned long ptrace;
int lock_depth; /* BKL lock depth */
@@ -800,12 +799,6 @@ struct task_struct {
#endif
struct list_head tasks;
- /*
- * ptrace_list/ptrace_children forms the list of my children
- * that were stolen by a ptracer.
- */
- struct list_head ptrace_children;
- struct list_head ptrace_list;
struct mm_struct *mm, *active_mm;
@@ -820,15 +813,13 @@ struct task_struct {
pid_t pid;
pid_t tgid;
/*
- * pointers to (original) parent process, youngest child, younger sibling,
+ * pointers to parent process, youngest child, younger sibling,
* older sibling, respectively. (p->father can be replaced with
* p->parent->pid)
*/
- struct task_struct *real_parent; /* real parent process (when being debugged) */
struct task_struct *parent; /* parent process */
/*
- * children/sibling forms the list of my children plus the
- * tasks I'm ptracing.
+ * children/sibling forms the list of my children
*/
struct list_head children; /* list of my children */
struct list_head sibling; /* linkage in my parent's children list */
@@ -900,6 +891,11 @@ struct task_struct {
struct audit_context *audit_context;
seccomp_t seccomp;
+#ifdef CONFIG_UTRACE
+ struct utrace *utrace;
+ unsigned long utrace_flags;
+#endif
+
/* Thread group tracking */
u32 parent_exec_id;
u32 self_exec_id;
@@ -953,8 +949,6 @@ struct task_struct {
struct io_context *io_context;
- unsigned long ptrace_message;
- siginfo_t *last_siginfo; /* For ptrace use. */
/*
* current io wait handle: wait queue entry to use for io waits
* If this thread is processing aio, this points at the waitqueue
@@ -989,6 +983,10 @@ struct task_struct {
atomic_t fs_excl; /* holding fs exclusive resources */
struct rcu_head rcu;
+#ifdef CONFIG_PTRACE
+ struct list_head ptracees;
+#endif
+
/*
* cache last used pipe for splice
*/
@@ -1226,6 +1224,7 @@ extern int kill_pg_info(int, struct sigi
extern int kill_proc_info(int, struct siginfo *, pid_t);
extern int kill_proc_info_as_uid(int, struct siginfo *, pid_t, uid_t, uid_t, u32);
extern void do_notify_parent(struct task_struct *, int);
+extern void do_notify_parent_cldstop(struct task_struct *, int);
extern void force_sig(int, struct task_struct *);
extern void force_sig_specific(int, struct task_struct *);
extern int send_sig(int, struct task_struct *, int);
--- linux-2.6/include/linux/utrace.h.utrace-ptrace-compat
+++ linux-2.6/include/linux/utrace.h
@@ -0,0 +1,484 @@
+/*
+ * User Debugging Data & Event Rendezvous
+ *
+ * This interface allows for notification of interesting events in a thread.
+ * It also mediates access to thread state such as registers.
+ * Multiple unrelated users can be associated with a single thread.
+ * We call each of these a tracing engine.
+ *
+ * A tracing engine starts by calling utrace_attach on the chosen thread,
+ * passing in a set of hooks (struct utrace_engine_ops), and some associated
+ * data. This produces a struct utrace_attached_engine, which is the handle
+ * used for all other operations. An attached engine has its ops vector,
+ * its data, and a flags word controlled by utrace_set_flags.
+ *
+ * Each engine's flags word contains two kinds of flags: events of
+ * interest, and action state flags.
+ *
+ * For each event flag that is set, that engine will get the
+ * appropriate ops->report_* callback when the event occurs. The
+ * struct utrace_engine_ops need not provide callbacks for an event
+ * unless the engine sets one of the associated event flags.
+ *
+ * Action state flags change the normal behavior of the thread.
+ * These bits are in UTRACE_ACTION_STATE_MASK; these can be OR'd into
+ * flags set with utrace_set_flags. Also, every callback that return
+ * an action value can reset these bits for the engine (see below).
+ *
+ * The bits UTRACE_ACTION_STATE_MASK of all attached engines are OR'd
+ * together, so each action is in force as long as any engine requests it.
+ * As long as some engine sets the UTRACE_ACTION_QUIESCE flag, the thread
+ * will block and not resume running user code. When the last engine
+ * clears its UTRACE_ACTION_QUIESCE flag, the thread will resume running.
+ */
+
+#ifndef _LINUX_UTRACE_H
+#define _LINUX_UTRACE_H 1
+
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+#include <linux/signal.h>
+
+struct linux_binprm;
+struct pt_regs;
+struct utrace_regset;
+struct utrace_regset_view;
+
+
+/*
+ * Flags in task_struct.utrace_flags and utrace_attached_engine.flags.
+ * Low four bits are UTRACE_ACTION_STATE_MASK bits (below).
+ * Higher bits are events of interest.
+ */
+
+#define UTRACE_FIRST_EVENT 4
+#define UTRACE_EVENT_BITS (BITS_PER_LONG - UTRACE_FIRST_EVENT)
+#define UTRACE_EVENT_MASK (-1UL &~ UTRACE_ACTION_STATE_MASK)
+
+enum utrace_events {
+ _UTRACE_EVENT_QUIESCE, /* Tracing requests stop. */
+ _UTRACE_EVENT_REAP, /* Zombie reaped, no more tracing possible. */
+ _UTRACE_EVENT_CLONE, /* Successful clone/fork/vfork just done. */
+ _UTRACE_EVENT_VFORK_DONE, /* vfork woke from waiting for child. */
+ _UTRACE_EVENT_EXEC, /* Successful execve just completed. */
+ _UTRACE_EVENT_EXIT, /* Thread exit in progress. */
+ _UTRACE_EVENT_DEATH, /* Thread has died. */
+ _UTRACE_EVENT_SYSCALL_ENTRY, /* User entered kernel for system call. */
+ _UTRACE_EVENT_SYSCALL_EXIT, /* Returning to user after system call. */
+ _UTRACE_EVENT_SIGNAL, /* Signal delivery will run a user handler. */
+ _UTRACE_EVENT_SIGNAL_IGN, /* No-op signal to be delivered. */
+ _UTRACE_EVENT_SIGNAL_STOP, /* Signal delivery will suspend. */
+ _UTRACE_EVENT_SIGNAL_TERM, /* Signal delivery will terminate. */
+ _UTRACE_EVENT_SIGNAL_CORE, /* Signal delivery will dump core. */
+ _UTRACE_EVENT_JCTL, /* Job control stop or continue completed. */
+ _UTRACE_NEVENTS
+};
+#define UTRACE_EVENT_BIT(type) (UTRACE_FIRST_EVENT + _UTRACE_EVENT_##type)
+#define UTRACE_EVENT(type) (1UL << UTRACE_EVENT_BIT(type))
+
+/*
+ * All the kinds of signal events. These all use the report_signal callback.
+ */
+#define UTRACE_EVENT_SIGNAL_ALL (UTRACE_EVENT(SIGNAL) \
+ | UTRACE_EVENT(SIGNAL_IGN) \
+ | UTRACE_EVENT(SIGNAL_STOP) \
+ | UTRACE_EVENT(SIGNAL_TERM) \
+ | UTRACE_EVENT(SIGNAL_CORE))
+/*
+ * Both kinds of syscall events; these call the report_syscall_entry and
+ * report_syscall_exit callbacks, respectively.
+ */
+#define UTRACE_EVENT_SYSCALL \
+ (UTRACE_EVENT(SYSCALL_ENTRY) | UTRACE_EVENT(SYSCALL_EXIT))
+
+
+/*
+ * Action flags, in return value of callbacks.
+ *
+ * UTRACE_ACTION_RESUME (zero) is the return value to do nothing special.
+ * For each particular callback, some bits in UTRACE_ACTION_OP_MASK can
+ * be set in the return value to change the thread's behavior (see below).
+ *
+ * If UTRACE_ACTION_NEWSTATE is set, then the UTRACE_ACTION_STATE_MASK
+ * bits in the return value replace the engine's flags as in utrace_set_flags
+ * (but the event flags remained unchanged).
+ *
+ * If UTRACE_ACTION_HIDE is set, then the callbacks to other engines
+ * should be suppressed for this event. This is appropriate only when
+ * the event was artificially provoked by something this engine did,
+ * such as setting a breakpoint.
+ *
+ * If UTRACE_ACTION_DETACH is set, this engine is detached as by utrace_detach.
+ * The action bits in UTRACE_ACTION_OP_MASK work as normal, but the engine's
+ * UTRACE_ACTION_STATE_MASK bits will no longer affect the thread.
+ */
+#define UTRACE_ACTION_RESUME 0x0000 /* Continue normally after event. */
+#define UTRACE_ACTION_HIDE 0x0010 /* Hide event from other tracing. */
+#define UTRACE_ACTION_DETACH 0x0020 /* Detach me, state flags ignored. */
+#define UTRACE_ACTION_NEWSTATE 0x0040 /* Replace state bits. */
+
+/*
+ * These flags affect the state of the thread until they are changed via
+ * utrace_set_flags or by the next callback to the same engine that uses
+ * UTRACE_ACTION_NEWSTATE.
+ */
+#define UTRACE_ACTION_QUIESCE 0x0001 /* Stay quiescent after callbacks. */
+#define UTRACE_ACTION_SINGLESTEP 0x0002 /* Resume for one instruction. */
+#define UTRACE_ACTION_BLOCKSTEP 0x0004 /* Resume until next branch. */
+#define UTRACE_ACTION_NOREAP 0x0008 /* Inhibit parent SIGCHLD and wait. */
+#define UTRACE_ACTION_STATE_MASK 0x000f /* Lasting state bits. */
+
+/* These flags have meanings specific to the particular event report hook. */
+#define UTRACE_ACTION_OP_MASK 0xff00
+
+/*
+ * Action flags in return value and argument of report_signal callback.
+ */
+#define UTRACE_SIGNAL_DELIVER 0x0100 /* Deliver according to sigaction. */
+#define UTRACE_SIGNAL_IGN 0x0200 /* Ignore the signal. */
+#define UTRACE_SIGNAL_TERM 0x0300 /* Terminate the process. */
+#define UTRACE_SIGNAL_CORE 0x0400 /* Terminate with core dump. */
+#define UTRACE_SIGNAL_STOP 0x0500 /* Deliver as absolute stop. */
+#define UTRACE_SIGNAL_TSTP 0x0600 /* Deliver as job control stop. */
+#define UTRACE_SIGNAL_HOLD 0x1000 /* Flag, push signal back on queue. */
+/*
+ * This value is passed to a report_signal callback after a signal
+ * handler is entered while UTRACE_ACTION_SINGLESTEP is in force.
+ * For this callback, no signal will never actually be delivered regardless
+ * of the return value, and the other callback parameters are null.
+ */
+#define UTRACE_SIGNAL_HANDLER 0x0700
+
+/* Action flag in return value of report_jctl. */
+#define UTRACE_JCTL_NOSIGCHLD 0x0100 /* Do not notify the parent. */
+
+
+/*
+ * Flags for utrace_attach. If UTRACE_ATTACH_CREATE is not specified,
+ * you only look up an existing engine already attached to the
+ * thread. If UTRACE_ATTACH_MATCH_* bits are set, only consider
+ * matching engines. If UTRACE_ATTACH_EXCLUSIVE is set, attempting to
+ * attach a second (matching) engine fails with -EEXIST.
+ */
+#define UTRACE_ATTACH_CREATE 0x0010 /* Attach a new engine. */
+#define UTRACE_ATTACH_EXCLUSIVE 0x0020 /* Refuse if existing match. */
+#define UTRACE_ATTACH_MATCH_OPS 0x0001 /* Match engines on ops. */
+#define UTRACE_ATTACH_MATCH_DATA 0x0002 /* Match engines on data. */
+#define UTRACE_ATTACH_MATCH_MASK 0x000f
+
+
+/*
+ * Per-thread structure task_struct.utrace points to.
+ *
+ * The task itself never has to worry about this going away after
+ * some event is found set in task_struct.utrace_flags.
+ * Once created, this pointer is changed only when the task is quiescent
+ * (TASK_TRACED or TASK_STOPPED with the siglock held, or dead).
+ *
+ * For other parties, the pointer to this is protected by RCU and
+ * task_lock. Since call_rcu is never used while the thread is alive and
+ * using this struct utrace, we can overlay the RCU data structure used
+ * only for a dead struct with some local state used only for a live utrace
+ * on an active thread.
+ */
+struct utrace
+{
+ union {
+ struct rcu_head dead;
+ struct {
+ struct task_struct *cloning;
+ struct utrace_signal *signal;
+ } live;
+ struct {
+ int notified;
+ int reap;
+ } exit;
+ } u;
+
+ struct list_head engines;
+ spinlock_t lock;
+};
+#define utrace_lock(utrace) spin_lock(&(utrace)->lock)
+#define utrace_unlock(utrace) spin_unlock(&(utrace)->lock)
+
+
+/*
+ * Per-engine per-thread structure.
+ *
+ * The task itself never has to worry about engines detaching while
+ * it's doing event callbacks. These structures are freed only when
+ * the task is quiescent. For other parties, the list is protected
+ * by RCU and utrace_lock.
+ */
+struct utrace_attached_engine
+{
+ struct list_head entry; /* Entry on thread's utrace.engines list. */
+ struct rcu_head rhead;
+
+ const struct utrace_engine_ops *ops;
+ unsigned long data;
+
+ unsigned long flags;
+};
+
+
+struct utrace_engine_ops
+{
+ /*
+ * Event reporting hooks.
+ *
+ * Return values contain UTRACE_ACTION_* flag bits.
+ * The UTRACE_ACTION_OP_MASK bits are specific to each kind of event.
+ *
+ * All report_* hooks are called with no locks held, in a generally
+ * safe environment when we will be returning to user mode soon.
+ * It is fine to block for memory allocation and the like, but all
+ * hooks are *asynchronous* and must not block on external events.
+ * If you want the thread to block, request UTRACE_ACTION_QUIESCE in
+ * your hook; then later wake it up with utrace_set_flags.
+ *
+ */
+
+ /*
+ * Event reported for parent, before child might run.
+ * The PF_STARTING flag prevents other engines from attaching
+ * before this one has its chance.
+ */
+ u32 (*report_clone)(struct utrace_attached_engine *engine,
+ struct task_struct *parent,
+ unsigned long clone_flags,
+ struct task_struct *child);
+
+ /*
+ * Event reported for parent using CLONE_VFORK or vfork system call.
+ * The child has died or exec'd, so the vfork parent has unblocked
+ * and is about to return child_pid.
+ */
+ u32 (*report_vfork_done)(struct utrace_attached_engine *engine,
+ struct task_struct *parent, pid_t child_pid);
+
+ /*
+ * Event reported after UTRACE_ACTION_QUIESCE is set, when the target
+ * thread is quiescent. Either it's the current thread, or it's in
+ * TASK_TRACED or TASK_STOPPED and will not resume running until the
+ * UTRACE_ACTION_QUIESCE flag is no longer asserted by any engine.
+ */
+ u32 (*report_quiesce)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk);
+
+ /*
+ * Thread dequeuing a signal to be delivered.
+ * The action and *return_ka values say what UTRACE_ACTION_RESUME
+ * will do (possibly already influenced by another tracing engine).
+ * An UTRACE_SIGNAL_* return value overrides the signal disposition.
+ * The *info data (including info->si_signo) can be changed at will.
+ * Changing *return_ka affects the sigaction that be used.
+ * The *orig_ka value is the one in force before other tracing
+ * engines intervened.
+ */
+ u32 (*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);
+
+ /*
+ * Job control event completing, about to send SIGCHLD to parent
+ * with CLD_STOPPED or CLD_CONTINUED as given in type.
+ * UTRACE_JOBSTOP_NOSIGCHLD in the return value inhibits that.
+ */
+ u32 (*report_jctl)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ int type);
+
+ /*
+ * Thread has just completed an exec.
+ * The initial user register state is handy to be tweaked directly.
+ */
+ u32 (*report_exec)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ const struct linux_binprm *bprm,
+ struct pt_regs *regs);
+
+ /*
+ * Thread has entered the kernel to request a system call.
+ * The user register state is handy to be tweaked directly.
+ */
+ u32 (*report_syscall_entry)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ struct pt_regs *regs);
+
+ /*
+ * Thread is about to leave the kernel after a system call request.
+ * The user register state is handy to be tweaked directly.
+ */
+ u32 (*report_syscall_exit)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ struct pt_regs *regs);
+
+ /*
+ * Thread is exiting and cannot be prevented from doing so,
+ * but all its state is still live. The *code value will be
+ * the wait result seen by the parent, and can be changed by
+ * this engine or others. The orig_code value is the real
+ * status, not changed by any tracing engine.
+ */
+ u32 (*report_exit)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ long orig_code, long *code);
+
+ /*
+ * Thread is really dead now. If UTRACE_ACTION_NOREAP is in force,
+ * it remains an unreported zombie. Otherwise, it might be reaped
+ * by its parent, or self-reap immediately. Though the actual
+ * reaping may happen in parallel, a report_reap callback will
+ * always be ordered after a report_death callback.
+ */
+ u32 (*report_death)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk);
+
+ /*
+ * Called when someone reaps the dead task (parent, init, or self).
+ * No more callbacks are made after this one.
+ * The engine is always detached.
+ * There is nothing more a tracing engine can do about this thread.
+ */
+ void (*report_reap)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk);
+
+ /*
+ * Miscellaneous hooks. These are not associated with event reports.
+ * Any of these may be null if the engine has nothing to say.
+ * These hooks are called in more constrained environments and should
+ * not block or do very much.
+ */
+
+ /*
+ * Return nonzero iff the caller task should be allowed to access
+ * the memory of the target task via /proc/PID/mem and so forth,
+ * by dint of this engine's attachment to the target.
+ */
+ int (*allow_access_process_vm)(struct utrace_attached_engine *engine,
+ struct task_struct *target,
+ struct task_struct *caller);
+
+ /*
+ * Return LSM_UNSAFE_* bits that apply to the exec in progress
+ * due to tracing done by this engine. These bits indicate that
+ * someone is able to examine the process and so a set-UID or similar
+ * privilege escalation may not be safe to permit.
+ *
+ * Called with task_lock held.
+ */
+ int (*unsafe_exec)(struct utrace_attached_engine *engine,
+ struct task_struct *target);
+
+ /*
+ * Return the task_struct for the task using ptrace on this one, or
+ * NULL. Always called with rcu_read_lock held to keep the
+ * returned struct alive.
+ *
+ * At exec time, this may be called with task_lock(target) still
+ * held from when unsafe_exec was just called. In that case it
+ * must give results consistent with those unsafe_exec results,
+ * i.e. non-NULL if any LSM_UNSAFE_PTRACE_* bits were set.
+ *
+ * The value is also used to display after "TracerPid:" in
+ * /proc/PID/status, where it is called with only rcu_read_lock held.
+ *
+ * If this engine returns NULL, another engine may supply the result.
+ */
+ struct task_struct *(*tracer_task)(struct utrace_attached_engine *,
+ struct task_struct *target);
+};
+
+
+/***
+ *** These are the exported entry points for tracing engines to use.
+ ***/
+
+/*
+ * Attach a new tracing engine to a thread, or look up attached engines.
+ * See UTRACE_ATTACH_* flags, above. The caller must ensure that the
+ * target thread does not get freed, i.e. hold a ref or be its parent.
+ */
+struct utrace_attached_engine *utrace_attach(struct task_struct *target,
+ int flags,
+ const struct utrace_engine_ops *,
+ unsigned long data);
+
+/*
+ * Detach a tracing engine from a thread. After this, the engine
+ * data structure is no longer accessible, and the thread might be reaped.
+ * The thread will start running again if it was being kept quiescent
+ * and no longer has any attached engines asserting UTRACE_ACTION_QUIESCE.
+ *
+ * If the target thread is not already quiescent, then a callback to this
+ * engine might be in progress or about to start on another CPU. If it's
+ * quiescent when utrace_detach is called, then after return it's guaranteed
+ * that no more callbacks to the ops vector will be done.
+ */
+void utrace_detach(struct task_struct *target,
+ struct utrace_attached_engine *engine);
+
+/*
+ * Change the flags for a tracing engine.
+ * This resets the event flags and the action state flags.
+ * If UTRACE_ACTION_QUIESCE and UTRACE_EVENT(QUIESCE) are set,
+ * this will cause a report_quiesce callback soon, maybe immediately.
+ * If UTRACE_ACTION_QUIESCE was set before and is no longer set by
+ * any engine, this will wake the thread up.
+ */
+void utrace_set_flags(struct task_struct *target,
+ struct utrace_attached_engine *engine,
+ unsigned long flags);
+
+/*
+ * Cause a specified signal delivery in the target thread, which must be
+ * quiescent (or the current thread). The action has UTRACE_SIGNAL_* bits
+ * as returned from a report_signal callback. If ka is non-null, it gives
+ * the sigaction to follow for UTRACE_SIGNAL_DELIVER; otherwise, the
+ * installed sigaction at the time of delivery is used.
+ */
+int utrace_inject_signal(struct task_struct *target,
+ struct utrace_attached_engine *engine,
+ u32 action, siginfo_t *info,
+ const struct k_sigaction *ka);
+
+/*
+ * Prepare to access thread's machine state, see <linux/tracehook.h>.
+ * The given thread must be quiescent (or the current thread).
+ * When this returns, the struct utrace_regset calls may be used to
+ * interrogate or change the thread's state. Do not cache the returned
+ * pointer when the thread can resume. You must call utrace_regset to
+ * ensure that context switching has completed and consistent state is
+ * available.
+ */
+const struct utrace_regset *utrace_regset(struct task_struct *target,
+ struct utrace_attached_engine *,
+ const struct utrace_regset_view *,
+ int which);
+
+
+/*
+ * Hooks in <linux/tracehook.h> call these entry points to the utrace dispatch.
+ */
+void utrace_quiescent(struct task_struct *);
+void utrace_release_task(struct task_struct *);
+int utrace_get_signal(struct task_struct *, struct pt_regs *,
+ siginfo_t *, struct k_sigaction *);
+void utrace_report_clone(unsigned long clone_flags, struct task_struct *child);
+void utrace_report_vfork_done(pid_t child_pid);
+void utrace_report_exit(long *exit_code);
+void utrace_report_death(struct task_struct *, struct utrace *);
+int utrace_report_jctl(int type);
+void utrace_report_exec(struct linux_binprm *bprm, struct pt_regs *regs);
+void utrace_report_syscall(struct pt_regs *regs, int is_exit);
+struct task_struct *utrace_tracer_task(struct task_struct *);
+int utrace_allow_access_process_vm(struct task_struct *);
+int utrace_unsafe_exec(struct task_struct *);
+void utrace_signal_handler_singlestep(struct task_struct *, struct pt_regs *);
+
+
+#endif /* linux/utrace.h */
--- linux-2.6/include/linux/ptrace.h.utrace-ptrace-compat
+++ linux-2.6/include/linux/ptrace.h
@@ -49,66 +49,184 @@
#include <asm/ptrace.h>
#ifdef __KERNEL__
+#include <linux/compiler.h>
+#include <linux/types.h>
+struct task_struct;
+struct siginfo;
+struct rusage;
+
+
+extern int ptrace_may_attach(struct task_struct *task);
+
+
+#ifdef CONFIG_PTRACE
+#include <asm/tracehook.h>
+struct utrace_attached_engine;
+struct utrace_regset_view;
+
/*
- * Ptrace flags
+ * These must be defined by arch code to handle machine-specific ptrace
+ * requests such as PTRACE_PEEKUSR and PTRACE_GETREGS. Returns -ENOSYS for
+ * any request it does not handle, then handled by machine-independent code.
+ * This can change *request and then return -ENOSYS to handle a
+ * machine-specific alias for a generic request.
*
- * The owner ship rules for task->ptrace which holds the ptrace
- * flags is simple. When a task is running it owns it's task->ptrace
- * flags. When the a task is stopped the ptracer owns task->ptrace.
- */
-
-#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_ATTACHED 0x00000400 /* parent != real_parent */
-
-#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_disable(struct task_struct *);
-extern int ptrace_check_attach(struct task_struct *task, int kill);
-extern int ptrace_request(struct task_struct *child, long request, long addr, long data);
-extern void ptrace_notify(int exit_code);
-extern void __ptrace_link(struct task_struct *child,
- struct task_struct *new_parent);
-extern void __ptrace_unlink(struct task_struct *child);
-extern void ptrace_untrace(struct task_struct *child);
-extern int ptrace_may_attach(struct task_struct *task);
+ * This code should NOT access task machine state directly. Instead it
+ * should use the utrace_regset accessors. The functions below make this easy.
+ *
+ * Any nonzero return value should be for an error. If the return value of
+ * the ptrace syscall should be a nonzero success value, this returns zero
+ * and sets *retval to the value--which might have any bit pattern at all,
+ * including one that looks like -ENOSYS or another error code.
+ */
+extern fastcall int arch_ptrace(long *request, struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ unsigned long addr, unsigned long data,
+ long *retval);
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+
+extern fastcall int arch_compat_ptrace(compat_long_t *request,
+ struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ compat_ulong_t a, compat_ulong_t d,
+ compat_long_t *retval);
+#endif
+
+/*
+ * Convenience function doing access to a single utrace_regset for ptrace.
+ * The offset and size are in bytes, giving the location in the regset data.
+ */
+extern fastcall int ptrace_regset_access(struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ const struct utrace_regset_view *view,
+ int setno, unsigned long offset,
+ unsigned int size, void __user *data,
+ int write);
-static inline void ptrace_link(struct task_struct *child,
- struct task_struct *new_parent)
+/*
+ * Convenience wrapper for doing access to a whole utrace_regset for ptrace.
+ */
+static inline int ptrace_whole_regset(struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ long data, int setno, int write)
{
- if (unlikely(child->ptrace))
- __ptrace_link(child, new_parent);
+ return ptrace_regset_access(child, engine, utrace_native_view(current),
+ setno, 0, -1, (void __user *)data, write);
}
-static inline void ptrace_unlink(struct task_struct *child)
+
+/*
+ * Convenience function doing access to a single slot in a utrace_regset.
+ * The regno value gives a slot number plus regset->bias.
+ * The value accessed is regset->size bytes long.
+ */
+extern fastcall int ptrace_onereg_access(struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ const struct utrace_regset_view *view,
+ int setno, unsigned long regno,
+ void __user *data, int write);
+
+
+/*
+ * An array of these describes the layout of the virtual struct user
+ * accessed by PEEKUSR/POKEUSR, or the structure used by GETREGS et al.
+ * The array is terminated by an element with .end of zero.
+ * An element describes the range [.start, .end) of struct user offsets,
+ * measured in bytes; it maps to the regset in the view's regsets array
+ * at the index given by .regset, at .offset bytes into that regset's data.
+ * If .regset is -1, then the [.start, .end) range reads as zero.
+ */
+struct ptrace_layout_segment {
+ unsigned int start, end, regset, offset;
+};
+
+/*
+ * Convenience function for doing access to a ptrace compatibility layout.
+ * The offset and size are in bytes.
+ */
+extern fastcall int ptrace_layout_access(
+ struct task_struct *child, struct utrace_attached_engine *engine,
+ const struct utrace_regset_view *view,
+ const struct ptrace_layout_segment layout[],
+ unsigned long offset, unsigned int size,
+ void __user *data, void *kdata, int write);
+
+
+/* Convenience wrapper for the common PTRACE_PEEKUSR implementation. */
+static inline int ptrace_peekusr(struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ const struct ptrace_layout_segment layout[],
+ unsigned long addr, long data)
+{
+ return ptrace_layout_access(child, engine, utrace_native_view(current),
+ layout, addr, sizeof(long),
+ (unsigned long __user *)data, NULL, 0);
+}
+
+/* Convenience wrapper for the common PTRACE_PEEKUSR implementation. */
+static inline int ptrace_pokeusr(struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ const struct ptrace_layout_segment layout[],
+ unsigned long addr, long data)
+{
+ return ptrace_layout_access(child, engine, utrace_native_view(current),
+ layout, addr, sizeof(long),
+ NULL, &data, 1);
+}
+
+#ifdef CONFIG_COMPAT
+/* Convenience wrapper for the common PTRACE_PEEKUSR implementation. */
+static inline int ptrace_compat_peekusr(
+ struct task_struct *child, struct utrace_attached_engine *engine,
+ const struct ptrace_layout_segment layout[],
+ compat_ulong_t addr, compat_ulong_t data)
+{
+ compat_ulong_t *udata = (compat_ulong_t __user *) (unsigned long) data;
+ return ptrace_layout_access(child, engine, utrace_native_view(current),
+ layout, addr, sizeof(compat_ulong_t),
+ udata, NULL, 0);
+}
+
+/* Convenience wrapper for the common PTRACE_PEEKUSR implementation. */
+static inline int ptrace_compat_pokeusr(
+ struct task_struct *child, struct utrace_attached_engine *engine,
+ const struct ptrace_layout_segment layout[],
+ compat_ulong_t addr, compat_ulong_t data)
{
- if (unlikely(child->ptrace))
- __ptrace_unlink(child);
+ return ptrace_layout_access(child, engine, utrace_native_view(current),
+ layout, addr, sizeof(compat_ulong_t),
+ NULL, &data, 1);
}
+#endif
+
+
+/*
+ * Called in do_exit, after setting PF_EXITING, no locks are held.
+ */
+void ptrace_exit(struct task_struct *tsk);
+
+/*
+ * 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.
+ * In those cases the tasklist_lock is still held so next_thread(tsk) works.
+ * For any other return value, tasklist_lock is released before return.
+ */
+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
--- linux-2.6/include/linux/init_task.h.utrace-ptrace-compat
+++ linux-2.6/include/linux/init_task.h
@@ -98,9 +98,6 @@ extern struct group_info init_groups;
.ioprio = 0, \
.time_slice = HZ, \
.tasks = LIST_HEAD_INIT(tsk.tasks), \
- .ptrace_children= LIST_HEAD_INIT(tsk.ptrace_children), \
- .ptrace_list = LIST_HEAD_INIT(tsk.ptrace_list), \
- .real_parent = &tsk, \
.parent = &tsk, \
.children = LIST_HEAD_INIT(tsk.children), \
.sibling = LIST_HEAD_INIT(tsk.sibling), \
--- linux-2.6/include/linux/tracehook.h.utrace-ptrace-compat
+++ linux-2.6/include/linux/tracehook.h
@@ -0,0 +1,707 @@
+/*
+ * Tracing hooks
+ *
+ * This file defines hook entry points called by core code where
+ * user tracing/debugging support might need to do something.
+ * These entry points are called tracehook_*. Each hook declared below
+ * has a detailed comment giving the context (locking et al) from
+ * which it is called, and the meaning of its return value (if any).
+ *
+ * We also declare here tracehook_* functions providing access to low-level
+ * interrogation and control of threads. These functions must be called
+ * on either the current thread or on a quiescent thread. We say a
+ * thread is "quiescent" if it is in TASK_STOPPED or TASK_TRACED state,
+ * we are guaranteed it will not be woken up and return to user mode, and
+ * we have called wait_task_inactive on it.
+ */
+
+#ifndef _LINUX_TRACEHOOK_H
+#define _LINUX_TRACEHOOK_H 1
+
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+struct linux_binprm;
+struct pt_regs;
+
+
+/*
+ * The machine-specific asm/tracehook.h file is responsible for declaring
+ * the following entry points. These can be called only on a quiescent thread,
+ * or the current thread when it is about to return to user mode.
+ *
+ * Single-step control. When enabled, the next instruction or syscall exit
+ * produces a SIGTRAP. Enabling or disabling redundantly is harmless.
+ *
+ * void tracehook_enable_single_step(struct task_struct *tsk);
+ * void tracehook_disable_single_step(struct task_struct *tsk);
+ * int tracehook_single_step_enabled(struct task_struct *tsk);
+ *
+ * If those calls are defined, #define ARCH_HAS_SINGLE_STEP to nonzero.
+ * Do not #define it if these calls are never available in this kernel config.
+ * If defined, the value of ARCH_HAS_SINGLE_STEP can be constant or variable.
+ * It should evaluate to nonzero if the hardware is able to support
+ * tracehook_enable_single_step. If it's a variable expression, it
+ * should be one that can be evaluated in modules, i.e. uses exported symbols.
+ *
+ * Block-step control (trap on control transfer), when available.
+ * tracehook_disable_block_step will be called after tracehook_enable_single_step.
+ * When enabled, the next jump, or other control transfer or syscall exit,
+ * produces a SIGTRAP. Enabling or disabling redundantly is harmless.
+ *
+ * void tracehook_enable_block_step(struct task_struct *tsk);
+ * void tracehook_disable_block_step(struct task_struct *tsk);
+ * int tracehook_block_step_enabled(struct task_struct *tsk);
+ *
+ * If those calls are defined, #define ARCH_HAS_BLOCK_STEP to nonzero.
+ * Do not #define it if these calls are never available in this kernel config.
+ * If defined, the value of ARCH_HAS_BLOCK_STEP can be constant or variable.
+ * It should evaluate to nonzero if the hardware is able to support
+ * tracehook_enable_block_step. If it's a variable expression, it
+ * should be one that can be evaluated in modules, i.e. uses exported symbols.
+ *
+ * Control system call tracing. When enabled a syscall entry or exit
+ * produces a call to tracehook_report_syscall, below.
+ *
+ * void tracehook_enable_syscall_trace(struct task_struct *tsk);
+ * void tracehook_disable_syscall_trace(struct task_struct *tsk);
+ *
+ * When stopped in tracehook_report_syscall for syscall entry,
+ * abort the syscall so no kernel function is called.
+ * If the register state was not otherwise updated before,
+ * this produces an -ENOSYS error return as for an invalid syscall number.
+ *
+ * void tracehook_abort_syscall(struct pt_regs *regs);
+ *
+ * Return the regset view (see below) that is native for the given process.
+ * For example, what it would access when it called ptrace.
+ * Throughout the life of the process, this only changes at exec.
+ *
+ * const struct utrace_regset_view *utrace_native_view(struct task_struct *);
+ *
+ ***/
+
+
+/*
+ * This data structure describes a machine resource we call a register set.
+ * This is part of the state of an individual thread, not necessarily
+ * actual CPU registers per se. A register set consists of a number of
+ * similar slots, given by ->n. Each slot is ->size bytes, and aligned to
+ * ->align bytes (which is at least ->size).
+ *
+ * As described above, these entry points can be called on the current
+ * thread or on a quiescent thread. The pos argument must be aligned
+ * according to ->align; the count argument must be a multiple of ->size.
+ * These functions are not responsible for checking for invalid arguments.
+ *
+ * When there is a natural value to use as an index, ->bias gives the
+ * difference between the natural index and the slot index for the
+ * register set. For example, x86 GDT segment descriptors form a regset;
+ * the segment selector produces a natural index, but only a subset of
+ * that index space is available as a regset (the TLS slots); subtracting
+ * ->bias from a segment selector index value computes the regset slot.
+ */
+struct utrace_regset {
+ unsigned int n; /* Number of slots (registers). */
+ unsigned int size; /* Size in bytes of a slot (register). */
+ unsigned int align; /* Required alignment, in bytes. */
+ unsigned int bias; /* Bias from natural indexing. */
+
+ /*
+ * Return -ENODEV if not available on the hardware found.
+ * Return 0 if no interesting state in this thread.
+ * Return >0 number of ->size units of interesting state.
+ * Any get call fetching state beyond that number will
+ * see the default initialization state for this data,
+ * so a caller that knows that the default state is need
+ * not copy it all out.
+ * This call is optional; the pointer is NULL if there
+ * so no inexpensive check to yield a value < .n.
+ */
+ int (*active)(struct task_struct *, const struct utrace_regset *);
+
+ /*
+ * Fetch and store register values. Return 0 on success; -EIO or
+ * -ENODEV are usual failure returns. The pos and count values are
+ * in bytes, but must be properly aligned. If kbuf is non-null,
+ * that buffer is used and ubuf is ignored. If kbuf is NULL, then
+ * ubuf gives a userland pointer to access directly, and an -EFAULT
+ * return value is possible.
+ */
+ int (*get)(struct task_struct *, const struct utrace_regset *,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf);
+ int (*set)(struct task_struct *, const struct utrace_regset *,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf);
+
+ /*
+ * This call is optional; usually the pointer is NULL.
+ * When provided, there is some user memory associated
+ * with this regset's hardware, such as memory backing
+ * cached register data on register window machines; the
+ * regset's data controls what user memory is used
+ * (e.g. via the stack pointer value).
+ *
+ * Write register data back to user memory. If the
+ * immediate flag is nonzero, it must be written to the
+ * user memory so uaccess/access_process_vm can see it
+ * when this call returns; if zero, then it must be
+ * written back by the time the task completes a context
+ * switch (as synchronized with wait_task_inactive).
+ * Return 0 on success or if there was nothing to do,
+ * -EFAULT for a memory problem (bad stack pointer or
+ * whatever), or -EIO for a hardware problem.
+ */
+ int (*writeback)(struct task_struct *, const struct utrace_regset *,
+ int immediate);
+};
+
+/*
+ * A regset view is a collection of regsets (struct utrace_regset, above).
+ * This describes all the state of a thread that can be seen from a given
+ * architecture/ABI environment. More than one view might refer to the
+ * same utrace_regset, or more than one regset might refer to the same
+ * machine-specific state in the thread. For example, a 32-bit thread's
+ * state could be examined from the 32-bit view or from the 64-bit view.
+ * Either method reaches the same thread register state, doing appropriate
+ * widening or truncation.
+ */
+struct utrace_regset_view {
+ const char *name; /* Identifier, e.g. ELF_PLATFORM string. */
+
+ const struct utrace_regset *regsets;
+ unsigned int n;
+
+ /*
+ * EM_* value for which this is the native view, if any.
+ */
+ u16 e_machine;
+};
+
+
+/*
+ * These two are helpers for writing regset get/set functions in arch code.
+ * Use one or more calls sequentially for each chunk of regset data stored
+ * contiguously in memory. Call with constants for start_pos and end_pos,
+ * giving the range of byte positions in the regset that data corresponds
+ * to; end_pos can be -1 if this chunk is at the end of the regset layout.
+ * Each call updates the arguments to point past its chunk.
+ */
+
+static inline int
+utrace_regset_copyout(unsigned int *pos, unsigned int *count,
+ void **kbuf, void __user **ubuf,
+ const void *data, int start_pos, int end_pos)
+{
+ if (*count == 0)
+ return 0;
+ BUG_ON(*pos < start_pos);
+ if (end_pos < 0 || *pos < end_pos) {
+ unsigned int copy = (end_pos < 0 ? *count
+ : min(*count, end_pos - *pos));
+ data += *pos - start_pos;
+ if (*kbuf) {
+ memcpy(*kbuf, data, copy);
+ *kbuf += copy;
+ }
+ else if (copy_to_user(*ubuf, data, copy))
+ return -EFAULT;
+ else
+ *ubuf += copy;
+ *pos += copy;
+ *count -= copy;
+ }
+ return 0;
+}
+
+static inline int
+utrace_regset_copyin(unsigned int *pos, unsigned int *count,
+ const void **kbuf, const void __user **ubuf,
+ void *data, int start_pos, int end_pos)
+{
+ if (*count == 0)
+ return 0;
+ BUG_ON(*pos < start_pos);
+ if (end_pos < 0 || *pos < end_pos) {
+ unsigned int copy = (end_pos < 0 ? *count
+ : min(*count, end_pos - *pos));
+ data += *pos - start_pos;
+ if (*kbuf) {
+ memcpy(data, *kbuf, copy);
+ *kbuf += copy;
+ }
+ else if (copy_from_user(data, *ubuf, copy))
+ return -EFAULT;
+ else
+ *ubuf += copy;
+ *pos += copy;
+ *count -= copy;
+ }
+ return 0;
+}
+
+/*
+ * These two parallel the two above, but for portions of a regset layout
+ * that always read as all-zero or for which writes are ignored.
+ */
+static inline int
+utrace_regset_copyout_zero(unsigned int *pos, unsigned int *count,
+ void **kbuf, void __user **ubuf,
+ int start_pos, int end_pos)
+{
+ if (*count == 0)
+ return 0;
+ BUG_ON(*pos < start_pos);
+ if (end_pos < 0 || *pos < end_pos) {
+ unsigned int copy = (end_pos < 0 ? *count
+ : min(*count, end_pos - *pos));
+ if (*kbuf) {
+ memset(*kbuf, 0, copy);
+ *kbuf += copy;
+ }
+ else if (clear_user(*ubuf, copy))
+ return -EFAULT;
+ else
+ *ubuf += copy;
+ *pos += copy;
+ *count -= copy;
+ }
+ return 0;
+}
+
+static inline int
+utrace_regset_copyin_ignore(unsigned int *pos, unsigned int *count,
+ const void **kbuf, const void __user **ubuf,
+ int start_pos, int end_pos)
+{
+ if (*count == 0)
+ return 0;
+ BUG_ON(*pos < start_pos);
+ if (end_pos < 0 || *pos < end_pos) {
+ unsigned int copy = (end_pos < 0 ? *count
+ : min(*count, end_pos - *pos));
+ if (*kbuf)
+ *kbuf += copy;
+ else
+ *ubuf += copy;
+ *pos += copy;
+ *count -= copy;
+ }
+ return 0;
+}
+
+/**/
+
+
+/***
+ ***
+ *** Following are entry points from core code, where the user debugging
+ *** support can affect the normal behavior. The locking situation is
+ *** described for each call.
+ ***
+ ***/
+
+#ifdef CONFIG_UTRACE
+#include <linux/utrace.h>
+#endif
+
+
+/*
+ * Called in copy_process when setting up the copied task_struct,
+ * with tasklist_lock held for writing.
+ */
+static inline void tracehook_init_task(struct task_struct *child)
+{
+#ifdef CONFIG_UTRACE
+ child->utrace_flags = 0;
+ child->utrace = NULL;
+#endif
+}
+
+/*
+ * Called from release_task, no locks held.
+ * After this, there should be no tracing entanglements.
+ */
+static inline void tracehook_release_task(struct task_struct *p)
+{
+#ifdef CONFIG_UTRACE
+ smp_mb();
+ if (p->utrace != NULL)
+ utrace_release_task(p);
+#endif
+}
+
+/*
+ * Return nonzero to trigger a BUG_ON crash in release_task.
+ * This should verify that there is no tracing-related state
+ * still affecting the task_struct about to be released.
+ * Called with tasklist_lock held for writing.
+ */
+static inline int tracehook_check_released(struct task_struct *p)
+{
+#ifdef CONFIG_UTRACE
+ return unlikely(p->utrace != NULL);
+#endif
+ return 0;
+}
+
+/*
+ * do_notify_parent_cldstop calls this when it's about to generate a SIGCHLD
+ * for a job control stop. Return nonzero to prevent that signal generation.
+ * Called with tasklist_lock held for reading, sometimes with irqs disabled.
+ */
+static inline int tracehook_notify_cldstop(struct task_struct *tsk,
+ const siginfo_t *info)
+{
+#ifdef CONFIG_UTRACE
+ if (tsk->utrace_flags & UTRACE_ACTION_NOREAP)
+ return 1;
+#endif
+ return 0;
+}
+
+/*
+ * exit_notify calls this with tasklist_lock held for writing.
+ * Return nonzero to prevent any normal SIGCHLD generation for this
+ * thread's death (i.e. when it is not ignored and its thread group is
+ * empty). This call must set *noreap to 0, or to 1 to force this thread
+ * to become a zombie when it would normally reap itself.
+ * The *death_cookie is passed to tracehook_report_death (below).
+ */
+static inline int tracehook_notify_death(struct task_struct *tsk,
+ int *noreap, void **death_cookie)
+{
+ *death_cookie = NULL;
+#ifdef CONFIG_UTRACE
+ *death_cookie = tsk->utrace;
+ if (tsk->utrace_flags & UTRACE_ACTION_NOREAP) {
+ *noreap = 1;
+ return 1;
+ }
+#endif
+ *noreap = 0;
+ return 0;
+}
+
+/*
+ * Return zero iff tracing doesn't care to examine this fatal signal,
+ * so it can short-circuit normal delivery directly to a group exit.
+ * Called with tsk->sighand->siglock held.
+ */
+static inline int tracehook_consider_fatal_signal(struct task_struct *tsk,
+ int sig)
+{
+#ifdef CONFIG_UTRACE
+ return (tsk->utrace_flags & (UTRACE_EVENT(SIGNAL_TERM)
+ | UTRACE_EVENT(SIGNAL_CORE)));
+#endif
+ return 0;
+}
+
+/*
+ * Return zero iff tracing doesn't care to examine this ignored signal,
+ * so it can short-circuit normal delivery and never even get queued.
+ * Either the handler is SIG_DFL and sig's default is ignore, or it's SIG_IGN.
+ * Called with tsk->sighand->siglock held.
+ */
+static inline int tracehook_consider_ignored_signal(struct task_struct *tsk,
+ int sig, void *handler)
+{
+#ifdef CONFIG_UTRACE
+ return (tsk->utrace_flags & UTRACE_EVENT(SIGNAL_IGN));
+#endif
+ return 0;
+}
+
+
+/*
+ * Called with the siglock held when computing tsk's signal_pending flag.
+ * Return nonzero to force the signal_pending flag on, so that
+ * tracehook_induce_signal will be called before the next return to user mode.
+ */
+static inline int tracehook_induce_sigpending(struct task_struct *tsk)
+{
+#ifdef CONFIG_UTRACE
+ return unlikely(tsk->utrace_flags & UTRACE_ACTION_QUIESCE);
+#endif
+ return 0;
+}
+
+/*
+ * Called with the siglock held before dequeuing pending signals.
+ * Return zero to check for a real pending signal normally.
+ * Return -1 after releasing the siglock to repeat the check.
+ * Return a signal number to induce an artifical signal delivery,
+ * setting *info and *return_ka to specify its details and behavior.
+ */
+static inline int tracehook_get_signal(struct task_struct *tsk,
+ struct pt_regs *regs,
+ siginfo_t *info,
+ struct k_sigaction *return_ka)
+{
+#ifdef CONFIG_UTRACE
+ if (unlikely(tsk->utrace_flags))
+ return utrace_get_signal(tsk, regs, info, return_ka);
+#endif
+ return 0;
+}
+
+/*
+ * Called with no locks held when about to stop for job control;
+ * we are already in TASK_STOPPED state, about to call schedule.
+ * Return zero if the normal SIGCHLD should be generated, which
+ * will happen if last_one is true meaning this is the last thread
+ * in the thread group to stop.
+ */
+static inline int tracehook_finish_stop(int last_one)
+{
+#ifdef CONFIG_UTRACE
+ if (current->utrace_flags & UTRACE_EVENT(JCTL))
+ return utrace_report_jctl(CLD_STOPPED);
+#endif
+
+ return 0;
+}
+
+/*
+ * Called with tasklist_lock held for reading, for an event notification stop.
+ * We are already in TASK_TRACED. Return zero to go back to running,
+ * or nonzero to actually stop until resumed.
+ */
+static inline int tracehook_stop_now(void)
+{
+ return 0;
+}
+
+
+/*
+ * Return nonzero if the child's parent (current) should be prevented
+ * from seeing its child in TASK_STOPPED state when it waits with WSTOPPED.
+ * Called with tasklist_lock held for reading.
+ */
+static inline int tracehook_inhibit_wait_stopped(struct task_struct *child)
+{
+#ifdef CONFIG_UTRACE
+ return (child->utrace_flags & UTRACE_ACTION_NOREAP);
+#endif
+ return 0;
+}
+
+/*
+ * Return nonzero if the child's parent (current) should be prevented
+ * from seeing its child in TASK_ZOMBIE state when it waits with WEXITED.
+ * Called with tasklist_lock held for reading.
+ */
+static inline int tracehook_inhibit_wait_zombie(struct task_struct *child)
+{
+#ifdef CONFIG_UTRACE
+ return (child->utrace_flags & UTRACE_ACTION_NOREAP);
+#endif
+ return 0;
+}
+
+/*
+ * Return nonzero if the child's parent (current) should be prevented
+ * from seeing its child resuming after job stop when it waits with WCONTINUED.
+ * Called with tasklist_lock held for reading.
+ */
+static inline int tracehook_inhibit_wait_continued(struct task_struct *child)
+{
+#ifdef CONFIG_UTRACE
+ return (child->utrace_flags & UTRACE_ACTION_NOREAP);
+#endif
+ return 0;
+}
+
+
+/*
+ * Return LSM_UNSAFE_* bits applied to an exec because of tracing.
+ * Called with task_lock(tsk) held.
+ */
+static inline int tracehook_unsafe_exec(struct task_struct *tsk)
+{
+#ifdef CONFIG_UTRACE
+ if (tsk->utrace_flags)
+ return utrace_unsafe_exec(tsk);
+#endif
+ return 0;
+}
+
+/*
+ * Return the task_struct for the task using ptrace on this one, or NULL.
+ * Must be called with rcu_read_lock held to keep the returned struct alive.
+ *
+ * At exec time, this may be called with task_lock(p) still held from when
+ * tracehook_unsafe_exec was just called.
+ *
+ * The value is also used to display after "TracerPid:" in /proc/PID/status,
+ * where it is called with only rcu_read_lock held.
+ */
+static inline struct task_struct *tracehook_tracer_task(struct task_struct *p)
+{
+#ifdef CONFIG_UTRACE
+ if (p->utrace_flags)
+ return utrace_tracer_task(p);
+#endif
+ return NULL;
+}
+
+/*
+ * Return nonzero if the current task should be allowed to use
+ * access_process_vm on the given task.
+ */
+static inline int tracehook_allow_access_process_vm(struct task_struct *tsk)
+{
+ if (tsk == current)
+ return 1;
+#ifdef CONFIG_UTRACE
+ if (tsk->utrace_flags)
+ return utrace_allow_access_process_vm(tsk);
+#endif
+ return 0;
+}
+
+
+/***
+ ***
+ *** Following decelarations are hook stubs where core code reports
+ *** events. These are called without locks, from the thread having the
+ *** event. In all tracehook_report_* calls, no locks are held and the thread
+ *** is in a state close to returning to user mode with little baggage to
+ *** unwind, except as noted below for tracehook_report_clone. It is generally
+ *** OK to block in these places if you want the user thread to be suspended.
+ ***
+ ***/
+
+/*
+ * Thread has just become a zombie (exit_state==TASK_ZOMBIE) or is about to
+ * self-reap (exit_state==EXIT_DEAD). If normal reaping is not inhibited,
+ * tsk->exit_state might be changing in parallel. The death_cookie was
+ * passed back by tracehook_notify_death (above).
+ */
+static inline void tracehook_report_death(struct task_struct *tsk,
+ int exit_state, void *death_cookie)
+{
+#ifdef CONFIG_UTRACE
+ smp_mb();
+ if (tsk->utrace_flags & (UTRACE_EVENT(DEATH) | UTRACE_ACTION_QUIESCE))
+ utrace_report_death(tsk, death_cookie);
+#endif
+}
+
+/*
+ * exec completed, we are shortly going to return to user mode.
+ * The freshly initialized register state can be seen and changed here.
+ */
+static inline void tracehook_report_exec(struct linux_binprm *bprm,
+ struct pt_regs *regs)
+{
+#ifdef CONFIG_UTRACE
+ if (current->utrace_flags & UTRACE_EVENT(EXEC))
+ utrace_report_exec(bprm, regs);
+#endif
+}
+
+/*
+ * Called from do_exit, we are about to exit. The code returned to the
+ * parent for wait can be changed here.
+ */
+static inline void tracehook_report_exit(long *exit_code)
+{
+#ifdef CONFIG_UTRACE
+ if (current->utrace_flags & UTRACE_EVENT(EXIT))
+ utrace_report_exit(exit_code);
+#endif
+}
+
+/*
+ * Called after a child is set up, but before it has been started or
+ * been given its CLONE_STOPPED initial stop. (See also tracehook_init_task.)
+ * This is not a good place to block, because the child has not started yet.
+ * Suspend the child here if desired, and block in clone_complete (below).
+ * This must prevent the child from self-reaping if clone_complete uses
+ * the task_struct pointer; otherwise it might have died and been released
+ * by the time tracehook_report_clone_complete is called.
+ */
+static inline void tracehook_report_clone(unsigned long clone_flags,
+ struct task_struct *child)
+{
+#ifdef CONFIG_UTRACE
+ if (current->utrace_flags & UTRACE_EVENT(CLONE))
+ utrace_report_clone(clone_flags, child);
+#endif
+}
+
+/*
+ * Called after the child has started running, shortly after
+ * tracehook_report_clone. This is just before the clone/fork syscall
+ * returns, or blocks for vfork child completion if (clone_flags &
+ * CLONE_VFORK). The child pointer may be invalid if a self-reaping
+ * child died and tracehook_report_clone took no action to prevent it
+ * from self-reaping.
+ */
+static inline void tracehook_report_clone_complete(unsigned long clone_flags,
+ pid_t pid,
+ struct task_struct *child)
+{
+#ifdef CONFIG_UTRACE
+ if (current->utrace_flags & UTRACE_ACTION_QUIESCE)
+ utrace_quiescent(current);
+#endif
+}
+
+/*
+ * Called after a CLONE_VFORK parent has waited for the child to complete.
+ * The clone/vfork system call will return immediately after this.
+ * The child pointer may be invalid if a self-reaping child died and
+ * tracehook_report_clone took no action to prevent it from self-reaping.
+ */
+static inline void tracehook_report_vfork_done(struct task_struct *child,
+ pid_t child_pid)
+{
+#ifdef CONFIG_UTRACE
+ if (current->utrace_flags & UTRACE_EVENT(VFORK_DONE))
+ utrace_report_vfork_done(child_pid);
+#endif
+}
+
+/*
+ * Called for system call entry or exit.
+ */
+static inline void tracehook_report_syscall(struct pt_regs *regs, int is_exit)
+{
+#ifdef CONFIG_UTRACE
+ if (current->utrace_flags & (is_exit ? UTRACE_EVENT(SYSCALL_EXIT)
+ : UTRACE_EVENT(SYSCALL_ENTRY)))
+ utrace_report_syscall(regs, is_exit);
+#endif
+}
+
+/*
+ * Called after system call exit if single/block-stepped into the syscall.
+ */
+static inline void tracehook_report_syscall_step(struct pt_regs *regs)
+{
+}
+
+/*
+ * Called when a signal handler has been set up.
+ * Register and stack state reflects the user handler about to run.
+ * Signal mask changes have already been made.
+ */
+static inline void tracehook_report_handle_signal(int sig,
+ const struct k_sigaction *ka,
+ const sigset_t *oldset,
+ struct pt_regs *regs)
+{
+#ifdef CONFIG_UTRACE
+ struct task_struct *tsk = current;
+ if ((tsk->utrace_flags & UTRACE_EVENT_SIGNAL_ALL)
+ && (tsk->utrace_flags & (UTRACE_ACTION_SINGLESTEP
+ | UTRACE_ACTION_BLOCKSTEP)))
+ utrace_signal_handler_singlestep(tsk, regs);
+#endif
+}
+
+
+#endif /* <linux/tracehook.h> */
--- linux-2.6/Documentation/utrace.txt.utrace-ptrace-compat
+++ linux-2.6/Documentation/utrace.txt
@@ -0,0 +1,455 @@
+DRAFT DRAFT DRAFT WORK IN PROGRESS DRAFT DRAFT DRAFT
+
+This is work in progress and likely to change.
+
+
+ Roland McGrath <roland@redhat.com>
+
+---
+
+ User Debugging Data & Event Rendezvous
+ ---- --------- ---- - ----- ----------
+
+See linux/utrace.h for all the declarations used here.
+See also linux/tracehook.h for the utrace_regset declarations.
+
+The UTRACE is infrastructure code for tracing and controlling user
+threads. This is the foundation for writing tracing engines, which
+can be loadable kernel modules. The UTRACE interfaces provide three
+basic facilities:
+
+* Thread event reporting
+
+ Tracing engines can request callbacks for events of interest in
+ the thread: signals, system calls, exit, exec, clone, etc.
+
+* Core thread control
+
+ Tracing engines can prevent a thread from running (keeping it in
+ TASK_TRACED state), or make it single-step or block-step (when
+ hardware supports it). Engines can cause a thread to abort system
+ calls, they change the behaviors of signals, and they can inject
+ signal-style actions at will.
+
+* Thread machine state access
+
+ Tracing engines can read and write a thread's registers and
+ similar per-thread CPU state.
+
+
+ Tracing engines
+ ------- -------
+
+The basic actors in UTRACE are the thread and the tracing engine.
+A tracing engine is some body of code that calls into the utrace_*
+interfaces, represented by a struct utrace_engine_ops. (Usually it's a
+kernel module, though the legacy ptrace support is a tracing engine
+that is not in a kernel module.) The UTRACE interface operates on
+individual threads (struct task_struct). If an engine wants to
+treat several threads as a group, that is up to its higher-level
+code. Using the UTRACE starts out by attaching an engine to a thread.
+
+ struct utrace_attached_engine *
+ utrace_attach(struct task_struct *target, int flags,
+ const struct utrace_engine_ops *ops, unsigned long data);
+
+Calling utrace_attach is what sets up a tracing engine to trace a
+thread. Use UTRACE_ATTACH_CREATE in flags, and pass your engine's ops.
+Check the return value with IS_ERR. If successful, it returns a
+struct pointer that is the handle used in all other utrace_* calls.
+The data argument is stored in the utrace_attached_engine structure,
+for your code to use however it wants.
+
+ void utrace_detach(struct task_struct *target,
+ struct utrace_attached_engine *engine);
+
+The utrace_detach call removes an engine from a thread.
+No more callbacks will be made after this returns.
+
+
+An attached engine does nothing by default.
+An engine makes something happen by setting its flags.
+
+ void utrace_set_flags(struct task_struct *target,
+ struct utrace_attached_engine *engine,
+ unsigned long flags);
+
+
+ Action Flags
+ ------ -----
+
+There are two kinds of flags that an attached engine can set: event
+flags, and action flags. Event flags register interest in particular
+events; when an event happens and an engine has the right event flag
+set, it gets a callback. Action flags change the normal behavior of
+the thread. The action flags available are:
+
+ UTRACE_ACTION_QUIESCE
+
+ The thread will stay quiescent (see below).
+ As long as any engine asserts the QUIESCE action flag,
+ the thread will not resume running in user mode.
+ (Usually it will be in TASK_TRACED state.)
+ Nothing will wake the thread up except for SIGKILL
+ (and implicit SIGKILLs such as a core dump in
+ another thread sharing the same address space, or a
+ group exit or fatal signal in another thread in the
+ same thread group).
+
+ UTRACE_ACTION_SINGLESTEP
+
+ When the thread runs, it will run one instruction
+ and then trap. (Exiting a system call or entering a
+ signal handler is considered "an instruction" for this.)
+ This can be used only if ARCH_HAS_SINGLE_STEP #define'd
+ by <asm/tracehook.h> and evaluates to nonzero.
+
+ UTRACE_ACTION_BLOCKSTEP
+
+ When the thread runs, it will run until the next branch,
+ and then trap. (Exiting a system call or entering a
+ signal handler is considered a branch for this.)
+ When the SINGLESTEP flag is set, BLOCKSTEP has no effect.
+ This is only available on some machines (actually none yet).
+ This can be used only if ARCH_HAS_BLOCK_STEP #define'd
+ by <asm/tracehook.h> and evaluates to nonzero.
+
+ UTRACE_ACTION_NOREAP
+
+ When the thread exits or stops for job control, its
+ parent process will not receive a SIGCHLD and the
+ parent's wait calls will not wake up or report the
+ child as dead. A well-behaved tracing engine does not
+ want to interfere with the parent's normal notifications.
+ This is provided mainly for the ptrace compatibility
+ code to implement the traditional behavior.
+
+Event flags are specified using the macro UTRACE_EVENT(TYPE).
+Each event type is associated with a report_* callback in struct
+utrace_engine_ops. A tracing engine can leave unused callbacks NULL.
+The only callbacks required are those used by the event flags it sets.
+
+Many engines can be attached to each thread. When a thread has an
+event, each engine gets a report_* callback if it has set the event flag
+for that event type. Engines are called in the order they attached.
+
+Each callback takes arguments giving the details of the particular
+event. The first two arguments two every callback are the struct
+utrace_attached_engine and struct task_struct pointers for the engine
+and the thread producing the event. Usually this will be the current
+thread that is running the callback functions.
+
+The return value of report_* callbacks is a bitmask. Some bits are
+common to all callbacks, and some are particular to that callback and
+event type. The value zero (UTRACE_ACTION_RESUME) always means the
+simplest thing: do what would have happened with no tracing engine here.
+These are the flags that can be set in any report_* return value:
+
+ UTRACE_ACTION_NEWSTATE
+
+ Update the action state flags, described above. Those
+ bits from the return value (UTRACE_ACTION_STATE_MASK)
+ replace those bits in the engine's flags. This has the
+ same effect as calling utrace_set_flags, but is a more
+ efficient short-cut. To change the event flags, you must
+ call utrace_set_flags.
+
+ UTRACE_ACTION_DETACH
+
+ Detach this engine. This has the effect of calling
+ utrace_detach, but is a more efficient short-cut.
+
+ UTRACE_ACTION_HIDE
+
+ Hide this event from other tracing engines. This is
+ only appropriate to do when the event was induced by
+ some action of this engine, such as a breakpoint trap.
+ Some events cannot be hidden, since every engine has to
+ know about them: exit, death, reap.
+
+The return value bits in UTRACE_ACTION_OP_MASK indicate a change to the
+normal behavior of the event taking place. If zero, the thread does
+whatever that event normally means. For report_signal, other values
+control the disposition of the signal.
+
+
+ Quiescence
+ ----------
+
+To control another thread and access its state, it must be "quiescent".
+This means that it is stopped and won't start running again while we access
+it. A quiescent thread is stopped in a place close to user mode, where the
+user state can be accessed safely; either it's about to return to user
+mode, or it's just entered the kernel from user mode, or it has already
+finished exiting (TASK_ZOMBIE). Setting the UTRACE_ACTION_QUIESCE action
+flag will force the attached thread to become quiescent soon. After
+setting the flag, an engine must wait for an event callback when the thread
+becomes quiescent. The thread may be running on another CPU, or may be in
+an uninterruptible wait. When it is ready to be examined, it will make
+callbacks to engines that set the UTRACE_EVENT(QUIESCE) event flag.
+
+As long as some engine has UTRACE_ACTION_QUIESCE set, then the thread will
+remain stopped. SIGKILL will wake it up, but it will not run user code.
+When the flag is cleared via utrace_set_flags or a callback return value,
+the thread starts running again.
+
+During the event callbacks (report_*), the thread in question makes the
+callback from a safe place. It is not quiescent, but it can safely access
+its own state. Callbacks can access thread state directly without setting
+the QUIESCE action flag. If a callback does want to prevent the thread
+from resuming normal execution, it *must* use the QUIESCE action state
+rather than simply blocking; see "Core Events & Callbacks", below.
+
+
+ Thread control
+ ------ -------
+
+These calls must be made on a quiescent thread (or the current thread):
+
+ int utrace_inject_signal(struct task_struct *target,
+ struct utrace_attached_engine *engine,
+ u32 action, siginfo_t *info,
+ const struct k_sigaction *ka);
+
+Cause a specified signal delivery in the target thread. This is not
+like kill, which generates a signal to be dequeued and delivered later.
+Injection directs the thread to deliver a signal now, before it next
+resumes in user mode or dequeues any other pending signal. It's as if
+the tracing engine intercepted a signal event and its report_signal
+callback returned the action argument as its value (see below). The
+info and ka arguments serve the same purposes as their counterparts in
+a report_signal callback.
+
+ const struct utrace_regset *
+ utrace_regset(struct task_struct *target,
+ struct utrace_attached_engine *engine,
+ const struct utrace_regset_view *view,
+ int which);
+
+Get access to machine state for the thread. The struct utrace_regset_view
+indicates a view of machine state, corresponding to a user mode
+architecture personality (such as 32-bit or 64-bit versions of a machine).
+The which argument selects one of the register sets available in that view.
+The utrace_regset call must be made before accessing any machine state,
+each time the thread has been running and has then become quiescent.
+It ensures that the thread's state is ready to be accessed, and returns
+the struct utrace_regset giving its accessor functions.
+
+XXX needs front ends for argument checks, export utrace_native_view
+
+
+ Core Events & Callbacks
+ ---- ------ - ---------
+
+Event reporting callbacks have details particular to the event type, but
+are all called in similar environments and have the same constraints.
+Callbacks are made from safe spots, where no locks are held, no special
+resources are pinned, and the user-mode state of the thread is accessible.
+So, callback code has a pretty free hand. But to be a good citizen,
+callback code should never block for long periods. It is fine to block in
+kmalloc and the like, but never wait for i/o or for user mode to do
+something. If you need the thread to wait, set UTRACE_ACTION_QUIESCE and
+return from the callback quickly. When your i/o finishes or whatever, you
+can use utrace_set_flags to resume the thread.
+
+Well-behaved callbacks are important to maintain two essential properties
+of the interface. The first of these is that unrelated tracing engines not
+interfere with each other. If your engine's event callback does not return
+quickly, then another engine won't get the event notification in a timely
+manner. The second important property is that tracing be as noninvasive as
+possible to the normal operation of the system overall and of the traced
+thread in particular. That is, attached tracing engines should not perturb
+a thread's behavior, except to the extent that changing its user-visible
+state is explicitly what you want to do. (Obviously some perturbation is
+unavoidable, primarily timing changes, ranging from small delays due to the
+overhead of tracing, to arbitrary pauses in user code execution when a user
+stops a thread with a debugger for examination. When doing asynchronous
+utrace_attach to a thread doing a system call, more troublesome side
+effects are possible.) Even when you explicitly want the pertrubation of
+making the traced thread block, just blocking directly in your callback has
+more unwanted effects. For example, the CLONE event callbacks are called
+when the new child thread has been created but not yet started running; the
+child can never be scheduled until the CLONE tracing callbacks return.
+(This allows engines tracing the parent to attach to the child.) If a
+CLONE event callback blocks the parent thread, it also prevents the child
+thread from running (even to process a SIGKILL). If what you want is to
+make both the parent and child block, then use utrace_attach on the child
+and then set the QUIESCE action state flag on both threads. A more crucial
+problem with blocking in callbacks is that it can prevent SIGKILL from
+working. A thread that is blocking due to UTRACE_ACTION_QUIESCE will still
+wake up and die immediately when sent a SIGKILL, as all threads should.
+Relying on the utrace infrastructure rather than on private synchronization
+calls in event callbacks is an important way to help keep tracing robustly
+noninvasive.
+
+
+EVENT(REAP) Dead thread has been reaped
+Callback:
+ void (*report_reap)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk);
+
+This means the parent called wait, or else this was a detached thread or
+a process whose parent ignores SIGCHLD. This cannot happen while the
+UTRACE_ACTION_NOREAP flag is set. This is the only callback you are
+guaranteed to get (if you set the flag).
+
+Unlike other callbacks, this can be called from the parent's context
+rather than from the traced thread itself--it must not delay the parent by
+blocking. This callback is different from all others, it returns void.
+Once you get this callback, your engine is automatically detached and you
+cannot access this thread or use this struct utrace_attached_engine handle
+any longer. This is the place to clean up your data structures and
+synchronize with your code that might try to make utrace_* calls using this
+engine data structure. The struct is still valid during this callback,
+but will be freed soon after it returns (via RCU).
+
+In all other callbacks, the return value is as described above.
+The common UTRACE_ACTION_* flags in the return value are always observed.
+Unless otherwise specified below, other bits in the return value are ignored.
+
+
+EVENT(QUIESCE) Thread is quiescent
+Callback:
+ u32 (*report_quiesce)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk);
+
+This is the least interesting callback. It happens at any safe spot,
+including after any other event callback. This lets the tracing engine
+know that it is safe to access the thread's state, or to report to users
+that it has stopped running user code.
+
+EVENT(CLONE) Thread is creating a child
+Callback:
+ u32 (*report_clone)(struct utrace_attached_engine *engine,
+ struct task_struct *parent,
+ unsigned long clone_flags,
+ struct task_struct *child);
+
+A clone/clone2/fork/vfork system call has succeeded in creating a new
+thread or child process. The new process is fully formed, but not yet
+running. During this callback, other tracing engines are prevented from
+using utrace_attach asynchronously on the child, so that engines tracing
+the parent get the first opportunity to attach. After this callback
+returns, the child will start and the parent's system call will return.
+If CLONE_VFORK is set, the parent will block before returning.
+
+EVENT(VFORK_DONE) Finished waiting for CLONE_VFORK child
+Callback:
+ u32 (*report_vfork_done)(struct utrace_attached_engine *engine,
+ struct task_struct *parent, pid_t child_pid);
+
+Event reported for parent using CLONE_VFORK or vfork system call.
+The child has died or exec'd, so the vfork parent has unblocked
+and is about to return child_pid.
+
+UTRACE_EVENT(EXEC) Completed exec
+Callback:
+ u32 (*report_exec)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ const struct linux_binprm *bprm,
+ struct pt_regs *regs);
+
+An execve system call has succeeded and the new program is about to
+start running. The initial user register state is handy to be tweaked
+directly, or utrace_regset can be used for full machine state access.
+
+UTRACE_EVENT(EXIT) Thread is exiting
+Callback:
+ u32 (*report_exit)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ long orig_code, long *code);
+
+The thread is exiting and cannot be prevented from doing so, but all its
+state is still live. The *code value will be the wait result seen by
+the parent, and can be changed by this engine or others. The orig_code
+value is the real status, not changed by any tracing engine.
+
+UTRACE_EVENT(DEATH) Thread has finished exiting
+Callback:
+ u32 (*report_death)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk);
+
+The thread is really dead now. If the UTRACE_ACTION_NOREAP flag is set
+after this callback, it remains an unreported zombie. Otherwise, it might
+be reaped by its parent, or self-reap immediately. Though the actual
+reaping may happen in parallel, a report_reap callback will always be
+ordered after a report_death callback.
+
+UTRACE_EVENT(SYSCALL_ENTRY) Thread has entered kernel for a system call
+Callback:
+ u32 (*report_syscall_entry)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ struct pt_regs *regs);
+
+The system call number and arguments can be seen and modified in the
+registers. The return value register has -ENOSYS, which will be
+returned for an invalid system call. The macro tracehook_abort_syscall(regs)
+will abort the system call so that we go immediately to syscall exit,
+and return -ENOSYS (or whatever the register state is changed to). If
+tracing enginges keep the thread quiescent here, the system call will
+not be performed until it resumes.
+
+UTRACE_EVENT(SYSCALL_EXIT) Thread is leaving kernel after a system call
+Callback:
+ u32 (*report_syscall_exit)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ struct pt_regs *regs);
+
+The return value can be seen and modified in the registers. If the
+thread is allowed to resume, it will see any pending signals and then
+return to user mode.
+
+UTRACE_EVENT(SIGNAL) Signal caught by user handler
+UTRACE_EVENT(SIGNAL_IGN) Signal with no effect (SIG_IGN or default)
+UTRACE_EVENT(SIGNAL_STOP) Job control stop signal
+UTRACE_EVENT(SIGNAL_TERM) Fatal termination signal
+UTRACE_EVENT(SIGNAL_CORE) Fatal core-dump signal
+UTRACE_EVENT_SIGNAL_ALL All of the above (bitmask)
+Callback:
+ u32 (*report_signal)(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ u32 action, siginfo_t *info,
+ const struct k_sigaction *orig_ka,
+ struct k_sigaction *return_ka);
+
+There are five types of signal events, but all use the same callback.
+These happen when a thread is dequeuing a signal to be delivered.
+(Not immediately when the signal is sent, and not when the signal is
+blocked.) No signal event is reported for SIGKILL; no tracing engine
+can prevent it from killing the thread immediately. The specific
+event types allow an engine to trace signals based on what they do.
+UTRACE_EVENT_SIGNAL_ALL is all of them OR'd together, to trace all
+signals (except SIGKILL). A subset of these event flags can be used
+e.g. to catch only fatal signals, not handled ones, or to catch only
+core-dump signals, not normal termination signals.
+
+The action argument says what the signal's default disposition is:
+
+ UTRACE_SIGNAL_DELIVER Run the user handler from sigaction.
+ UTRACE_SIGNAL_IGN Do nothing, ignore the signal.
+ UTRACE_SIGNAL_TERM Terminate the process.
+ UTRACE_SIGNAL_CORE Terminate the process a write a core dump.
+ UTRACE_SIGNAL_STOP Absolutely stop the process, a la SIGSTOP.
+ UTRACE_SIGNAL_TSTP Job control stop (no stop if orphaned).
+
+This selection is made from consulting the process's sigaction and the
+default action for the signal number, but may already have been
+changed by an earlier tracing engine (in which case you see its override).
+A return value of UTRACE_ACTION_RESUME means to carry out this action.
+If instead UTRACE_SIGNAL_* bits are in the return value, that overrides
+the normal behavior of the signal.
+
+The signal number and other details of the signal are in info, and
+this data can be changed to make the thread see a different signal.
+A return value of UTRACE_SIGNAL_DELIVER says to follow the sigaction in
+return_ka, which can specify a user handler or SIG_IGN to ignore the
+signal or SIG_DFL to follow the default action for info->si_signo.
+The orig_ka parameter shows the process's sigaction at the time the
+signal was dequeued, and return_ka initially contains this. Tracing
+engines can modify return_ka to change the effects of delivery.
+For other UTRACE_SIGNAL_* return values, return_ka is ignored.
+
+UTRACE_SIGNAL_HOLD is a flag bit that can be OR'd into the return
+value. It says to push the signal back on the thread's queue, with
+the signal number and details possibly changed in info. When the
+thread is allowed to resume, it will dequeue and report it again.
--- linux-2.6/security/selinux/include/objsec.h.utrace-ptrace-compat
+++ linux-2.6/security/selinux/include/objsec.h
@@ -34,7 +34,6 @@ struct task_security_struct {
u32 create_sid; /* fscreate SID */
u32 keycreate_sid; /* keycreate SID */
u32 sockcreate_sid; /* fscreate SID */
- u32 ptrace_sid; /* SID of ptrace parent */
};
struct inode_security_struct {
--- linux-2.6/security/selinux/hooks.c.utrace-ptrace-compat
+++ linux-2.6/security/selinux/hooks.c
@@ -21,7 +21,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/security.h>
@@ -159,7 +159,7 @@ static int task_alloc_security(struct ta
return -ENOMEM;
tsec->task = task;
- tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
+ tsec->osid = tsec->sid = SECINITSID_UNLABELED;
task->security = tsec;
return 0;
@@ -1387,19 +1387,13 @@ static int inode_security_set_sid(struct
static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
{
- struct task_security_struct *psec = parent->security;
- struct task_security_struct *csec = child->security;
int rc;
rc = secondary_ops->ptrace(parent,child);
if (rc)
return rc;
- rc = task_has_perm(parent, child, PROCESS__PTRACE);
- /* Save the SID of the tracing process for later use in apply_creds. */
- if (!(child->ptrace & PT_PTRACED) && !rc)
- csec->ptrace_sid = psec->sid;
- return rc;
+ return task_has_perm(parent, child, PROCESS__PTRACE);
}
static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
@@ -1821,12 +1815,24 @@ static void selinux_bprm_apply_creds(str
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and kill. */
if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
- rc = avc_has_perm(tsec->ptrace_sid, sid,
- SECCLASS_PROCESS, PROCESS__PTRACE,
- NULL);
- if (rc) {
- bsec->unsafe = 1;
- return;
+ struct task_struct *t;
+
+ rcu_read_lock();
+ t = tracehook_tracer_task(current);
+ if (unlikely(t == NULL))
+ rcu_read_unlock();
+ else {
+ struct task_security_struct *sec = t->security;
+ u32 ptsid = sec->sid;
+ rcu_read_unlock();
+
+ rc = avc_has_perm(ptsid, sid,
+ SECCLASS_PROCESS,
+ PROCESS__PTRACE, NULL);
+ if (rc) {
+ bsec->unsafe = 1;
+ return;
+ }
}
}
tsec->sid = sid;
@@ -2684,11 +2690,6 @@ static int selinux_task_alloc_security(s
tsec2->keycreate_sid = tsec1->keycreate_sid;
tsec2->sockcreate_sid = tsec1->sockcreate_sid;
- /* Retain ptracer SID across fork, if any.
- This will be reset by the ptrace hook upon any
- subsequent ptrace_attach operations. */
- tsec2->ptrace_sid = tsec1->ptrace_sid;
-
return 0;
}
@@ -4293,6 +4294,7 @@ static int selinux_setprocattr(struct ta
char *name, void *value, size_t size)
{
struct task_security_struct *tsec;
+ struct task_struct *tracer;
u32 sid = 0;
int error;
char *str = value;
@@ -4381,18 +4383,24 @@ static int selinux_setprocattr(struct ta
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and fail. */
task_lock(p);
- if (p->ptrace & PT_PTRACED) {
- error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
+ rcu_read_lock();
+ tracer = tracehook_tracer_task(p);
+ if (tracer != NULL) {
+ struct task_security_struct *ptsec = tracer->security;
+ u32 ptsid = ptsec->sid;
+ rcu_read_unlock();
+ error = avc_has_perm_noaudit(ptsid, sid,
SECCLASS_PROCESS,
PROCESS__PTRACE, &avd);
if (!error)
tsec->sid = sid;
task_unlock(p);
- avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
+ avc_audit(ptsid, sid, SECCLASS_PROCESS,
PROCESS__PTRACE, &avd, error, NULL);
if (error)
return error;
} else {
+ rcu_read_unlock();
tsec->sid = sid;
task_unlock(p);
}
--- linux-2.6/kernel/fork.c.utrace-ptrace-compat
+++ linux-2.6/kernel/fork.c
@@ -36,7 +36,7 @@
#include <linux/jiffies.h>
#include <linux/futex.h>
#include <linux/rcupdate.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/mount.h>
#include <linux/audit.h>
#include <linux/profile.h>
@@ -908,8 +908,7 @@ static inline void copy_flags(unsigned l
new_flags &= ~(PF_SUPERPRIV | PF_NOFREEZE);
new_flags |= PF_FORKNOEXEC;
- if (!(clone_flags & CLONE_PTRACE))
- p->ptrace = 0;
+ new_flags |= PF_STARTING;
p->flags = new_flags;
}
@@ -1018,6 +1017,9 @@ static struct task_struct *copy_process(
INIT_LIST_HEAD(&p->sibling);
p->vfork_done = NULL;
spin_lock_init(&p->alloc_lock);
+#ifdef CONFIG_PTRACE
+ INIT_LIST_HEAD(&p->ptracees);
+#endif
clear_tsk_thread_flag(p, TIF_SIGPENDING);
init_sigpending(&p->pending);
@@ -1153,8 +1155,6 @@ static struct task_struct *copy_process(
*/
p->group_leader = p;
INIT_LIST_HEAD(&p->thread_group);
- INIT_LIST_HEAD(&p->ptrace_children);
- INIT_LIST_HEAD(&p->ptrace_list);
/* Perform scheduler related setup. Assign this task to a CPU. */
sched_fork(p, clone_flags);
@@ -1178,10 +1178,9 @@ static struct task_struct *copy_process(
/* CLONE_PARENT re-uses the old parent */
if (clone_flags & (CLONE_PARENT|CLONE_THREAD))
- p->real_parent = current->real_parent;
+ p->parent = current->parent;
else
- p->real_parent = current;
- p->parent = p->real_parent;
+ p->parent = current;
spin_lock(¤t->sighand->siglock);
@@ -1228,8 +1227,7 @@ static struct task_struct *copy_process(
if (likely(p->pid)) {
add_parent(p);
- if (unlikely(p->ptrace & PT_PTRACED))
- __ptrace_link(p, current->parent);
+ tracehook_init_task(p);
if (thread_group_leader(p)) {
p->signal->tty = current->signal->tty;
@@ -1313,22 +1311,6 @@ struct task_struct * __devinit fork_idle
return task;
}
-static inline int fork_traceflag (unsigned clone_flags)
-{
- if (clone_flags & CLONE_UNTRACED)
- return 0;
- else if (clone_flags & CLONE_VFORK) {
- if (current->ptrace & PT_TRACE_VFORK)
- return PTRACE_EVENT_VFORK;
- } else if ((clone_flags & CSIGNAL) != SIGCHLD) {
- if (current->ptrace & PT_TRACE_CLONE)
- return PTRACE_EVENT_CLONE;
- } else if (current->ptrace & PT_TRACE_FORK)
- return PTRACE_EVENT_FORK;
-
- return 0;
-}
-
/*
* Ok, this is the main fork-routine.
*
@@ -1343,18 +1325,12 @@ long do_fork(unsigned long clone_flags,
int __user *child_tidptr)
{
struct task_struct *p;
- int trace = 0;
struct pid *pid = alloc_pid();
long nr;
if (!pid)
return -EAGAIN;
nr = pid->nr;
- if (unlikely(current->ptrace)) {
- trace = fork_traceflag (clone_flags);
- if (trace)
- clone_flags |= CLONE_PTRACE;
- }
p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, nr);
/*
@@ -1369,30 +1345,26 @@ long do_fork(unsigned long clone_flags,
init_completion(&vfork);
}
- if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {
+ tracehook_report_clone(clone_flags, p);
+
+ p->flags &= ~PF_STARTING;
+
+ if (clone_flags & CLONE_STOPPED) {
/*
* We'll start up with an immediate SIGSTOP.
*/
sigaddset(&p->pending.signal, SIGSTOP);
set_tsk_thread_flag(p, TIF_SIGPENDING);
+ p->state = TASK_STOPPED;
}
-
- if (!(clone_flags & CLONE_STOPPED))
- wake_up_new_task(p, clone_flags);
else
- p->state = TASK_STOPPED;
+ wake_up_new_task(p, clone_flags);
- if (unlikely (trace)) {
- current->ptrace_message = nr;
- ptrace_notify ((trace << 8) | SIGTRAP);
- }
+ tracehook_report_clone_complete(clone_flags, nr, p);
if (clone_flags & CLONE_VFORK) {
wait_for_completion(&vfork);
- if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) {
- current->ptrace_message = nr;
- ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
- }
+ tracehook_report_vfork_done(p, nr);
}
} else {
free_pid(pid);
--- linux-2.6/kernel/signal.c.utrace-ptrace-compat
+++ linux-2.6/kernel/signal.c
@@ -20,7 +20,7 @@
#include <linux/binfmts.h>
#include <linux/security.h>
#include <linux/syscalls.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/signal.h>
#include <linux/capability.h>
#include <asm/param.h>
@@ -160,12 +160,6 @@ static int sig_ignored(struct task_struc
void __user * handler;
/*
- * Tracers always want to know about signals..
- */
- if (t->ptrace & PT_PTRACED)
- return 0;
-
- /*
* Blocked signals are never ignored, since the
* signal handler may change by the time it is
* unblocked.
@@ -175,8 +169,12 @@ static int sig_ignored(struct task_struc
/* Is it explicitly or implicitly ignored? */
handler = t->sighand->action[sig-1].sa.sa_handler;
- return handler == SIG_IGN ||
- (handler == SIG_DFL && sig_kernel_ignore(sig));
+ if (handler != SIG_IGN &&
+ (handler != SIG_DFL || !sig_kernel_ignore(sig)))
+ return 0;
+
+ /* It's ignored, we can short-circuit unless a debugger wants it. */
+ return !tracehook_consider_ignored_signal(t, sig, handler);
}
/*
@@ -216,7 +214,8 @@ fastcall void recalc_sigpending_tsk(stru
if (t->signal->group_stop_count > 0 ||
(freezing(t)) ||
PENDING(&t->pending, &t->blocked) ||
- PENDING(&t->signal->shared_pending, &t->blocked))
+ PENDING(&t->signal->shared_pending, &t->blocked) ||
+ tracehook_induce_sigpending(t))
set_tsk_thread_flag(t, TIF_SIGPENDING);
else
clear_tsk_thread_flag(t, TIF_SIGPENDING);
@@ -589,8 +588,6 @@ static int check_kill_permission(int sig
return error;
}
-/* forward decl */
-static void do_notify_parent_cldstop(struct task_struct *tsk, int why);
/*
* Handle magic process-wide effects of stop/continue signals.
@@ -896,7 +893,7 @@ __group_complete_signal(int sig, struct
*/
if (sig_fatal(p, sig) && !(p->signal->flags & SIGNAL_GROUP_EXIT) &&
!sigismember(&t->real_blocked, sig) &&
- (sig == SIGKILL || !(t->ptrace & PT_PTRACED))) {
+ (sig == SIGKILL || !tracehook_consider_fatal_signal(t, sig))) {
/*
* This signal will be fatal to the whole group.
*/
@@ -1438,8 +1435,7 @@ void do_notify_parent(struct task_struct
/* do_notify_parent_cldstop should have been called instead. */
BUG_ON(tsk->state & (TASK_STOPPED|TASK_TRACED));
- BUG_ON(!tsk->ptrace &&
- (tsk->group_leader != tsk || !thread_group_empty(tsk)));
+ BUG_ON(tsk->group_leader != tsk || !thread_group_empty(tsk));
info.si_signo = sig;
info.si_errno = 0;
@@ -1464,7 +1460,7 @@ void do_notify_parent(struct task_struct
psig = tsk->parent->sighand;
spin_lock_irqsave(&psig->siglock, flags);
- if (!tsk->ptrace && sig == SIGCHLD &&
+ if (sig == SIGCHLD &&
(psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN ||
(psig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT))) {
/*
@@ -1492,20 +1488,13 @@ void do_notify_parent(struct task_struct
spin_unlock_irqrestore(&psig->siglock, flags);
}
-static void do_notify_parent_cldstop(struct task_struct *tsk, int why)
+void do_notify_parent_cldstop(struct task_struct *tsk, int why)
{
struct siginfo info;
unsigned long flags;
struct task_struct *parent;
struct sighand_struct *sighand;
- if (tsk->ptrace & PT_PTRACED)
- parent = tsk->parent;
- else {
- tsk = tsk->group_leader;
- parent = tsk->real_parent;
- }
-
info.si_signo = SIGCHLD;
info.si_errno = 0;
info.si_pid = tsk->pid;
@@ -1530,6 +1519,15 @@ static void do_notify_parent_cldstop(str
BUG();
}
+ /*
+ * Tracing can decide that we should not do the normal notification.
+ */
+ if (tracehook_notify_cldstop(tsk, &info))
+ return;
+
+ tsk = tsk->group_leader;
+ parent = tsk->parent;
+
sighand = parent->sighand;
spin_lock_irqsave(&sighand->siglock, flags);
if (sighand->action[SIGCHLD-1].sa.sa_handler != SIG_IGN &&
@@ -1542,110 +1540,6 @@ static void do_notify_parent_cldstop(str
spin_unlock_irqrestore(&sighand->siglock, flags);
}
-static inline int may_ptrace_stop(void)
-{
- if (!likely(current->ptrace & PT_PTRACED))
- return 0;
-
- if (unlikely(current->parent == current->real_parent &&
- (current->ptrace & PT_ATTACHED)))
- return 0;
-
- if (unlikely(current->signal == current->parent->signal) &&
- unlikely(current->signal->flags & SIGNAL_GROUP_EXIT))
- return 0;
-
- /*
- * Are we in the middle of do_coredump?
- * If so and our tracer is also part of the coredump stopping
- * is a deadlock situation, and pointless because our tracer
- * is dead so don't allow us to stop.
- * If SIGKILL was already sent before the caller unlocked
- * ->siglock we must see ->core_waiters != 0. Otherwise it
- * is safe to enter schedule().
- */
- if (unlikely(current->mm->core_waiters) &&
- unlikely(current->mm == current->parent->mm))
- return 0;
-
- return 1;
-}
-
-/*
- * This must be called with current->sighand->siglock held.
- *
- * This should be the path for all ptrace stops.
- * We always set current->last_siginfo while stopped here.
- * That makes it a way to test a stopped process for
- * being ptrace-stopped vs being job-control-stopped.
- *
- * If we actually decide not to stop at all because the tracer is gone,
- * we leave nostop_code in current->exit_code.
- */
-static void ptrace_stop(int exit_code, int nostop_code, siginfo_t *info)
-{
- /*
- * If there is a group stop in progress,
- * we must participate in the bookkeeping.
- */
- if (current->signal->group_stop_count > 0)
- --current->signal->group_stop_count;
-
- current->last_siginfo = info;
- current->exit_code = exit_code;
-
- /* Let the debugger run. */
- set_current_state(TASK_TRACED);
- spin_unlock_irq(¤t->sighand->siglock);
- try_to_freeze();
- read_lock(&tasklist_lock);
- if (may_ptrace_stop()) {
- do_notify_parent_cldstop(current, CLD_TRAPPED);
- read_unlock(&tasklist_lock);
- schedule();
- } else {
- /*
- * By the time we got the lock, our tracer went away.
- * Don't stop here.
- */
- read_unlock(&tasklist_lock);
- set_current_state(TASK_RUNNING);
- current->exit_code = nostop_code;
- }
-
- /*
- * We are back. Now reacquire the siglock before touching
- * last_siginfo, so that we are sure to have synchronized with
- * any signal-sending on another CPU that wants to examine it.
- */
- spin_lock_irq(¤t->sighand->siglock);
- current->last_siginfo = NULL;
-
- /*
- * Queued signals ignored us while we were stopped for tracing.
- * So check for any that we should take before resuming user mode.
- */
- recalc_sigpending();
-}
-
-void ptrace_notify(int exit_code)
-{
- siginfo_t info;
-
- BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP);
-
- memset(&info, 0, sizeof info);
- info.si_signo = SIGTRAP;
- info.si_code = exit_code;
- info.si_pid = current->pid;
- info.si_uid = current->uid;
-
- /* Let the debugger run. */
- spin_lock_irq(¤t->sighand->siglock);
- ptrace_stop(exit_code, 0, &info);
- spin_unlock_irq(¤t->sighand->siglock);
-}
-
static void
finish_stop(int stop_count)
{
@@ -1654,7 +1548,7 @@ finish_stop(int stop_count)
* a group stop in progress and we are the last to stop,
* report to the parent. When ptraced, every thread reports itself.
*/
- if (stop_count == 0 || (current->ptrace & PT_PTRACED)) {
+ if (!tracehook_finish_stop(stop_count <= 0) && stop_count <= 0) {
read_lock(&tasklist_lock);
do_notify_parent_cldstop(current, CLD_STOPPED);
read_unlock(&tasklist_lock);
@@ -1779,44 +1673,24 @@ relock:
handle_group_stop())
goto relock;
- signr = dequeue_signal(current, mask, info);
-
- if (!signr)
- break; /* will return 0 */
-
- if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
- ptrace_signal_deliver(regs, cookie);
-
- /* Let the debugger run. */
- ptrace_stop(signr, signr, info);
-
- /* We're back. Did the debugger cancel the sig? */
- signr = current->exit_code;
- if (signr == 0)
- continue;
-
- current->exit_code = 0;
-
- /* 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->parent->pid;
- info->si_uid = current->parent->uid;
- }
-
- /* If the (new) signal is now blocked, requeue it. */
- if (sigismember(¤t->blocked, signr)) {
- specific_send_sig_info(signr, info, current);
- continue;
- }
+ /*
+ * Tracing can induce an artifical signal and choose sigaction.
+ * The return value in signr determines the default action,
+ * but info->si_signo is the signal number we will report.
+ */
+ signr = tracehook_get_signal(current, regs, info, return_ka);
+ if (unlikely(signr < 0))
+ goto relock;
+ if (unlikely(signr != 0))
+ ka = return_ka;
+ else {
+ signr = dequeue_signal(current, mask, info);
+
+ if (!signr)
+ break; /* will return 0 */
+ ka = ¤t->sighand->action[signr-1];
}
- ka = ¤t->sighand->action[signr-1];
if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */
continue;
if (ka->sa.sa_handler != SIG_DFL) {
@@ -1861,7 +1735,7 @@ relock:
spin_lock_irq(¤t->sighand->siglock);
}
- if (likely(do_signal_stop(signr))) {
+ if (likely(do_signal_stop(info->si_signo))) {
/* It released the siglock. */
goto relock;
}
@@ -1888,13 +1762,13 @@ relock:
* first and our do_group_exit call below will use
* that value and ignore the one we pass it.
*/
- do_coredump((long)signr, signr, regs);
+ do_coredump(info->si_signo, info->si_signo, regs);
}
/*
* Death signals, no core dump.
*/
- do_group_exit(signr);
+ do_group_exit(info->si_signo);
/* NOTREACHED */
}
spin_unlock_irq(¤t->sighand->siglock);
@@ -1907,7 +1781,6 @@ EXPORT_SYMBOL(flush_signals);
EXPORT_SYMBOL(force_sig);
EXPORT_SYMBOL(kill_pg);
EXPORT_SYMBOL(kill_proc);
-EXPORT_SYMBOL(ptrace_notify);
EXPORT_SYMBOL(send_sig);
EXPORT_SYMBOL(send_sig_info);
EXPORT_SYMBOL(sigprocmask);
--- linux-2.6/kernel/utrace.c.utrace-ptrace-compat
+++ linux-2.6/kernel/utrace.c
@@ -0,0 +1,1590 @@
+#include <linux/utrace.h>
+#include <linux/tracehook.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/tracehook.h>
+
+
+static kmem_cache_t *utrace_cachep;
+static kmem_cache_t *utrace_engine_cachep;
+
+static int __init
+utrace_init(void)
+{
+ utrace_cachep =
+ kmem_cache_create("utrace_cache",
+ sizeof(struct utrace), 0,
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+ utrace_engine_cachep =
+ kmem_cache_create("utrace_engine_cache",
+ sizeof(struct utrace_attached_engine), 0,
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+ return 0;
+}
+subsys_initcall(utrace_init);
+
+
+/*
+ * Make sure target->utrace is allocated, and return with it locked on
+ * success. This function mediates startup races. The creating parent
+ * task has priority, and other callers will delay here to let its call
+ * succeed and take the new utrace lock first.
+ */
+static struct utrace *
+utrace_first_engine(struct task_struct *target,
+ struct utrace_attached_engine *engine)
+{
+ struct utrace *utrace, *ret;
+
+ /*
+ * If this is a newborn thread and we are not the creator,
+ * we have to wait for it. The creator gets the first chance
+ * to attach. The PF_STARTING flag is cleared after its
+ * report_clone hook has had a chance to run.
+ */
+ if ((target->flags & PF_STARTING)
+ && (current->utrace == NULL
+ || current->utrace->u.live.cloning != target)) {
+ yield();
+ return (signal_pending(current)
+ ? ERR_PTR(-ERESTARTNOINTR) : NULL);
+ }
+
+ utrace = kmem_cache_alloc(utrace_cachep, SLAB_KERNEL);
+ if (unlikely(utrace == NULL))
+ return ERR_PTR(-ENOMEM);
+
+ utrace->u.live.cloning = NULL;
+ utrace->u.live.signal = NULL;
+ INIT_LIST_HEAD(&utrace->engines);
+ list_add(&engine->entry, &utrace->engines);
+ spin_lock_init(&utrace->lock);
+
+ ret = utrace;
+ utrace_lock(utrace);
+ task_lock(target);
+ if (likely(target->utrace == NULL)) {
+ rcu_assign_pointer(target->utrace, utrace);
+ /*
+ * The task_lock protects us against another thread doing
+ * the same thing. We might still be racing against
+ * tracehook_release_task. It's called with ->exit_state
+ * set to EXIT_DEAD and then checks ->utrace with an
+ * smp_mb() in between. If EXIT_DEAD is set, then
+ * release_task might have checked ->utrace already and saw
+ * it NULL; we can't attach. If we see EXIT_DEAD not yet
+ * set after our barrier, then we know release_task will
+ * see our target->utrace pointer.
+ */
+ smp_mb();
+ if (target->exit_state == EXIT_DEAD) {
+ /*
+ * The target has already been through release_task.
+ */
+ target->utrace = NULL;
+ goto cannot_attach;
+ }
+ task_unlock(target);
+
+ /*
+ * If the thread is already dead when we attach, then its
+ * parent was notified already and we shouldn't repeat the
+ * notification later after a detach or NOREAP flag change.
+ */
+ if (target->exit_state)
+ utrace->u.exit.notified = 1;
+ }
+ else {
+ /*
+ * Another engine attached first, so there is a struct already.
+ * A null return says to restart looking for the existing one.
+ */
+ cannot_attach:
+ ret = NULL;
+ task_unlock(target);
+ utrace_unlock(utrace);
+ kmem_cache_free(utrace_cachep, utrace);
+ }
+
+ return ret;
+}
+
+static void
+utrace_free(struct rcu_head *rhead)
+{
+ struct utrace *utrace = container_of(rhead, struct utrace, u.dead);
+ kmem_cache_free(utrace_cachep, utrace);
+}
+
+static void
+rcu_utrace_free(struct utrace *utrace)
+{
+ INIT_RCU_HEAD(&utrace->u.dead);
+ call_rcu(&utrace->u.dead, utrace_free);
+}
+
+static void
+utrace_engine_free(struct rcu_head *rhead)
+{
+ struct utrace_attached_engine *engine =
+ container_of(rhead, struct utrace_attached_engine, rhead);
+ kmem_cache_free(utrace_engine_cachep, engine);
+}
+
+/*
+ * Called with utrace locked and the target quiescent (maybe current).
+ * If this was the last engine, utrace is left locked and not freed,
+ * but is removed from the task.
+ */
+static void
+remove_engine(struct utrace_attached_engine *engine,
+ struct task_struct *tsk, struct utrace *utrace)
+{
+ list_del_rcu(&engine->entry);
+ if (list_empty(&utrace->engines)) {
+ task_lock(tsk);
+ if (likely(tsk->utrace != NULL)) {
+ rcu_assign_pointer(tsk->utrace, NULL);
+ tsk->utrace_flags = 0;
+ }
+ task_unlock(tsk);
+ }
+ call_rcu(&engine->rhead, utrace_engine_free);
+}
+
+
+/*
+ * Called with utrace locked, after remove_engine may have run.
+ * Passed the flags from all remaining engines, i.e. zero if none left.
+ * Install the flags in tsk->utrace_flags and return with utrace unlocked.
+ * If no engines are left, utrace is freed and we return NULL.
+ */
+static struct utrace *
+check_dead_utrace(struct task_struct *tsk, struct utrace *utrace,
+ unsigned long flags)
+{
+ if (flags) {
+ tsk->utrace_flags = flags;
+ utrace_unlock(utrace);
+ return utrace;
+ }
+
+ utrace_unlock(utrace);
+ rcu_utrace_free(utrace);
+ return NULL;
+}
+
+
+
+/*
+ * Get the target thread to quiesce. Return nonzero if it's already quiescent.
+ * Return zero if it will report a QUIESCE event soon.
+ * If interrupt is nonzero, wake it like a signal would so it quiesces ASAP.
+ * If interrupt is zero, just make sure it quiesces before going to user mode.
+ */
+static int
+quiesce(struct task_struct *target, int interrupt)
+{
+ int quiescent;
+
+ target->utrace_flags |= UTRACE_ACTION_QUIESCE;
+ read_barrier_depends();
+
+ quiescent = (target->exit_state
+ || target->state & (TASK_TRACED | TASK_STOPPED));
+
+ if (!quiescent) {
+ spin_lock_irq(&target->sighand->siglock);
+ quiescent = (unlikely(target->exit_state)
+ || unlikely(target->state
+ & (TASK_TRACED | TASK_STOPPED)));
+ if (!quiescent) {
+ if (interrupt)
+ signal_wake_up(target, 0);
+ else {
+ set_tsk_thread_flag(target, TIF_SIGPENDING);
+ kick_process(target);
+ }
+ }
+ spin_unlock_irq(&target->sighand->siglock);
+ }
+
+ return quiescent;
+}
+
+
+static struct utrace_attached_engine *
+matching_engine(struct utrace *utrace, int flags,
+ const struct utrace_engine_ops *ops, unsigned long data)
+{
+ struct utrace_attached_engine *engine;
+ list_for_each_entry_rcu(engine, &utrace->engines, entry) {
+ if ((flags & UTRACE_ATTACH_MATCH_OPS)
+ && engine->ops != ops)
+ continue;
+ if ((flags & UTRACE_ATTACH_MATCH_DATA)
+ && engine->data != data)
+ continue;
+ if (flags & UTRACE_ATTACH_EXCLUSIVE)
+ engine = ERR_PTR(-EEXIST);
+ return engine;
+ }
+ return NULL;
+}
+
+/*
+ option to stop it?
+ option to match existing on ops, ops+data, return it; nocreate:lookup only
+ */
+struct utrace_attached_engine *
+utrace_attach(struct task_struct *target, int flags,
+ const struct utrace_engine_ops *ops, unsigned long data)
+{
+ struct utrace *utrace;
+ struct utrace_attached_engine *engine;
+
+restart:
+ rcu_read_lock();
+ utrace = rcu_dereference(target->utrace);
+ smp_rmb();
+ if (utrace == NULL) {
+ rcu_read_unlock();
+
+ if (!(flags & UTRACE_ATTACH_CREATE)) {
+ return ERR_PTR(-ENOENT);
+ }
+
+ engine = kmem_cache_alloc(utrace_engine_cachep, SLAB_KERNEL);
+ if (unlikely(engine == NULL))
+ return ERR_PTR(-ENOMEM);
+ engine->flags = 0;
+
+ first:
+ utrace = utrace_first_engine(target, engine);
+ if (IS_ERR(utrace)) {
+ kmem_cache_free(utrace_engine_cachep, engine);
+ return ERR_PTR(PTR_ERR(utrace));
+ }
+ if (unlikely(utrace == NULL)) /* Race condition. */
+ goto restart;
+ }
+ else if (unlikely(target->exit_state == EXIT_DEAD)) {
+ /*
+ * The target has already been reaped.
+ */
+ rcu_read_unlock();
+ return ERR_PTR(-ESRCH);
+ }
+ else {
+ if (!(flags & UTRACE_ATTACH_CREATE)) {
+ engine = matching_engine(utrace, flags, ops, data);
+ rcu_read_unlock();
+ return engine;
+ }
+
+ engine = kmem_cache_alloc(utrace_engine_cachep, SLAB_KERNEL);
+ if (unlikely(engine == NULL))
+ return ERR_PTR(-ENOMEM);
+ engine->flags = ops->report_reap ? UTRACE_EVENT(REAP) : 0;
+
+ rcu_read_lock();
+ utrace = rcu_dereference(target->utrace);
+ if (unlikely(utrace == NULL)) { /* Race with detach. */
+ rcu_read_unlock();
+ goto first;
+ }
+
+ utrace_lock(utrace);
+ if (flags & UTRACE_ATTACH_EXCLUSIVE) {
+ struct utrace_attached_engine *old;
+ old = matching_engine(utrace, flags, ops, data);
+ if (old != NULL) {
+ utrace_unlock(utrace);
+ rcu_read_unlock();
+ kmem_cache_free(utrace_engine_cachep, engine);
+ return ERR_PTR(-EEXIST);
+ }
+ }
+
+ if (unlikely(rcu_dereference(target->utrace) != utrace)) {
+ /*
+ * We lost a race with other CPUs doing a sequence
+ * of detach and attach before we got in.
+ */
+ utrace_unlock(utrace);
+ rcu_read_unlock();
+ kmem_cache_free(utrace_engine_cachep, engine);
+ goto restart;
+ }
+ list_add_tail_rcu(&engine->entry, &utrace->engines);
+ }
+
+ engine->ops = ops;
+ engine->data = data;
+
+ utrace_unlock(utrace);
+
+ return engine;
+}
+EXPORT_SYMBOL_GPL(utrace_attach);
+
+/*
+ * When an engine is detached, the target thread may still see it and make
+ * callbacks until it quiesces. We reset its event flags to just QUIESCE
+ * and install a special ops vector whose callback is dead_engine_delete.
+ * When the target thread quiesces, it can safely free the engine itself.
+ */
+static u32
+dead_engine_delete(struct utrace_attached_engine *engine,
+ struct task_struct *tsk)
+{
+ return UTRACE_ACTION_DETACH;
+}
+
+static const struct utrace_engine_ops dead_engine_ops =
+{
+ .report_quiesce = &dead_engine_delete
+};
+
+
+/*
+ * If tracing was preventing a SIGCHLD or self-reaping
+ * and is no longer, do that report or reaping right now.
+ */
+static void
+check_noreap(struct task_struct *target, struct utrace *utrace,
+ u32 old_action, u32 action)
+{
+ if ((action | ~old_action) & UTRACE_ACTION_NOREAP)
+ return;
+
+ if (utrace && xchg(&utrace->u.exit.notified, 1))
+ return;
+
+ if (target->exit_signal == -1)
+ release_task(target);
+ else if (thread_group_empty(target)) {
+ read_lock(&tasklist_lock);
+ do_notify_parent(target, target->exit_signal);
+ read_unlock(&tasklist_lock);
+ }
+}
+
+/*
+ * We may have been the one keeping the target thread quiescent.
+ * Check if it should wake up now.
+ * Called with utrace locked, and unlocks it on return.
+ * If we were keeping it stopped, resume it.
+ * If we were keeping its zombie from reporting/self-reap, do it now.
+ */
+static void
+wake_quiescent(unsigned long old_flags,
+ struct utrace *utrace, struct task_struct *target)
+{
+ unsigned long flags;
+ struct utrace_attached_engine *engine;
+
+ if (target->exit_state) {
+ /*
+ * Update the set of events of interest from the union
+ * of the interests of the remaining tracing engines.
+ */
+ flags = 0;
+ list_for_each_entry(engine, &utrace->engines, entry)
+ flags |= engine->flags | UTRACE_EVENT(REAP);
+ utrace = check_dead_utrace(target, utrace, flags);
+
+ check_noreap(target, utrace, old_flags, flags);
+ return;
+ }
+
+ /*
+ * Update the set of events of interest from the union
+ * of the interests of the remaining tracing engines.
+ */
+ flags = 0;
+ list_for_each_entry(engine, &utrace->engines, entry)
+ flags |= engine->flags | UTRACE_EVENT(REAP);
+ utrace = check_dead_utrace(target, utrace, flags);
+
+ if (flags & UTRACE_ACTION_QUIESCE)
+ return;
+
+ read_lock(&tasklist_lock);
+ if (!target->exit_state) {
+ /*
+ * The target is not dead and should not be in tracing stop
+ * any more. Wake it unless it's in job control stop.
+ */
+ spin_lock_irq(&target->sighand->siglock);
+ if (target->signal->flags & SIGNAL_STOP_STOPPED) {
+ int stop_count = target->signal->group_stop_count;
+ target->state = TASK_STOPPED;
+ spin_unlock_irq(&target->sighand->siglock);
+
+ /*
+ * If tracing was preventing a CLD_STOPPED report
+ * and is no longer, do that report right now.
+ */
+ if (stop_count == 0
+ && 0
+ /*&& (events &~ interest) & UTRACE_INHIBIT_CLDSTOP*/
+ )
+ do_notify_parent_cldstop(target, CLD_STOPPED);
+ }
+ else {
+ /*
+ * Wake the task up.
+ */
+ recalc_sigpending_tsk(target);
+ wake_up_state(target, TASK_STOPPED | TASK_TRACED);
+ spin_unlock_irq(&target->sighand->siglock);
+ }
+ }
+ read_unlock(&tasklist_lock);
+}
+
+void
+utrace_detach(struct task_struct *target,
+ struct utrace_attached_engine *engine)
+{
+ struct utrace *utrace;
+ unsigned long flags;
+
+ rcu_read_lock();
+ utrace = rcu_dereference(target->utrace);
+ smp_rmb();
+ if (unlikely(target->exit_state == EXIT_DEAD)) {
+ /*
+ * Called after utrace_release_task might have started.
+ * A call to this engine's report_reap callback might
+ * already be in progress or engine might even have been
+ * freed already.
+ */
+ rcu_read_unlock();
+ return;
+ }
+ utrace_lock(utrace);
+ rcu_read_unlock();
+
+ flags = engine->flags;
+ engine->flags = UTRACE_EVENT(QUIESCE) | UTRACE_ACTION_QUIESCE;
+ rcu_assign_pointer(engine->ops, &dead_engine_ops);
+
+ if (quiesce(target, 1)) {
+ remove_engine(engine, target, utrace);
+ wake_quiescent(flags, utrace, target);
+ }
+ else
+ utrace_unlock(utrace);
+}
+EXPORT_SYMBOL_GPL(utrace_detach);
+
+
+/*
+ * Called with utrace->lock held.
+ * Notify and clean up all engines, then free utrace.
+ */
+static void
+utrace_reap(struct task_struct *target, struct utrace *utrace)
+{
+ struct utrace_attached_engine *engine, *next;
+ const struct utrace_engine_ops *ops;
+
+restart:
+ list_for_each_entry_safe(engine, next, &utrace->engines, entry) {
+ list_del_rcu(&engine->entry);
+
+ /*
+ * Now nothing else refers to this engine.
+ */
+ if (engine->flags & UTRACE_EVENT(REAP)) {
+ ops = rcu_dereference(engine->ops);
+ if (ops != &dead_engine_ops) {
+ utrace_unlock(utrace);
+ (*ops->report_reap)(engine, target);
+ call_rcu(&engine->rhead, utrace_engine_free);
+ utrace_lock(utrace);
+ goto restart;
+ }
+ }
+ call_rcu(&engine->rhead, utrace_engine_free);
+ }
+ utrace_unlock(utrace);
+
+ rcu_utrace_free(utrace);
+}
+
+/*
+ * Called by release_task. After this, target->utrace must be cleared.
+ */
+void
+utrace_release_task(struct task_struct *target)
+{
+ struct utrace *utrace;
+
+ task_lock(target);
+ utrace = target->utrace;
+ rcu_assign_pointer(target->utrace, NULL);
+ task_unlock(target);
+
+ if (unlikely(utrace == NULL))
+ return;
+
+ utrace_lock(utrace);
+
+ if (!utrace->u.exit.notified
+ && (target->utrace_flags & (UTRACE_EVENT(DEATH)
+ | UTRACE_EVENT(QUIESCE)))) {
+ /*
+ * The target will do some final callbacks but hasn't
+ * finished them yet. We know because it clears these
+ * event bits after it's done. Instead of cleaning up here
+ * and requiring utrace_report_death to cope with it, we
+ * delay the REAP report and the teardown until after the
+ * target finishes its death reports.
+ */
+ utrace->u.exit.reap = 1;
+ utrace_unlock(utrace);
+ }
+ else
+ utrace_reap(target, utrace); /* Unlocks and frees. */
+}
+
+
+void
+utrace_set_flags(struct task_struct *target,
+ struct utrace_attached_engine *engine,
+ unsigned long flags)
+{
+ struct utrace *utrace;
+ int report = 0;
+ unsigned long old_flags, old_utrace_flags;
+
+#ifdef ARCH_HAS_SINGLE_STEP
+ if (! ARCH_HAS_SINGLE_STEP)
+#endif
+ WARN_ON(flags & UTRACE_ACTION_SINGLESTEP);
+#ifdef ARCH_HAS_BLOCK_STEP
+ if (! ARCH_HAS_BLOCK_STEP)
+#endif
+ WARN_ON(flags & UTRACE_ACTION_BLOCKSTEP);
+
+ rcu_read_lock();
+ utrace = rcu_dereference(target->utrace);
+ smp_rmb();
+ if (unlikely(target->exit_state == EXIT_DEAD)) {
+ /*
+ * Race with reaping.
+ */
+ rcu_read_unlock();
+ return;
+ }
+
+ utrace_lock(utrace);
+ rcu_read_unlock();
+
+ old_utrace_flags = target->utrace_flags;
+ old_flags = engine->flags;
+ engine->flags = flags;
+ target->utrace_flags |= flags;
+
+ if ((old_flags ^ flags) & UTRACE_ACTION_QUIESCE) {
+ if (flags & UTRACE_ACTION_QUIESCE) {
+ report = (quiesce(target, 1)
+ && (flags & UTRACE_EVENT(QUIESCE)));
+ utrace_unlock(utrace);
+ }
+ else
+ wake_quiescent(old_flags, utrace, target);
+ }
+ else {
+ /*
+ * If we're asking for single-stepping or syscall tracing,
+ * we need to pass through utrace_quiescent before resuming
+ * in user mode to get those effects, even if the target is
+ * not going to be quiescent right now.
+ */
+ if (!(target->utrace_flags & UTRACE_ACTION_QUIESCE)
+ && ((flags &~ old_utrace_flags)
+ & (UTRACE_ACTION_SINGLESTEP | UTRACE_ACTION_BLOCKSTEP
+ | UTRACE_EVENT_SYSCALL)))
+ quiesce(target, 0);
+ utrace_unlock(utrace);
+ }
+
+ if (report) /* Already quiescent, won't report itself. */
+ (*engine->ops->report_quiesce)(engine, target);
+}
+EXPORT_SYMBOL_GPL(utrace_set_flags);
+
+/*
+ * While running an engine callback, no locks are held.
+ * If a callback updates its engine's action state, then
+ * we need to take the utrace lock to install the flags update.
+ */
+static inline u32
+update_action(struct task_struct *tsk, struct utrace *utrace,
+ struct utrace_attached_engine *engine,
+ u32 ret)
+{
+ if (ret & UTRACE_ACTION_DETACH)
+ rcu_assign_pointer(engine->ops, &dead_engine_ops);
+ else if ((ret & UTRACE_ACTION_NEWSTATE)
+ && ((ret ^ engine->flags) & UTRACE_ACTION_STATE_MASK)) {
+#ifdef ARCH_HAS_SINGLE_STEP
+ if (! ARCH_HAS_SINGLE_STEP)
+#endif
+ WARN_ON(ret & UTRACE_ACTION_SINGLESTEP);
+#ifdef ARCH_HAS_BLOCK_STEP
+ if (! ARCH_HAS_BLOCK_STEP)
+#endif
+ WARN_ON(ret & UTRACE_ACTION_BLOCKSTEP);
+ utrace_lock(utrace);
+ /*
+ * If we're changing something other than just QUIESCE,
+ * make sure we pass through utrace_quiescent before
+ * resuming even if we aren't going to stay quiescent.
+ * That's where we get the correct union of all engines'
+ * flags after they've finished changing, and apply changes.
+ */
+ if (((ret ^ engine->flags) & (UTRACE_ACTION_STATE_MASK
+ & ~UTRACE_ACTION_QUIESCE)))
+ tsk->utrace_flags |= UTRACE_ACTION_QUIESCE;
+ engine->flags &= ~UTRACE_ACTION_STATE_MASK;
+ engine->flags |= ret & UTRACE_ACTION_STATE_MASK;
+ tsk->utrace_flags |= engine->flags;
+ utrace_unlock(utrace);
+ }
+ else
+ ret |= engine->flags & UTRACE_ACTION_STATE_MASK;
+ return ret;
+}
+
+#define REPORT(callback, ...) do { \
+ u32 ret = (*rcu_dereference(engine->ops)->callback) \
+ (engine, tsk, ##__VA_ARGS__); \
+ action = update_action(tsk, utrace, engine, ret); \
+ } while (0)
+
+
+/*
+ * Called with utrace->lock held, returns with it released.
+ */
+static u32
+remove_detached(struct task_struct *tsk, struct utrace *utrace,
+ struct utrace **utracep, u32 action)
+{
+ struct utrace_attached_engine *engine, *next;
+ unsigned long flags;
+
+ flags = 0;
+ list_for_each_entry_safe(engine, next, &utrace->engines, entry) {
+ if (engine->ops == &dead_engine_ops)
+ remove_engine(engine, tsk, utrace);
+ else
+ flags |= engine->flags | UTRACE_EVENT(REAP);
+ }
+ utrace = check_dead_utrace(tsk, utrace, flags);
+ if (utracep)
+ *utracep = utrace;
+
+ flags &= UTRACE_ACTION_STATE_MASK;
+ return flags | (action & UTRACE_ACTION_OP_MASK);
+}
+
+/*
+ * Called after an event report loop. Remove any engines marked for detach.
+ */
+static inline u32
+check_detach(struct task_struct *tsk, u32 action)
+{
+ if (action & UTRACE_ACTION_DETACH) {
+ utrace_lock(tsk->utrace);
+ action = remove_detached(tsk, tsk->utrace, NULL, action);
+ }
+ return action;
+}
+
+static inline void
+check_quiescent(struct task_struct *tsk, u32 action)
+{
+ if (action & UTRACE_ACTION_STATE_MASK)
+ utrace_quiescent(tsk);
+}
+
+/*
+ * Called iff UTRACE_EVENT(CLONE) flag is set.
+ * This notification call blocks the wake_up_new_task call on the child.
+ * So we must not quiesce here. tracehook_report_clone_complete will do
+ * a quiescence check momentarily.
+ */
+void
+utrace_report_clone(unsigned long clone_flags, struct task_struct *child)
+{
+ struct task_struct *tsk = current;
+ struct utrace *utrace = tsk->utrace;
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+ unsigned long action;
+
+ utrace->u.live.cloning = child;
+
+ /* XXX must change for sharing */
+ action = UTRACE_ACTION_RESUME;
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine, entry);
+ if (engine->flags & UTRACE_EVENT(CLONE))
+ REPORT(report_clone, clone_flags, child);
+ if (action & UTRACE_ACTION_HIDE)
+ break;
+ }
+
+ utrace->u.live.cloning = NULL;
+
+ check_detach(tsk, action);
+}
+
+static unsigned long
+report_quiescent(struct task_struct *tsk, struct utrace *utrace, u32 action)
+{
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine, entry);
+ if (engine->flags & UTRACE_EVENT(QUIESCE))
+ REPORT(report_quiesce);
+ action |= engine->flags & UTRACE_ACTION_STATE_MASK;
+ }
+
+ return check_detach(tsk, action);
+}
+
+/*
+ * Called iff UTRACE_EVENT(JCTL) flag is set.
+ */
+int
+utrace_report_jctl(int what)
+{
+ struct task_struct *tsk = current;
+ struct utrace *utrace = tsk->utrace;
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+ unsigned long action;
+
+ /* XXX must change for sharing */
+ action = UTRACE_ACTION_RESUME;
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine, entry);
+ if (engine->flags & UTRACE_EVENT(JCTL))
+ REPORT(report_jctl, what);
+ if (action & UTRACE_ACTION_HIDE)
+ break;
+ }
+
+ /*
+ * We are becoming quiescent, so report it now.
+ * We don't block in utrace_quiescent because we are stopping anyway.
+ * We know that upon resuming we'll go through tracehook_induce_signal,
+ * which will keep us quiescent or set us up to resume with tracing.
+ */
+ action = report_quiescent(tsk, utrace, action);
+
+ if (what == CLD_STOPPED && tsk->state != TASK_STOPPED) {
+ /*
+ * The event report hooks could have blocked, though
+ * it should have been briefly. Make sure we're in
+ * TASK_STOPPED state again to block properly, unless
+ * we've just come back out of job control stop.
+ */
+ spin_lock_irq(&tsk->sighand->siglock);
+ if (tsk->signal->flags & SIGNAL_STOP_STOPPED)
+ set_current_state(TASK_STOPPED);
+ spin_unlock_irq(&tsk->sighand->siglock);
+ }
+
+ return action & UTRACE_JCTL_NOSIGCHLD;
+}
+
+
+/*
+ * Called if UTRACE_EVENT(QUIESCE) or UTRACE_ACTION_QUIESCE flag is set.
+ * Also called after other event reports.
+ * It is a good time to block.
+ */
+void
+utrace_quiescent(struct task_struct *tsk)
+{
+ struct utrace *utrace = tsk->utrace;
+ unsigned long action;
+
+restart:
+ /* XXX must change for sharing */
+
+ action = report_quiescent(tsk, utrace, UTRACE_ACTION_RESUME);
+
+ /*
+ * If some engines want us quiescent, we block here.
+ */
+ if (action & UTRACE_ACTION_QUIESCE) {
+ spin_lock_irq(&tsk->sighand->siglock);
+ /*
+ * If wake_quiescent is trying to wake us up now, it will
+ * have cleared the QUIESCE flag before trying to take the
+ * siglock. Now we have the siglock, so either it has
+ * already cleared the flag, or it will wake us up after we
+ * release the siglock it's waiting for.
+ * Never stop when there is a SIGKILL bringing us down.
+ */
+ if ((tsk->utrace_flags & UTRACE_ACTION_QUIESCE)
+ /*&& !(tsk->signal->flags & SIGNAL_GROUP_SIGKILL)*/) {
+ set_current_state(TASK_TRACED);
+ /*
+ * If there is a group stop in progress,
+ * we must participate in the bookkeeping.
+ */
+ if (tsk->signal->group_stop_count > 0)
+ --tsk->signal->group_stop_count;
+ spin_unlock_irq(&tsk->sighand->siglock);
+ schedule();
+ }
+ else
+ spin_unlock_irq(&tsk->sighand->siglock);
+
+ /*
+ * We've woken up. One engine could be waking us up while
+ * another has asked us to quiesce. So check afresh. We
+ * could have been detached while quiescent. Now we are no
+ * longer quiescent, so don't need to do any RCU locking.
+ * But we do need to check our utrace pointer anew.
+ */
+ utrace = tsk->utrace;
+ if (tsk->utrace_flags
+ & (UTRACE_EVENT(QUIESCE) | UTRACE_ACTION_STATE_MASK))
+ goto restart;
+ }
+ else if (tsk->utrace_flags & UTRACE_ACTION_QUIESCE) {
+ /*
+ * Our flags are out of date.
+ * Update the set of events of interest from the union
+ * of the interests of the remaining tracing engines.
+ */
+ struct utrace_attached_engine *engine;
+ unsigned long flags = 0;
+ utrace = rcu_dereference(tsk->utrace);
+ utrace_lock(utrace);
+ list_for_each_entry(engine, &utrace->engines, entry)
+ flags |= engine->flags | UTRACE_EVENT(REAP);
+ tsk->utrace_flags = flags;
+ utrace_unlock(utrace);
+ }
+
+ /*
+ * We're resuming. Update the machine layer tracing state and then go.
+ */
+#ifdef ARCH_HAS_SINGLE_STEP
+ if (action & UTRACE_ACTION_SINGLESTEP)
+ tracehook_enable_single_step(tsk);
+ else
+ tracehook_disable_single_step(tsk);
+#endif
+#ifdef ARCH_HAS_BLOCK_STEP
+ if ((action & (UTRACE_ACTION_BLOCKSTEP|UTRACE_ACTION_SINGLESTEP))
+ == UTRACE_ACTION_BLOCKSTEP)
+ tracehook_enable_block_step(tsk);
+ else
+ tracehook_disable_block_step(tsk);
+#endif
+ if (tsk->utrace_flags & UTRACE_EVENT_SYSCALL)
+ tracehook_enable_syscall_trace(tsk);
+ else
+ tracehook_disable_syscall_trace(tsk);
+}
+
+
+/*
+ * Called iff UTRACE_EVENT(EXIT) flag is set.
+ */
+void
+utrace_report_exit(long *exit_code)
+{
+ struct task_struct *tsk = current;
+ struct utrace *utrace = tsk->utrace;
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+ unsigned long action;
+ long orig_code = *exit_code;
+
+ /* XXX must change for sharing */
+ action = UTRACE_ACTION_RESUME;
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine, entry);
+ if (engine->flags & UTRACE_EVENT(EXIT))
+ REPORT(report_exit, orig_code, exit_code);
+ }
+ action = check_detach(tsk, action);
+ check_quiescent(tsk, action);
+}
+
+/*
+ * Called iff UTRACE_EVENT(DEATH) flag is set.
+ *
+ * It is always possible that we are racing with utrace_release_task here,
+ * if UTRACE_ACTION_NOREAP is not set, or in the case of non-leader exec
+ * where the old leader will get released regardless of NOREAP. For this
+ * reason, utrace_release_task checks for the event bits that get us here,
+ * and delays its cleanup for us to do.
+ */
+void
+utrace_report_death(struct task_struct *tsk, struct utrace *utrace)
+{
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+ u32 action, oaction;
+
+ BUG_ON(!tsk->exit_state);
+
+ oaction = tsk->utrace_flags;
+
+ /* XXX must change for sharing */
+ action = UTRACE_ACTION_RESUME;
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine, entry);
+ if (engine->flags & UTRACE_EVENT(DEATH))
+ REPORT(report_death);
+ if (engine->flags & UTRACE_EVENT(QUIESCE))
+ REPORT(report_quiesce);
+ }
+ /*
+ * Unconditionally lock and recompute the flags.
+ * This may notice that there are no engines left and
+ * free the utrace struct.
+ */
+ utrace_lock(utrace);
+ if (utrace->u.exit.reap) {
+ /*
+ * utrace_release_task was already called in parallel.
+ * We must complete its work now.
+ */
+ reap:
+ utrace_reap(tsk, utrace);
+ }
+ else {
+ action = remove_detached(tsk, utrace, &utrace, action);
+
+ if (utrace != NULL) {
+ utrace_lock(utrace);
+ if (utrace->u.exit.reap)
+ goto reap;
+
+ /*
+ * Clear event bits we can't see any more. This
+ * tells utrace_release_task we have already
+ * finished, if it comes along later.
+ */
+ tsk->utrace_flags &= (UTRACE_EVENT(REAP)
+ | UTRACE_ACTION_NOREAP);
+
+ utrace_unlock(utrace);
+ }
+
+ check_noreap(tsk, utrace, oaction, action);
+ }
+}
+
+/*
+ * Called iff UTRACE_EVENT(VFORK_DONE) flag is set.
+ */
+void
+utrace_report_vfork_done(pid_t child_pid)
+{
+ struct task_struct *tsk = current;
+ struct utrace *utrace = tsk->utrace;
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+ unsigned long action;
+
+ /* XXX must change for sharing */
+ action = UTRACE_ACTION_RESUME;
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine, entry);
+ if (engine->flags & UTRACE_EVENT(VFORK_DONE))
+ REPORT(report_vfork_done, child_pid);
+ if (action & UTRACE_ACTION_HIDE)
+ break;
+ }
+ action = check_detach(tsk, action);
+ check_quiescent(tsk, action);
+}
+
+/*
+ * Called iff UTRACE_EVENT(EXEC) flag is set.
+ */
+void
+utrace_report_exec(struct linux_binprm *bprm, struct pt_regs *regs)
+{
+ struct task_struct *tsk = current;
+ struct utrace *utrace = tsk->utrace;
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+ unsigned long action;
+
+ /* XXX must change for sharing */
+ action = UTRACE_ACTION_RESUME;
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine, entry);
+ if (engine->flags & UTRACE_EVENT(EXEC))
+ REPORT(report_exec, bprm, regs);
+ if (action & UTRACE_ACTION_HIDE)
+ break;
+ }
+ action = check_detach(tsk, action);
+ check_quiescent(tsk, action);
+}
+
+/*
+ * Called iff UTRACE_EVENT(SYSCALL_{ENTRY,EXIT}) flag is set.
+ */
+void
+utrace_report_syscall(struct pt_regs *regs, int is_exit)
+{
+ struct task_struct *tsk = current;
+ struct utrace *utrace = tsk->utrace;
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+ unsigned long action, ev;
+
+/*
+ XXX pass syscall # to engine hook directly, let it return inhibit-action
+ to reset to -1
+ long syscall = tracehook_syscall_number(regs, is_exit);
+*/
+
+ ev = is_exit ? UTRACE_EVENT(SYSCALL_EXIT) : UTRACE_EVENT(SYSCALL_ENTRY);
+
+ /* XXX must change for sharing */
+ action = UTRACE_ACTION_RESUME;
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine, entry);
+ if (engine->flags & ev) {
+ if (is_exit)
+ REPORT(report_syscall_exit, regs);
+ else
+ REPORT(report_syscall_entry, regs);
+ }
+ if (action & UTRACE_ACTION_HIDE)
+ break;
+ }
+ action = check_detach(tsk, action);
+ check_quiescent(tsk, action);
+}
+
+
+/*
+ * This is pointed to by the utrace struct, but it's really a private
+ * structure between utrace_get_signal and utrace_inject_signal.
+ */
+struct utrace_signal
+{
+ siginfo_t *const info;
+ struct k_sigaction *return_ka;
+ int signr;
+};
+
+
+// XXX copied from signal.c
+#ifdef SIGEMT
+#define M_SIGEMT M(SIGEMT)
+#else
+#define M_SIGEMT 0
+#endif
+
+#if SIGRTMIN > BITS_PER_LONG
+#define M(sig) (1ULL << ((sig)-1))
+#else
+#define M(sig) (1UL << ((sig)-1))
+#endif
+#define T(sig, mask) (M(sig) & (mask))
+
+#define SIG_KERNEL_ONLY_MASK (\
+ M(SIGKILL) | M(SIGSTOP) )
+
+#define SIG_KERNEL_STOP_MASK (\
+ M(SIGSTOP) | M(SIGTSTP) | M(SIGTTIN) | M(SIGTTOU) )
+
+#define SIG_KERNEL_COREDUMP_MASK (\
+ M(SIGQUIT) | M(SIGILL) | M(SIGTRAP) | M(SIGABRT) | \
+ M(SIGFPE) | M(SIGSEGV) | M(SIGBUS) | M(SIGSYS) | \
+ M(SIGXCPU) | M(SIGXFSZ) | M_SIGEMT )
+
+#define SIG_KERNEL_IGNORE_MASK (\
+ M(SIGCONT) | M(SIGCHLD) | M(SIGWINCH) | M(SIGURG) )
+
+#define sig_kernel_only(sig) \
+ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_ONLY_MASK))
+#define sig_kernel_coredump(sig) \
+ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_COREDUMP_MASK))
+#define sig_kernel_ignore(sig) \
+ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_IGNORE_MASK))
+#define sig_kernel_stop(sig) \
+ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_STOP_MASK))
+
+
+/*
+ * Call each interested tracing engine's report_signal callback.
+ */
+static u32
+report_signal(struct task_struct *tsk, struct pt_regs *regs,
+ struct utrace *utrace, u32 action,
+ unsigned long flags1, unsigned long flags2, siginfo_t *info,
+ const struct k_sigaction *ka, struct k_sigaction *return_ka)
+{
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+
+ /* XXX must change for sharing */
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine, entry);
+ if ((engine->flags & flags1) && (engine->flags & flags2)) {
+ u32 disp = action & UTRACE_ACTION_OP_MASK;
+ action &= ~UTRACE_ACTION_OP_MASK;
+ REPORT(report_signal, regs, disp, info, ka, return_ka);
+ if ((action & UTRACE_ACTION_OP_MASK) == 0)
+ action |= disp;
+ if (action & UTRACE_ACTION_HIDE)
+ break;
+ }
+ }
+
+ return action;
+}
+
+void
+utrace_signal_handler_singlestep(struct task_struct *tsk, struct pt_regs *regs)
+{
+ u32 action;
+ action = report_signal(tsk, regs, tsk->utrace, UTRACE_SIGNAL_HANDLER,
+ UTRACE_EVENT_SIGNAL_ALL,
+ UTRACE_ACTION_SINGLESTEP|UTRACE_ACTION_BLOCKSTEP,
+ NULL, NULL, NULL);
+ action = check_detach(tsk, action);
+ check_quiescent(tsk, action);
+}
+
+
+/*
+ * This is the hook from the signals code, called with the siglock held.
+ * Here is the ideal place to quiesce. We also dequeue and intercept signals.
+ */
+int
+utrace_get_signal(struct task_struct *tsk, struct pt_regs *regs,
+ siginfo_t *info, struct k_sigaction *return_ka)
+{
+ struct utrace *utrace = tsk->utrace;
+ struct utrace_signal signal = { info, return_ka, 0 };
+ struct k_sigaction *ka;
+ unsigned long action, event;
+
+#if 0 /* XXX */
+ if (tsk->signal->flags & SIGNAL_GROUP_SIGKILL)
+ return 0;
+#endif
+
+ /*
+ * If we should quiesce, now is the time.
+ * First stash a pointer to the state on our stack,
+ * so that utrace_inject_signal can tell us what to do.
+ */
+ if (utrace->u.live.signal == NULL)
+ utrace->u.live.signal = &signal;
+
+ if (tsk->utrace_flags & UTRACE_ACTION_QUIESCE) {
+ spin_unlock_irq(&tsk->sighand->siglock);
+ utrace_quiescent(tsk);
+ if (utrace->u.live.signal != &signal || signal.signr == 0)
+ /*
+ * This return value says to reacquire the siglock
+ * and check again. This will check for a pending
+ * group stop and process it before coming back here.
+ */
+ return -1;
+ spin_lock_irq(&tsk->sighand->siglock);
+ }
+
+ /*
+ * If a signal was injected previously, it could not use our
+ * stack space directly. It had to allocate a data structure,
+ * which we can now copy out of and free.
+ */
+ if (utrace->u.live.signal != &signal) {
+ signal.signr = utrace->u.live.signal->signr;
+ copy_siginfo(info, utrace->u.live.signal->info);
+ if (utrace->u.live.signal->return_ka)
+ *return_ka = *utrace->u.live.signal->return_ka;
+ else
+ signal.return_ka = NULL;
+ kfree(utrace->u.live.signal);
+ }
+ utrace->u.live.signal = NULL;
+
+ /*
+ * If a signal was injected, everything is in place now. Go do it.
+ */
+ if (signal.signr != 0) {
+ if (signal.return_ka == NULL) {
+ ka = &tsk->sighand->action[signal.signr - 1];
+ if (ka->sa.sa_flags & SA_ONESHOT)
+ ka->sa.sa_handler = SIG_DFL;
+ *return_ka = *ka;
+ }
+ else
+ BUG_ON(signal.return_ka != return_ka);
+ return signal.signr;
+ }
+
+ /*
+ * If noone is interested in intercepting signals, let the caller
+ * just dequeue them normally.
+ */
+ if ((tsk->utrace_flags & UTRACE_EVENT_SIGNAL_ALL) == 0)
+ return 0;
+
+ /*
+ * Steal the next signal so we can let tracing engines examine it.
+ * From the signal number and sigaction, determine what normal
+ * delivery would do. If no engine perturbs it, we'll do that
+ * by returning the signal number after setting *return_ka.
+ */
+ signal.signr = dequeue_signal(tsk, &tsk->blocked, info);
+ if (signal.signr == 0)
+ return 0;
+
+ BUG_ON(signal.signr != info->si_signo);
+
+ ka = &tsk->sighand->action[signal.signr - 1];
+ *return_ka = *ka;
+
+ if (ka->sa.sa_handler == SIG_IGN) {
+ event = UTRACE_EVENT(SIGNAL_IGN);
+ action = UTRACE_SIGNAL_IGN;
+ }
+ else if (ka->sa.sa_handler != SIG_DFL) {
+ event = UTRACE_EVENT(SIGNAL);
+ action = UTRACE_ACTION_RESUME;
+ }
+ else if (sig_kernel_coredump(signal.signr)) {
+ event = UTRACE_EVENT(SIGNAL_CORE);
+ action = UTRACE_SIGNAL_CORE;
+ }
+ else if (sig_kernel_ignore(signal.signr)) {
+ event = UTRACE_EVENT(SIGNAL_IGN);
+ action = UTRACE_SIGNAL_IGN;
+ }
+ else if (sig_kernel_stop(signal.signr)) {
+ event = UTRACE_EVENT(SIGNAL_STOP);
+ action = (signal.signr == SIGSTOP
+ ? UTRACE_SIGNAL_STOP : UTRACE_SIGNAL_TSTP);
+ }
+ else {
+ event = UTRACE_EVENT(SIGNAL_TERM);
+ action = UTRACE_SIGNAL_TERM;
+ }
+
+ if (tsk->utrace_flags & event) {
+ /*
+ * We have some interested engines, so tell them about the
+ * signal and let them change its disposition.
+ */
+
+ spin_unlock_irq(&tsk->sighand->siglock);
+
+ action = report_signal(tsk, regs, utrace, action, event, event,
+ info, ka, return_ka);
+ action &= UTRACE_ACTION_OP_MASK;
+
+ if (action & UTRACE_SIGNAL_HOLD) {
+ struct sigqueue *q = sigqueue_alloc();
+ if (likely(q != NULL)) {
+ q->flags = 0;
+ copy_siginfo(&q->info, info);
+ }
+ action &= ~UTRACE_SIGNAL_HOLD;
+ spin_lock_irq(&tsk->sighand->siglock);
+ sigaddset(&tsk->pending.signal, info->si_signo);
+ if (likely(q != NULL))
+ list_add(&q->list, &tsk->pending.list);
+ }
+ else
+ spin_lock_irq(&tsk->sighand->siglock);
+
+ recalc_sigpending_tsk(tsk);
+ }
+
+ /*
+ * We express the chosen action to the signals code in terms
+ * of a representative signal whose default action does it.
+ */
+ switch (action) {
+ case UTRACE_SIGNAL_IGN:
+ /*
+ * We've eaten the signal. That's all we do.
+ * Tell the caller to restart.
+ */
+ spin_unlock_irq(&tsk->sighand->siglock);
+ return -1;
+
+ case UTRACE_ACTION_RESUME:
+ case UTRACE_SIGNAL_DELIVER:
+ /*
+ * The handler will run. We do the SA_ONESHOT work here
+ * since the normal path will only touch *return_ka now.
+ */
+ if (return_ka->sa.sa_flags & SA_ONESHOT)
+ ka->sa.sa_handler = SIG_DFL;
+ break;
+
+ case UTRACE_SIGNAL_TSTP:
+ signal.signr = SIGTSTP;
+ tsk->signal->flags |= SIGNAL_STOP_DEQUEUED;
+ return_ka->sa.sa_handler = SIG_DFL;
+ break;
+
+ case UTRACE_SIGNAL_STOP:
+ signal.signr = SIGSTOP;
+ tsk->signal->flags |= SIGNAL_STOP_DEQUEUED;
+ return_ka->sa.sa_handler = SIG_DFL;
+ break;
+
+ case UTRACE_SIGNAL_TERM:
+ signal.signr = SIGTERM;
+ return_ka->sa.sa_handler = SIG_DFL;
+ break;
+
+ case UTRACE_SIGNAL_CORE:
+ signal.signr = SIGQUIT;
+ return_ka->sa.sa_handler = SIG_DFL;
+ break;
+
+ default:
+ BUG();
+ }
+
+ return signal.signr;
+}
+
+
+/*
+ * Cause a specified signal delivery in the target thread,
+ * which must be quiescent. The action has UTRACE_SIGNAL_* bits
+ * as returned from a report_signal callback. If ka is non-null,
+ * it gives the sigaction to follow for UTRACE_SIGNAL_DELIVER;
+ * otherwise, the installed sigaction at the time of delivery is used.
+ */
+int
+utrace_inject_signal(struct task_struct *target,
+ struct utrace_attached_engine *engine,
+ u32 action, siginfo_t *info,
+ const struct k_sigaction *ka)
+{
+ struct utrace *utrace;
+ struct utrace_signal *signal;
+ int ret;
+
+ if (info->si_signo == 0 || !valid_signal(info->si_signo))
+ return -EINVAL;
+
+ rcu_read_lock();
+ utrace = rcu_dereference(target->utrace);
+ if (utrace == NULL) {
+ rcu_read_unlock();
+ return -ESRCH;
+ }
+ utrace_lock(utrace);
+ rcu_read_unlock();
+
+ ret = 0;
+ signal = utrace->u.live.signal;
+ if (signal == NULL) {
+ ret = -ENOSYS; /* XXX */
+ }
+ else if (signal->signr != 0)
+ ret = -EAGAIN;
+ else {
+ if (info != signal->info)
+ copy_siginfo(signal->info, info);
+
+ switch (action) {
+ default:
+ ret = -EINVAL;
+ break;
+
+ case UTRACE_SIGNAL_IGN:
+ break;
+
+ case UTRACE_ACTION_RESUME:
+ case UTRACE_SIGNAL_DELIVER:
+ /*
+ * The handler will run. We do the SA_ONESHOT work
+ * here since the normal path will not touch the
+ * real sigaction when using an injected signal.
+ */
+ if (ka == NULL)
+ signal->return_ka = NULL;
+ else if (ka != signal->return_ka)
+ *signal->return_ka = *ka;
+ if (ka && ka->sa.sa_flags & SA_ONESHOT) {
+ struct k_sigaction *a;
+ a = &target->sighand->action[info->si_signo-1];
+ spin_lock_irq(&target->sighand->siglock);
+ a->sa.sa_handler = SIG_DFL;
+ spin_unlock_irq(&target->sighand->siglock);
+ }
+ signal->signr = info->si_signo;
+ break;
+
+ case UTRACE_SIGNAL_TSTP:
+ signal->signr = SIGTSTP;
+ spin_lock_irq(&target->sighand->siglock);
+ target->signal->flags |= SIGNAL_STOP_DEQUEUED;
+ spin_unlock_irq(&target->sighand->siglock);
+ signal->return_ka->sa.sa_handler = SIG_DFL;
+ break;
+
+ case UTRACE_SIGNAL_STOP:
+ signal->signr = SIGSTOP;
+ spin_lock_irq(&target->sighand->siglock);
+ target->signal->flags |= SIGNAL_STOP_DEQUEUED;
+ spin_unlock_irq(&target->sighand->siglock);
+ signal->return_ka->sa.sa_handler = SIG_DFL;
+ break;
+
+ case UTRACE_SIGNAL_TERM:
+ signal->signr = SIGTERM;
+ signal->return_ka->sa.sa_handler = SIG_DFL;
+ break;
+
+ case UTRACE_SIGNAL_CORE:
+ signal->signr = SIGQUIT;
+ signal->return_ka->sa.sa_handler = SIG_DFL;
+ break;
+ }
+ }
+
+ utrace_unlock(utrace);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(utrace_inject_signal);
+
+
+const struct utrace_regset *
+utrace_regset(struct task_struct *target,
+ struct utrace_attached_engine *engine,
+ const struct utrace_regset_view *view, int which)
+{
+ if (unlikely((unsigned) which >= view->n))
+ return NULL;
+
+ if (target != current)
+ wait_task_inactive(target);
+
+ return &view->regsets[which];
+}
+EXPORT_SYMBOL_GPL(utrace_regset);
+
+
+/*
+ * Return the task_struct for the task using ptrace on this one, or NULL.
+ * Must be called with rcu_read_lock held to keep the returned struct alive.
+ *
+ * At exec time, this may be called with task_lock(p) still held from when
+ * tracehook_unsafe_exec was just called. In that case it must give
+ * results consistent with those unsafe_exec results, i.e. non-NULL if
+ * any LSM_UNSAFE_PTRACE_* bits were set.
+ *
+ * The value is also used to display after "TracerPid:" in /proc/PID/status,
+ * where it is called with only rcu_read_lock held.
+ */
+struct task_struct *
+utrace_tracer_task(struct task_struct *target)
+{
+ struct utrace *utrace;
+ struct task_struct *tracer = NULL;
+
+ utrace = rcu_dereference(target->utrace);
+ if (utrace != NULL) {
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+ const struct utrace_engine_ops *ops;
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine,
+ entry);
+ ops = rcu_dereference(engine->ops);
+ if (ops->tracer_task) {
+ tracer = (*ops->tracer_task)(engine, target);
+ if (tracer != NULL)
+ break;
+ }
+ }
+ }
+
+ return tracer;
+}
+
+int
+utrace_allow_access_process_vm(struct task_struct *target)
+{
+ struct utrace *utrace;
+ int ret = 0;
+
+ rcu_read_lock();
+ utrace = rcu_dereference(target->utrace);
+ if (utrace != NULL) {
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+ const struct utrace_engine_ops *ops;
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine,
+ entry);
+ ops = rcu_dereference(engine->ops);
+ if (ops->allow_access_process_vm) {
+ ret = (*ops->allow_access_process_vm)(engine,
+ target,
+ current);
+ if (ret)
+ break;
+ }
+ }
+ }
+ rcu_read_unlock();
+
+ return ret;
+}
+
+/*
+ * Called on the current task to return LSM_UNSAFE_* bits implied by tracing.
+ * Called with task_lock held.
+ */
+int
+utrace_unsafe_exec(struct task_struct *tsk)
+{
+ struct utrace *utrace = tsk->utrace;
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+ const struct utrace_engine_ops *ops;
+ int unsafe = 0;
+
+ /* XXX must change for sharing */
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine, entry);
+ ops = rcu_dereference(engine->ops);
+ if (ops->unsafe_exec)
+ unsafe |= (*ops->unsafe_exec)(engine, tsk);
+ }
+
+ return unsafe;
+}
--- linux-2.6/kernel/exit.c.utrace-ptrace-compat
+++ linux-2.6/kernel/exit.c
@@ -21,6 +21,7 @@
#include <linux/file.h>
#include <linux/binfmts.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/profile.h>
#include <linux/mount.h>
#include <linux/proc_fs.h>
@@ -139,10 +140,10 @@ void release_task(struct task_struct * p
struct task_struct *leader;
int zap_leader;
repeat:
+ tracehook_release_task(p);
atomic_dec(&p->user->processes);
write_lock_irq(&tasklist_lock);
- ptrace_unlink(p);
- BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children));
+ BUG_ON(tracehook_check_released(p));
__exit_signal(p);
/*
@@ -219,10 +220,10 @@ static int will_become_orphaned_pgrp(int
do_each_task_pid(pgrp, PIDTYPE_PGID, p) {
if (p == ignored_task
|| p->exit_state
- || p->real_parent->pid == 1)
+ || p->parent->pid == 1)
continue;
- if (process_group(p->real_parent) != pgrp
- && p->real_parent->signal->session == p->signal->session) {
+ if (process_group(p->parent) != pgrp
+ && p->parent->signal->session == p->signal->session) {
ret = 0;
break;
}
@@ -250,16 +251,6 @@ static int has_stopped_jobs(int pgrp)
if (p->state != TASK_STOPPED)
continue;
- /* If p is stopped by a debugger on a signal that won't
- stop it, then don't count p as stopped. This isn't
- perfect but it's a good approximation. */
- if (unlikely (p->ptrace)
- && p->exit_code != SIGSTOP
- && p->exit_code != SIGTSTP
- && p->exit_code != SIGTTOU
- && p->exit_code != SIGTTIN)
- continue;
-
retval = 1;
break;
} while_each_task_pid(pgrp, PIDTYPE_PGID, p);
@@ -282,11 +273,9 @@ static void reparent_to_init(void)
{
write_lock_irq(&tasklist_lock);
- ptrace_unlink(current);
/* Reparent to init */
remove_parent(current);
current->parent = child_reaper;
- current->real_parent = child_reaper;
add_parent(current);
/* Set the exit signal to SIGCHLD so we signal init on exit */
@@ -592,11 +581,11 @@ choose_new_parent(struct task_struct *p,
* the parent is not a zombie.
*/
BUG_ON(p == reaper || reaper->exit_state);
- p->real_parent = reaper;
+ p->parent = reaper;
}
static void
-reparent_thread(struct task_struct *p, struct task_struct *father, int traced)
+reparent_thread(struct task_struct *p, struct task_struct *father)
{
/* We don't want people slaying init. */
if (p->exit_signal != -1)
@@ -607,35 +596,14 @@ reparent_thread(struct task_struct *p, s
group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p);
/* Move the child from its dying parent to the new one. */
- if (unlikely(traced)) {
- /* Preserve ptrace links if someone else is tracing this child. */
- list_del_init(&p->ptrace_list);
- if (p->parent != p->real_parent)
- list_add(&p->ptrace_list, &p->real_parent->ptrace_children);
- } else {
- /* If this child is being traced, then we're the one tracing it
- * anyway, so let go of it.
- */
- p->ptrace = 0;
- remove_parent(p);
- p->parent = p->real_parent;
- add_parent(p);
+ list_move_tail(&p->sibling, &p->parent->children);
- /* If we'd notified the old parent about this child's death,
- * also notify the new parent.
- */
- if (p->exit_state == EXIT_ZOMBIE && p->exit_signal != -1 &&
- thread_group_empty(p))
- do_notify_parent(p, p->exit_signal);
- else if (p->state == TASK_TRACED) {
- /*
- * If it was at a trace stop, turn it into
- * a normal stop since it's no longer being
- * traced.
- */
- ptrace_untrace(p);
- }
- }
+ /* If we'd notified the old parent about this child's death,
+ * also notify the new parent.
+ */
+ if (p->exit_state == EXIT_ZOMBIE && p->exit_signal != -1 &&
+ thread_group_empty(p))
+ do_notify_parent(p, p->exit_signal);
/*
* process group orphan check
@@ -661,7 +629,7 @@ reparent_thread(struct task_struct *p, s
* the global child reaper process (ie "init")
*/
static void
-forget_original_parent(struct task_struct *father, struct list_head *to_release)
+forget_original_parent(struct task_struct *father)
{
struct task_struct *p, *reaper = father;
struct list_head *_p, *_n;
@@ -674,48 +642,10 @@ forget_original_parent(struct task_struc
}
} while (reaper->exit_state);
- /*
- * There are only two places where our children can be:
- *
- * - in our child list
- * - in our ptraced child list
- *
- * Search them and reparent children.
- */
list_for_each_safe(_p, _n, &father->children) {
- int ptrace;
p = list_entry(_p, struct task_struct, sibling);
-
- ptrace = p->ptrace;
-
- /* if father isn't the real parent, then ptrace must be enabled */
- BUG_ON(father != p->real_parent && !ptrace);
-
- if (father == p->real_parent) {
- /* reparent with a reaper, real father it's us */
- choose_new_parent(p, reaper);
- reparent_thread(p, father, 0);
- } else {
- /* reparent ptraced task to its real parent */
- __ptrace_unlink (p);
- if (p->exit_state == EXIT_ZOMBIE && p->exit_signal != -1 &&
- thread_group_empty(p))
- do_notify_parent(p, p->exit_signal);
- }
-
- /*
- * if the ptraced child is a zombie with exit_signal == -1
- * we must collect it before we exit, or it will remain
- * zombie forever since we prevented it from self-reap itself
- * while it was being traced by us, to be able to see it in wait4.
- */
- if (unlikely(ptrace && p->exit_state == EXIT_ZOMBIE && p->exit_signal == -1))
- list_add(&p->ptrace_list, to_release);
- }
- list_for_each_safe(_p, _n, &father->ptrace_children) {
- p = list_entry(_p, struct task_struct, ptrace_list);
choose_new_parent(p, reaper);
- reparent_thread(p, father, 1);
+ reparent_thread(p, father);
}
}
@@ -727,7 +657,8 @@ static void exit_notify(struct task_stru
{
int state;
struct task_struct *t;
- struct list_head ptrace_dead, *_p, *_n;
+ int noreap;
+ void *cookie;
if (signal_pending(tsk) && !(tsk->signal->flags & SIGNAL_GROUP_EXIT)
&& !thread_group_empty(tsk)) {
@@ -763,10 +694,8 @@ static void exit_notify(struct task_stru
* jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2)
*/
- INIT_LIST_HEAD(&ptrace_dead);
- forget_original_parent(tsk, &ptrace_dead);
+ forget_original_parent(tsk);
BUG_ON(!list_empty(&tsk->children));
- BUG_ON(!list_empty(&tsk->ptrace_children));
/*
* Check to see if any process groups have become orphaned
@@ -778,7 +707,7 @@ static void exit_notify(struct task_stru
* is about to become orphaned.
*/
- t = tsk->real_parent;
+ t = tsk->parent;
if ((process_group(t) != process_group(tsk)) &&
(t->signal->session == tsk->signal->session) &&
@@ -810,32 +739,18 @@ static void exit_notify(struct task_stru
&& !capable(CAP_KILL))
tsk->exit_signal = SIGCHLD;
-
- /* If something other than our normal parent is ptracing us, then
- * send it a SIGCHLD instead of honoring exit_signal. exit_signal
- * only has special meaning to our real parent.
- */
- if (tsk->exit_signal != -1 && thread_group_empty(tsk)) {
- int signal = tsk->parent == tsk->real_parent ? tsk->exit_signal : SIGCHLD;
- do_notify_parent(tsk, signal);
- } else if (tsk->ptrace) {
- do_notify_parent(tsk, SIGCHLD);
- }
+ if (!tracehook_notify_death(tsk, &noreap, &cookie)
+ && tsk->exit_signal != -1 && thread_group_empty(tsk))
+ do_notify_parent(tsk, tsk->exit_signal);
state = EXIT_ZOMBIE;
- if (tsk->exit_signal == -1 &&
- (likely(tsk->ptrace == 0) ||
- unlikely(tsk->parent->signal->flags & SIGNAL_GROUP_EXIT)))
+ if (tsk->exit_signal == -1 && !noreap)
state = EXIT_DEAD;
tsk->exit_state = state;
write_unlock_irq(&tasklist_lock);
- list_for_each_safe(_p, _n, &ptrace_dead) {
- list_del_init(_p);
- t = list_entry(_p, struct task_struct, ptrace_list);
- release_task(t);
- }
+ tracehook_report_death(tsk, state, cookie);
/* If the process is dead, release it - nobody will wait for it */
if (state == EXIT_DEAD)
@@ -860,10 +775,7 @@ fastcall NORET_TYPE void do_exit(long co
if (unlikely(tsk == child_reaper))
panic("Attempted to kill init!");
- if (unlikely(current->ptrace & PT_TRACE_EXIT)) {
- current->ptrace_message = code;
- ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP);
- }
+ tracehook_report_exit(&code);
/*
* We're taking recursive faults here in do_exit. Safest is to just
@@ -880,6 +792,8 @@ fastcall NORET_TYPE void do_exit(long co
tsk->flags |= PF_EXITING;
+ ptrace_exit(tsk);
+
if (unlikely(in_atomic()))
printk(KERN_INFO "note: %s[%d] exited with preempt_count %d\n",
current->comm, current->pid,
@@ -1035,10 +949,9 @@ static int eligible_child(pid_t pid, int
}
/*
- * Do not consider detached threads that are
- * not ptraced:
+ * Do not consider detached threads.
*/
- if (p->exit_signal == -1 && !p->ptrace)
+ if (p->exit_signal == -1)
return 0;
/* Wait for all children (clone and not) if __WALL is set;
@@ -1109,7 +1022,7 @@ static int wait_task_zombie(struct task_
if (unlikely(p->exit_state != EXIT_ZOMBIE))
return 0;
- if (unlikely(p->exit_signal == -1 && p->ptrace == 0))
+ if (unlikely(p->exit_signal == -1))
return 0;
get_task_struct(p);
read_unlock(&tasklist_lock);
@@ -1133,15 +1046,9 @@ static int wait_task_zombie(struct task_
BUG_ON(state != EXIT_DEAD);
return 0;
}
- if (unlikely(p->exit_signal == -1 && p->ptrace == 0)) {
- /*
- * This can only happen in a race with a ptraced thread
- * dying on another processor.
- */
- return 0;
- }
+ BUG_ON(p->exit_signal == -1);
- if (likely(p->real_parent == p->parent) && likely(p->signal)) {
+ if (likely(p->signal)) {
struct signal_struct *psig;
struct signal_struct *sig;
@@ -1223,28 +1130,8 @@ static int wait_task_zombie(struct task_
return retval;
}
retval = p->pid;
- if (p->real_parent != p->parent) {
- write_lock_irq(&tasklist_lock);
- /* Double-check with lock held. */
- if (p->real_parent != p->parent) {
- __ptrace_unlink(p);
- // TODO: is this safe?
- p->exit_state = EXIT_ZOMBIE;
- /*
- * If this is not a detached task, notify the parent.
- * If it's still not detached after that, don't release
- * it now.
- */
- if (p->exit_signal != -1) {
- do_notify_parent(p, p->exit_signal);
- if (p->exit_signal != -1)
- p = NULL;
- }
- }
- write_unlock_irq(&tasklist_lock);
- }
- if (p != NULL)
- release_task(p);
+ release_task(p);
+
BUG_ON(!retval);
return retval;
}
@@ -1263,7 +1150,7 @@ static int wait_task_stopped(struct task
if (!p->exit_code)
return 0;
- if (delayed_group_leader && !(p->ptrace & PT_PTRACED) &&
+ if (delayed_group_leader &&
p->signal && p->signal->group_stop_count > 0)
/*
* A group stop is in progress and this is the group leader.
@@ -1284,14 +1171,13 @@ static int wait_task_stopped(struct task
if (unlikely(noreap)) {
pid_t pid = p->pid;
uid_t uid = p->uid;
- int why = (p->ptrace & PT_PTRACED) ? CLD_TRAPPED : CLD_STOPPED;
exit_code = p->exit_code;
if (unlikely(!exit_code) ||
unlikely(p->state & TASK_TRACED))
goto bail_ref;
- return wait_noreap_copyout(p, pid, uid,
- why, (exit_code << 8) | 0x7f,
+ return wait_noreap_copyout(p, pid, uid, CLD_STOPPED,
+ (exit_code << 8) | 0x7f,
infop, ru);
}
@@ -1347,9 +1233,7 @@ bail_ref:
if (!retval && infop)
retval = put_user(0, &infop->si_errno);
if (!retval && infop)
- retval = put_user((short)((p->ptrace & PT_PTRACED)
- ? CLD_TRAPPED : CLD_STOPPED),
- &infop->si_code);
+ retval = put_user((short)CLD_STOPPED, &infop->si_code);
if (!retval && infop)
retval = put_user(exit_code, &infop->si_status);
if (!retval && infop)
@@ -1417,22 +1301,6 @@ static int wait_task_continued(struct ta
}
-static inline int my_ptrace_child(struct task_struct *p)
-{
- if (!(p->ptrace & PT_PTRACED))
- return 0;
- if (!(p->ptrace & PT_ATTACHED))
- return 1;
- /*
- * This child was PTRACE_ATTACH'd. We should be seeing it only if
- * we are the attacher. If we are the real parent, this is a race
- * inside ptrace_attach. It is waiting for the tasklist_lock,
- * which we have to switch the parent links, but has already set
- * the flags in p->ptrace.
- */
- return (p->parent != p->real_parent);
-}
-
static long do_wait(pid_t pid, int options, struct siginfo __user *infop,
int __user *stat_addr, struct rusage __user *ru)
{
@@ -1464,26 +1332,17 @@ repeat:
switch (p->state) {
case TASK_TRACED:
- /*
- * When we hit the race with PTRACE_ATTACH,
- * we will not report this child. But the
- * race means it has not yet been moved to
- * our ptrace_children list, so we need to
- * set the flag here to avoid a spurious ECHILD
- * when the race happens with the only child.
- */
flag = 1;
- if (!my_ptrace_child(p))
- continue;
- /*FALLTHROUGH*/
+ continue;
case TASK_STOPPED:
/*
* It's stopped now, so it might later
* continue, exit, or stop again.
*/
flag = 1;
- if (!(options & WUNTRACED) &&
- !my_ptrace_child(p))
+ if (!(options & WUNTRACED))
+ continue;
+ if (tracehook_inhibit_wait_stopped(p))
continue;
retval = wait_task_stopped(p, ret == 2,
(options & WNOWAIT),
@@ -1508,6 +1367,10 @@ repeat:
goto check_continued;
if (!likely(options & WEXITED))
continue;
+ if (tracehook_inhibit_wait_zombie(p)) {
+ flag = 1;
+ continue;
+ }
retval = wait_task_zombie(
p, (options & WNOWAIT),
infop, stat_addr, ru);
@@ -1524,6 +1387,8 @@ check_continued:
flag = 1;
if (!unlikely(options & WCONTINUED))
continue;
+ if (tracehook_inhibit_wait_continued(p))
+ continue;
retval = wait_task_continued(
p, (options & WNOWAIT),
infop, stat_addr, ru);
@@ -1532,16 +1397,15 @@ check_continued:
break;
}
}
- if (!flag) {
- list_for_each(_p, &tsk->ptrace_children) {
- p = list_entry(_p, struct task_struct,
- ptrace_list);
- if (!eligible_child(pid, options, p))
- continue;
- flag = 1;
- break;
- }
+
+ retval = ptrace_do_wait(tsk, pid, options,
+ infop, stat_addr, ru);
+ if (retval != -ECHILD) {
+ flag = 1;
+ if (retval != 0) /* He released the lock. */
+ goto end;
}
+
if (options & __WNOTHREAD)
break;
tsk = next_thread(tsk);
@@ -1565,7 +1429,7 @@ end:
remove_wait_queue(¤t->signal->wait_chldexit,&wait);
if (infop) {
if (retval > 0)
- retval = 0;
+ retval = 0;
else {
/*
* For a WNOHANG return, clear out all the fields
--- linux-2.6/kernel/sys.c.utrace-ptrace-compat
+++ linux-2.6/kernel/sys.c
@@ -1274,7 +1274,7 @@ asmlinkage long sys_setpgid(pid_t pid, p
if (!thread_group_leader(p))
goto out;
- if (p->real_parent == group_leader) {
+ if (p->parent == group_leader) {
err = -EPERM;
if (p->signal->session != group_leader->signal->session)
goto out;
--- linux-2.6/kernel/timer.c.utrace-ptrace-compat
+++ linux-2.6/kernel/timer.c
@@ -1324,7 +1324,7 @@ asmlinkage long sys_getpid(void)
}
/*
- * Accessing ->real_parent is not SMP-safe, it could
+ * Accessing ->parent is not SMP-safe, it could
* change from under us. However, we can use a stale
* value of ->real_parent under rcu_read_lock(), see
* release_task()->call_rcu(delayed_put_task_struct).
@@ -1334,7 +1334,7 @@ asmlinkage long sys_getppid(void)
int pid;
rcu_read_lock();
- pid = rcu_dereference(current->real_parent)->tgid;
+ pid = rcu_dereference(current->parent)->tgid;
rcu_read_unlock();
return pid;
--- linux-2.6/kernel/ptrace.c.utrace-ptrace-compat
+++ linux-2.6/kernel/ptrace.c
@@ -22,101 +22,15 @@
#include <asm/pgtable.h>
#include <asm/uaccess.h>
-/*
- * ptrace a task: make the debugger its new parent and
- * move it to the ptrace list.
- *
- * Must be called with the tasklist lock write-held.
- */
-void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
-{
- BUG_ON(!list_empty(&child->ptrace_list));
- if (child->parent == new_parent)
- return;
- list_add(&child->ptrace_list, &child->parent->ptrace_children);
- remove_parent(child);
- child->parent = new_parent;
- add_parent(child);
-}
-
-/*
- * Turn a tracing stop into a normal stop now, since with no tracer there
- * would be no way to wake it up with SIGCONT or SIGKILL. If there was a
- * signal sent that would resume the child, but didn't because it was in
- * TASK_TRACED, resume it now.
- * Requires that irqs be disabled.
- */
-void ptrace_untrace(struct task_struct *child)
-{
- spin_lock(&child->sighand->siglock);
- if (child->state == TASK_TRACED) {
- if (child->signal->flags & SIGNAL_STOP_STOPPED) {
- child->state = TASK_STOPPED;
- } else {
- signal_wake_up(child, 1);
- }
- }
- spin_unlock(&child->sighand->siglock);
-}
-
-/*
- * unptrace a task: move it back to its original parent and
- * remove it from the ptrace list.
- *
- * Must be called with the tasklist lock write-held.
- */
-void __ptrace_unlink(struct task_struct *child)
-{
- BUG_ON(!child->ptrace);
-
- child->ptrace = 0;
- if (!list_empty(&child->ptrace_list)) {
- list_del_init(&child->ptrace_list);
- remove_parent(child);
- child->parent = child->real_parent;
- add_parent(child);
- }
-
- if (child->state == TASK_TRACED)
- ptrace_untrace(child);
-}
-
-/*
- * Check that we have indeed attached to the thing..
- */
-int ptrace_check_attach(struct task_struct *child, int kill)
-{
- int ret = -ESRCH;
-
- /*
- * We take the read lock around doing both checks to close a
- * possible race where someone else was tracing our child and
- * detached between these two checks. After this locked check,
- * we are sure that this is our traced child and that can only
- * be changed by us so it's not changing right after this.
- */
- read_lock(&tasklist_lock);
- if ((child->ptrace & PT_PTRACED) && child->parent == current &&
- (!(child->ptrace & PT_ATTACHED) || child->real_parent != current)
- && child->signal != NULL) {
- ret = 0;
- spin_lock_irq(&child->sighand->siglock);
- if (child->state == TASK_STOPPED) {
- child->state = TASK_TRACED;
- } else if (child->state != TASK_TRACED && !kill) {
- ret = -ESRCH;
- }
- spin_unlock_irq(&child->sighand->siglock);
- }
- read_unlock(&tasklist_lock);
+#ifdef CONFIG_PTRACE
+#include <linux/utrace.h>
+#include <linux/tracehook.h>
+#include <asm/tracehook.h>
+#endif
- if (!ret && !kill) {
- wait_task_inactive(child);
- }
+int getrusage(struct task_struct *, int, struct rusage __user *);
- /* All systems go.. */
- return ret;
-}
+//#define PTRACE_DEBUG
static int may_attach(struct task_struct *task)
{
@@ -157,90 +71,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 out;
-
-repeat:
- /*
- * Nasty, nasty.
- *
- * We want to hold both the task-lock and the
- * tasklist_lock for writing at the same time.
- * But that's against the rules (tasklist_lock
- * is taken for reading by interrupts on other
- * cpu's that may have task_lock).
- */
- task_lock(task);
- local_irq_disable();
- if (!write_trylock(&tasklist_lock)) {
- local_irq_enable();
- task_unlock(task);
- do {
- cpu_relax();
- } while (!write_can_lock(&tasklist_lock));
- goto repeat;
- }
-
- if (!task->mm)
- goto bad;
- /* the same process cannot be attached many times */
- if (task->ptrace & PT_PTRACED)
- goto bad;
- retval = may_attach(task);
- if (retval)
- goto bad;
-
- /* Go */
- task->ptrace |= PT_PTRACED | ((task->real_parent != current)
- ? PT_ATTACHED : 0);
- if (capable(CAP_SYS_PTRACE))
- task->ptrace |= PT_PTRACE_CAP;
-
- __ptrace_link(task, current);
-
- force_sig_specific(SIGSTOP, task);
-
-bad:
- write_unlock_irq(&tasklist_lock);
- task_unlock(task);
-out:
- return retval;
-}
-
-static inline void __ptrace_detach(struct task_struct *child, unsigned int data)
-{
- child->exit_code = data;
- /* .. re-parent .. */
- __ptrace_unlink(child);
- /* .. and wake it up. */
- if (child->exit_state != EXIT_ZOMBIE)
- wake_up_process(child);
-}
-
-int ptrace_detach(struct task_struct *child, unsigned int data)
-{
- if (!valid_signal(data))
- return -EIO;
-
- /* Architecture-specific hardware disable .. */
- ptrace_disable(child);
-
- write_lock_irq(&tasklist_lock);
- /* protect against de_thread()->release_task() */
- if (child->ptrace)
- __ptrace_detach(child, data);
- write_unlock_irq(&tasklist_lock);
-
- return 0;
-}
-
/*
* Access another process' address space.
* Source/target buffer must be kernel space,
@@ -295,249 +125,1371 @@ 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;
- }
- return copied;
-}
-
-int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
-{
- int copied = 0;
-
- while (len > 0) {
- char buf[128];
- int this_len, retval;
-
- 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;
+#else
+
+struct ptrace_state
+{
+ /*
+ * These elements are always available, even when the struct is
+ * awaiting destruction at the next RCU callback point.
+ */
+ struct utrace_attached_engine *engine;
+ struct task_struct *task; /* Target task. */
+ struct task_struct *parent; /* Whom we report to. */
+ struct list_head entry; /* Entry on parent->ptracees list. */
+
+ union {
+ struct rcu_head dead;
+ struct {
+ 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. */
+ unsigned int cap_sys_ptrace:1; /* Tracer capable. */
+
+ union
+ {
+ unsigned long eventmsg;
+ siginfo_t *siginfo;
+ } u;
+ } live;
+ } u;
+};
+
+static const struct utrace_engine_ops ptrace_utrace_ops; /* Initialized below. */
+
+
+static void
+ptrace_state_link(struct ptrace_state *state)
+{
+ task_lock(state->parent);
+ list_add_rcu(&state->entry, &state->parent->ptracees);
+ task_unlock(state->parent);
+}
+
+static void
+ptrace_state_unlink(struct ptrace_state *state)
+{
+ task_lock(state->parent);
+ list_del_rcu(&state->entry);
+ task_unlock(state->parent);
+}
+
+static int
+ptrace_setup(struct task_struct *target, struct utrace_attached_engine *engine,
+ struct task_struct *parent, u8 options, int cap_sys_ptrace)
+{
+ struct ptrace_state *state = kzalloc(sizeof *state, GFP_USER);
+ if (unlikely(state == NULL))
+ return -ENOMEM;
+
+ state->engine = engine;
+ state->task = target;
+ state->parent = parent;
+ state->u.live.options = options;
+ state->u.live.cap_sys_ptrace = cap_sys_ptrace;
+ 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.
+ * But for a corner case we sometimes see the REAP event instead.
+ */
+ flags |= UTRACE_ACTION_NOREAP | UTRACE_EVENT(REAP);
+
+ 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);
}
-static int ptrace_setoptions(struct task_struct *child, long data)
+static int ptrace_traceme(void)
{
- child->ptrace &= ~PT_TRACE_MASK;
+ struct utrace_attached_engine *engine;
+ int retval;
- if (data & PTRACE_O_TRACESYSGOOD)
- child->ptrace |= PT_TRACESYSGOOD;
+ 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 {
+ task_lock(current);
+ retval = security_ptrace(current->parent, current);
+ task_unlock(current);
+ if (!retval)
+ retval = ptrace_setup(current, engine,
+ current->parent, 0, 0);
+ if (retval)
+ utrace_detach(current, engine);
+ else
+ ptrace_update(current, engine, 0);
+ }
- if (data & PTRACE_O_TRACEFORK)
- child->ptrace |= PT_TRACE_FORK;
+ return retval;
+}
- if (data & PTRACE_O_TRACEVFORK)
- child->ptrace |= PT_TRACE_VFORK;
+static int ptrace_attach(struct task_struct *task)
+{
+ struct utrace_attached_engine *engine;
+ int retval;
- if (data & PTRACE_O_TRACECLONE)
- child->ptrace |= PT_TRACE_CLONE;
+ retval = -EPERM;
+ if (task->pid <= 1)
+ goto bad;
+ if (task->tgid == current->tgid)
+ goto bad;
+ if (!task->mm) /* kernel threads */
+ goto bad;
- if (data & PTRACE_O_TRACEEXEC)
- child->ptrace |= PT_TRACE_EXEC;
+ 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 (data & PTRACE_O_TRACEVFORKDONE)
- child->ptrace |= PT_TRACE_VFORK_DONE;
+ if (ptrace_may_attach(task))
+ retval = ptrace_setup(task, engine, current, 0,
+ capable(CAP_SYS_PTRACE));
+ if (retval)
+ utrace_detach(task, engine);
+ else {
+ int stopped;
+
+ /* Go */
+ ptrace_update(task, engine, 0);
+ force_sig_specific(SIGSTOP, task);
+
+ spin_lock_irq(&task->sighand->siglock);
+ stopped = (task->state == TASK_STOPPED);
+ spin_unlock_irq(&task->sighand->siglock);
+
+ if (stopped) {
+ /*
+ * Do now the regset 0 writeback that we do on every
+ * stop, since it's never been done. On register
+ * window machines, this makes sure the user memory
+ * backing the register data is up to date.
+ */
+ const struct utrace_regset *regset;
+ regset = utrace_regset(task, engine,
+ utrace_native_view(task), 0);
+ if (regset->writeback)
+ (*regset->writeback)(task, regset, 1);
+ }
+ }
- if (data & PTRACE_O_TRACEEXIT)
- child->ptrace |= PT_TRACE_EXIT;
+bad:
+ return retval;
+}
- return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
+static int ptrace_detach(struct task_struct *task,
+ struct utrace_attached_engine *engine)
+{
+ struct ptrace_state *state = (struct ptrace_state *) engine->data;
+ /*
+ * Clearing ->data before detach makes sure an unrelated task
+ * calling into ptrace_tracer_task won't try to touch stale state.
+ */
+ rcu_assign_pointer(engine->data, 0UL);
+ utrace_detach(task, engine);
+ ptrace_state_unlink(state);
+ ptrace_done(state);
+ return 0;
}
-static int ptrace_getsiginfo(struct task_struct *child, siginfo_t __user * data)
+
+/*
+ * This is called when we are exiting. We must stop all our ptracing.
+ */
+void
+ptrace_exit(struct task_struct *tsk)
{
- siginfo_t lastinfo;
- int error = -ESRCH;
+ rcu_read_lock();
+ if (unlikely(!list_empty(&tsk->ptracees))) {
+ struct ptrace_state *state, *next;
- read_lock(&tasklist_lock);
- if (likely(child->sighand != NULL)) {
- error = -EINVAL;
- spin_lock_irq(&child->sighand->siglock);
- if (likely(child->last_siginfo != NULL)) {
- lastinfo = *child->last_siginfo;
- error = 0;
+ /*
+ * First detach the utrace layer from all the tasks.
+ * We don't want to hold any locks while calling utrace_detach.
+ */
+ list_for_each_entry_rcu(state, &tsk->ptracees, entry) {
+ rcu_assign_pointer(state->engine->data, 0UL);
+ utrace_detach(state->task, state->engine);
}
- spin_unlock_irq(&child->sighand->siglock);
+
+ /*
+ * Now clear out our list and clean up our data structures.
+ * The task_lock protects our list structure.
+ */
+ task_lock(tsk);
+ list_for_each_entry_safe(state, next, &tsk->ptracees, entry) {
+ list_del_rcu(&state->entry);
+ ptrace_done(state);
+ }
+ task_unlock(tsk);
}
- read_unlock(&tasklist_lock);
- if (!error)
- return copy_siginfo_to_user(data, &lastinfo);
- return error;
+ rcu_read_unlock();
+
+ BUG_ON(!list_empty(&tsk->ptracees));
}
-static int ptrace_setsiginfo(struct task_struct *child, siginfo_t __user * data)
+static int
+ptrace_induce_signal(struct task_struct *target,
+ struct utrace_attached_engine *engine,
+ long signr)
{
- siginfo_t newinfo;
- int error = -ESRCH;
+ struct ptrace_state *state = (struct ptrace_state *) engine->data;
- if (copy_from_user(&newinfo, data, sizeof (siginfo_t)))
- return -EFAULT;
+ if (signr == 0)
+ return 0;
- read_lock(&tasklist_lock);
- if (likely(child->sighand != NULL)) {
- error = -EINVAL;
- spin_lock_irq(&child->sighand->siglock);
- if (likely(child->last_siginfo != NULL)) {
- *child->last_siginfo = newinfo;
- error = 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;
}
- spin_unlock_irq(&child->sighand->siglock);
+
+ return utrace_inject_signal(target, engine,
+ UTRACE_ACTION_RESUME, info, NULL);
+ }
+
+ return 0;
+}
+
+fastcall int
+ptrace_regset_access(struct task_struct *target,
+ struct utrace_attached_engine *engine,
+ const struct utrace_regset_view *view,
+ int setno, unsigned long offset, unsigned int size,
+ void __user *data, int write)
+{
+ const struct utrace_regset *regset = utrace_regset(target, engine,
+ view, setno);
+ int ret;
+
+ if (unlikely(regset == NULL))
+ return -EIO;
+
+ if (size == (unsigned int) -1)
+ size = regset->size * regset->n;
+
+ if (write) {
+ if (!access_ok(VERIFY_READ, data, size))
+ ret = -EIO;
+ else
+ ret = (*regset->set)(target, regset,
+ offset, size, NULL, data);
+ }
+ else {
+ if (!access_ok(VERIFY_WRITE, data, size))
+ ret = -EIO;
+ else
+ ret = (*regset->get)(target, regset,
+ offset, size, NULL, data);
+ }
+
+ return ret;
+}
+
+fastcall int
+ptrace_onereg_access(struct task_struct *target,
+ struct utrace_attached_engine *engine,
+ const struct utrace_regset_view *view,
+ int setno, unsigned long regno,
+ void __user *data, int write)
+{
+ const struct utrace_regset *regset = utrace_regset(target, engine,
+ view, setno);
+ unsigned int pos;
+ int ret;
+
+ if (unlikely(regset == NULL))
+ return -EIO;
+
+ if (regno < regset->bias || regno >= regset->bias + regset->n)
+ return -EINVAL;
+
+ pos = (regno - regset->bias) * regset->size;
+
+ if (write) {
+ if (!access_ok(VERIFY_READ, data, regset->size))
+ ret = -EIO;
+ else
+ ret = (*regset->set)(target, regset, pos, regset->size,
+ NULL, data);
+ }
+ else {
+ if (!access_ok(VERIFY_WRITE, data, regset->size))
+ ret = -EIO;
+ else
+ ret = (*regset->get)(target, regset, pos, regset->size,
+ NULL, data);
}
+
+ return ret;
+}
+
+fastcall int
+ptrace_layout_access(struct task_struct *target,
+ struct utrace_attached_engine *engine,
+ const struct utrace_regset_view *view,
+ const struct ptrace_layout_segment layout[],
+ unsigned long addr, unsigned int size,
+ void __user *udata, void *kdata, int write)
+{
+ const struct ptrace_layout_segment *seg;
+ int ret = -EIO;
+
+ if (kdata == NULL &&
+ !access_ok(write ? VERIFY_READ : VERIFY_WRITE, udata, size))
+ return -EIO;
+
+ seg = layout;
+ do {
+ unsigned int pos, n;
+
+ while (addr >= seg->end && seg->end != 0)
+ ++seg;
+
+ if (addr < seg->start || addr >= seg->end)
+ return -EIO;
+
+ pos = addr - seg->start + seg->offset;
+ n = min(size, seg->end - (unsigned int) addr);
+
+ if (unlikely(seg->regset == (unsigned int) -1)) {
+ /*
+ * This is a no-op/zero-fill portion of struct user.
+ */
+ ret = 0;
+ if (!write) {
+ if (kdata)
+ memset(kdata, 0, n);
+ else if (clear_user(udata, n))
+ ret = -EFAULT;
+ }
+ }
+ else {
+ unsigned int align;
+ const struct utrace_regset *regset = utrace_regset(
+ target, engine, view, seg->regset);
+ if (unlikely(regset == NULL))
+ return -EIO;
+
+ /*
+ * A ptrace compatibility layout can do a misaligned
+ * regset access, e.g. word access to larger data.
+ * An arch's compat layout can be this way only if
+ * it is actually ok with the regset code despite the
+ * regset->align setting.
+ */
+ align = min(regset->align, size);
+ if ((pos & (align - 1))
+ || pos >= regset->n * regset->size)
+ return -EIO;
+
+ if (write)
+ ret = (*regset->set)(target, regset,
+ pos, n, kdata, udata);
+ else
+ ret = (*regset->get)(target, regset,
+ pos, n, kdata, udata);
+ }
+
+ if (kdata)
+ kdata += n;
+ else
+ udata += n;
+ addr += n;
+ size -= n;
+ } while (ret == 0 && size > 0);
+
+ 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;
+
+ 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);
- return error;
+#ifdef PTRACE_DEBUG
+ printk("ptrace pid %ld => %p\n", pid, child);
+#endif
+ if (!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->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);
+ }
+
+ /*
+ * We do this for all requests to match traditional ptrace behavior.
+ * If the machine state synchronization done at context switch time
+ * includes e.g. writing back to user memory, we want to make sure
+ * that has finished before a PTRACE_PEEKDATA can fetch the results.
+ * On most machines, only regset data is affected by context switch
+ * and calling utrace_regset later on will take care of that, so
+ * this is superfluous.
+ *
+ * To do this purely in utrace terms, we could do:
+ * (void) utrace_regset(child, engine, utrace_native_view(child), 0);
+ */
+ wait_task_inactive(child);
+
+ if (child->exit_state)
+ goto out_tsk;
+
+ *childp = child;
+ *enginep = engine;
+ *statep = state;
+ return -EIO;
+
+out_tsk:
+ put_task_struct(child);
+out:
+ return ret;
}
-int ptrace_request(struct task_struct *child, long request,
- long addr, long data)
+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:
+#ifdef PTRACE_SYSEMU
+ case PTRACE_SYSEMU:
+ case PTRACE_SYSEMU_SINGLESTEP:
+#endif
+#ifdef PTRACE_SINGLEBLOCK
+ case PTRACE_SINGLEBLOCK:
+# ifdef ARCH_HAS_BLOCK_STEP
+ if (! ARCH_HAS_BLOCK_STEP)
+# endif
+ if (request == PTRACE_SINGLEBLOCK)
+ break;
+#endif
+ case PTRACE_SINGLESTEP:
+#ifdef ARCH_HAS_SINGLE_STEP
+ if (! ARCH_HAS_SINGLE_STEP)
+#endif
+ if (request == PTRACE_SINGLESTEP
+#ifdef PTRACE_SYSEMU_SINGLESTEP
+ || request == PTRACE_SYSEMU_SINGLESTEP
+#endif
+ )
+ break;
+
+ 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 = ptrace_setoptions(child, data);
+ ret = -EINVAL;
+ if (data & ~PTRACE_O_MASK)
+ break;
+ state->u.live.options = data;
+ ptrace_update(child, engine, UTRACE_ACTION_QUIESCE);
+ ret = 0;
break;
+ }
+
+ return ret;
+}
+
+
+asmlinkage long sys_ptrace(long request, long pid, long addr, long data)
+{
+ struct task_struct *child;
+ struct utrace_attached_engine *engine;
+ struct ptrace_state *state;
+ long ret, val;
+
+#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;
+
+ val = 0;
+ ret = arch_ptrace(&request, child, engine, addr, data, &val);
+ if (ret != -ENOSYS) {
+ if (ret == 0) {
+ ret = val;
+ force_successful_syscall_return();
+ }
+ 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(child->ptrace_message, (unsigned long __user *) data);
+ ret = put_user(state->u.live.have_eventmsg
+ ? state->u.live.u.eventmsg : 0L,
+ (unsigned long __user *) data);
break;
case PTRACE_GETSIGINFO:
- ret = ptrace_getsiginfo(child, (siginfo_t __user *) data);
+ 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 = ptrace_setsiginfo(child, (siginfo_t __user *) data);
+ 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;
+ compat_long_t ret, val;
+
+#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;
+
+ val = 0;
+ ret = arch_compat_ptrace(&request, child, engine, addr, cdata, &val);
+ if (ret != -ENOSYS) {
+ if (ret == 0) {
+ ret = val;
+ force_successful_syscall_return();
+ }
+ 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
+
-/**
- * ptrace_traceme -- helper for PTRACE_TRACEME
- *
- * Performs checks and sets PT_PTRACED.
- * Should be used by all ptrace implementations for PTRACE_TRACEME.
+/*
+ * We're called with tasklist_lock held for reading.
+ * If we return -ECHILD or zero, next_thread(tsk) must still be valid to use.
+ * If we return another error code, or a successful PID value, we
+ * release tasklist_lock first.
*/
-int ptrace_traceme(void)
+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)
{
- int ret = -EPERM;
+ struct ptrace_state *state;
+ struct task_struct *p;
+ int err = -ECHILD;
+ int why, status;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(state, &tsk->ptracees, entry) {
+ p = state->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;
+ if (delay_group_leader(p))
+ continue;
+ goto found;
+ }
+ // XXX should handle WCONTINUED
+ }
+ rcu_read_unlock();
+ return err;
+
+found:
+ rcu_read_unlock();
+
+ BUG_ON(state->parent != tsk);
+
+ if (p->exit_state) {
+ if (unlikely(p->parent == state->parent))
+ /*
+ * This is our natural child we were ptracing.
+ * When it dies it detaches (see ptrace_report_death).
+ * So we're seeing it here in a race. When it
+ * finishes detaching it will become reapable in
+ * the normal wait_task_zombie path instead.
+ */
+ return 0;
+ 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;
+ }
/*
- * Are we already being traced?
+ * At this point we are committed to a successful return
+ * or a user error return. Release the tasklist_lock.
*/
- task_lock(current);
- if (!(current->ptrace & PT_PTRACED)) {
- ret = security_ptrace(current->parent, current);
+ read_unlock(&tasklist_lock);
+
+ 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) {
+ struct utrace *utrace;
+
+ err = p->pid;
+
/*
- * Set the ptrace bit in the process ptrace flags.
+ * If this was a non-death report, the child might now be
+ * detaching on death in the same race possible in the
+ * p->exit_state check above. So check for p->utrace being
+ * NULL, then we don't need to update the state any more.
*/
- if (!ret)
- current->ptrace |= PT_PTRACED;
+ rcu_read_lock();
+ utrace = rcu_dereference(p->utrace);
+ if (likely(utrace != NULL)) {
+ utrace_lock(utrace);
+ if (unlikely(state->u.live.reported))
+ /*
+ * Another thread in the group got here
+ * first and reaped it before we locked.
+ */
+ err = -ERESTARTNOINTR;
+ state->u.live.reported = 1;
+ utrace_unlock(utrace);
+ }
+ rcu_read_unlock();
+
+ if (err > 0 && why != CLD_TRAPPED)
+ ptrace_detach(p, state->engine);
}
- task_unlock(current);
- return ret;
+
+ return err;
}
-/**
- * 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 void
+do_notify(struct task_struct *tsk, struct task_struct *parent, int why)
{
- struct task_struct *child;
+ struct siginfo info;
+ unsigned long flags;
+ struct sighand_struct *sighand;
+ int sa_mask;
+
+ 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);
+
+ sa_mask = SA_NOCLDSTOP;
+ info.si_code = why;
+ info.si_status = tsk->exit_code & 0x7f;
+ if (why == CLD_CONTINUED)
+ info.si_status = SIGCONT;
+ else if (why == CLD_STOPPED)
+ info.si_status = tsk->signal->group_exit_code & 0x7f;
+ else if (why == CLD_EXITED) {
+ sa_mask = SA_NOCLDWAIT;
+ 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_mask))
+ __group_send_sig_info(SIGCHLD, &info, parent);
/*
- * Tracing init is not allowed.
+ * Even if SIGCHLD is not generated, we must wake up wait4 calls.
*/
- if (pid == 1)
- return ERR_PTR(-EPERM);
-
- read_lock(&tasklist_lock);
- child = find_task_by_pid(pid);
- if (child)
- get_task_struct(child);
- read_unlock(&tasklist_lock);
- if (!child)
- return ERR_PTR(-ESRCH);
- return child;
+ wake_up_interruptible_sync(&parent->signal->wait_chldexit);
+ spin_unlock_irqrestore(&sighand->siglock, flags);
}
-#ifndef __ARCH_SYS_PTRACE
-asmlinkage long sys_ptrace(long request, long pid, long addr, long data)
+static u32
+ptrace_report(struct utrace_attached_engine *engine, struct task_struct *tsk,
+ int code)
{
- struct task_struct *child;
- long ret;
+ struct ptrace_state *state = (struct ptrace_state *) engine->data;
+ const struct utrace_regset *regset;
+
+#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->parent->pid, state->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
+
+ BUG_ON(state->u.live.stopped);
+
+ /*
+ * Set our QUIESCE flag right now, before notifying the tracer.
+ * We do this before setting state->u.live.stopped rather than
+ * by using UTRACE_ACTION_NEWSTATE in our return value, to
+ * ensure that the tracer can't get the notification and then
+ * try to resume us with PTRACE_CONT before we set the flag.
+ */
+ utrace_set_flags(tsk, engine, engine->flags | UTRACE_ACTION_QUIESCE);
/*
- * This lock_kernel fixes a subtle race with suid exec
+ * If regset 0 has a writeback call, do it now. On register window
+ * machines, this makes sure the user memory backing the register
+ * data is up to date by the time wait_task_inactive returns to
+ * ptrace_start in our tracer doing a PTRACE_PEEKDATA or the like.
*/
- lock_kernel();
- if (request == PTRACE_TRACEME) {
- ret = ptrace_traceme();
+ regset = utrace_regset(tsk, engine, utrace_native_view(tsk), 0);
+ if (regset->writeback)
+ (*regset->writeback)(tsk, regset, 0);
+
+ state->u.live.stopped = 1;
+ state->u.live.reported = 0;
+ tsk->exit_code = code;
+ do_notify(tsk, state->parent, CLD_TRAPPED);
+
+#ifdef PTRACE_DEBUG
+ printk("%d ptrace_report quiescing exit_code %x\n",
+ current->pid, current->exit_code);
+#endif
+
+ return UTRACE_ACTION_RESUME;
+}
+
+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->parent) {
+ /*
+ * This is a natural child, so we detach and let the normal
+ * reporting happen once our NOREAP action is gone. But
+ * first, generate a SIGCHLD for those cases where normal
+ * behavior won't. A ptrace'd child always generates SIGCHLD.
+ */
+ if (tsk->exit_signal == -1 || !thread_group_empty(tsk))
+ do_notify(tsk, state->parent, CLD_EXITED);
+ ptrace_state_unlink(state);
+ rcu_assign_pointer(engine->data, 0UL);
+ ptrace_done(state);
+ return UTRACE_ACTION_DETACH;
+ }
+
+ state->u.live.reported = 0;
+ do_notify(tsk, state->parent, CLD_EXITED);
+ return UTRACE_ACTION_RESUME;
+}
+
+/*
+ * We get this only in the case where our UTRACE_ACTION_NOREAP was ignored.
+ * That happens solely when a non-leader exec reaps the old leader.
+ */
+static void
+ptrace_report_reap(struct utrace_attached_engine *engine,
+ struct task_struct *tsk)
+{
+ struct ptrace_state *state;
+ rcu_read_lock();
+ state = rcu_dereference((struct ptrace_state *) engine->data);
+ if (state != NULL) {
+ ptrace_state_unlink(state);
+ rcu_assign_pointer(engine->data, 0UL);
+ ptrace_done(state);
+ }
+ rcu_read_unlock();
+}
+
+
+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;
}
- child = ptrace_get_task_struct(pid);
- if (IS_ERR(child)) {
- ret = PTR_ERR(child);
+ 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))) {
+ BUG_ON(PTR_ERR(child_engine) != -ENOMEM);
+ printk(KERN_ERR
+ "ptrace out of memory, lost child %d of %d",
+ child->pid, parent->pid);
+ }
+ else {
+ int ret = ptrace_setup(child, child_engine,
+ state->parent,
+ state->u.live.options,
+ state->u.live.cap_sys_ptrace);
+ if (unlikely(ret != 0)) {
+ BUG_ON(ret != -ENOMEM);
+ printk(KERN_ERR
+ "ptrace out of memory, lost child %d of %d",
+ child->pid, parent->pid);
+ 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 (request == PTRACE_ATTACH) {
- ret = ptrace_attach(child);
- goto out_put_task_struct;
+ 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);
}
- ret = ptrace_check_attach(child, request == PTRACE_KILL);
- if (ret < 0)
- goto out_put_task_struct;
+out:
+ return UTRACE_ACTION_RESUME;
+}
- ret = arch_ptrace(child, request, addr, data);
- if (ret < 0)
- goto out_put_task_struct;
- out_put_task_struct:
- put_task_struct(child);
- out:
- unlock_kernel();
- return ret;
+static u32
+ptrace_report_vfork_done(struct utrace_attached_engine *engine,
+ struct task_struct *parent, pid_t child_pid)
+{
+ struct ptrace_state *state = (struct ptrace_state *) engine->data;
+ state->u.live.have_eventmsg = 1;
+ state->u.live.u.eventmsg = child_pid;
+ 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)
+{
+ struct ptrace_state *state = (struct ptrace_state *) engine->data;
+ do_notify(tsk, state->parent, type);
+ return UTRACE_JCTL_NOSIGCHLD;
}
-#endif /* __ARCH_SYS_PTRACE */
+
+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,
+ int entry)
+{
+ struct ptrace_state *state = (struct ptrace_state *) engine->data;
+#ifdef PTRACE_SYSEMU
+ if (entry && 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_syscall_entry(struct utrace_attached_engine *engine,
+ struct task_struct *tsk, struct pt_regs *regs)
+{
+ return ptrace_report_syscall(engine, tsk, regs, 1);
+}
+
+static u32
+ptrace_report_syscall_exit(struct utrace_attached_engine *engine,
+ struct task_struct *tsk, struct pt_regs *regs)
+{
+ return ptrace_report_syscall(engine, tsk, regs, 0);
+}
+
+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 int
+ptrace_unsafe_exec(struct utrace_attached_engine *engine,
+ struct task_struct *tsk)
+{
+ struct ptrace_state *state = (struct ptrace_state *) engine->data;
+ int unsafe = LSM_UNSAFE_PTRACE;
+ if (state->u.live.cap_sys_ptrace)
+ unsafe = LSM_UNSAFE_PTRACE_CAP;
+ return unsafe;
+}
+
+static struct task_struct *
+ptrace_tracer_task(struct utrace_attached_engine *engine,
+ struct task_struct *target)
+{
+ struct ptrace_state *state;
+
+ /*
+ * This call is not necessarily made by the target task,
+ * so ptrace might be getting detached while we run here.
+ * The state pointer will be NULL if that happens.
+ */
+ state = rcu_dereference((struct ptrace_state *) engine->data);
+
+ return state == NULL ? NULL : state->parent;
+}
+
+static int
+ptrace_allow_access_process_vm(struct utrace_attached_engine *engine,
+ struct task_struct *target,
+ struct task_struct *caller)
+{
+ struct ptrace_state *state;
+ int ours;
+
+ /*
+ * This call is not necessarily made by the target task,
+ * so ptrace might be getting detached while we run here.
+ * The state pointer will be NULL if that happens.
+ */
+ rcu_read_lock();
+ state = rcu_dereference((struct ptrace_state *) engine->data);
+ ours = (state != NULL
+ && ((engine->flags & UTRACE_ACTION_QUIESCE)
+ || (target->state == TASK_STOPPED))
+ && state->parent == caller);
+ rcu_read_unlock();
+
+ return ours && security_ptrace(caller, target) == 0;
+}
+
+
+static const struct utrace_engine_ops ptrace_utrace_ops =
+{
+ .report_syscall_entry = ptrace_report_syscall_entry,
+ .report_syscall_exit = ptrace_report_syscall_exit,
+ .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,
+ .report_reap = ptrace_report_reap,
+ .unsafe_exec = ptrace_unsafe_exec,
+ .tracer_task = ptrace_tracer_task,
+ .allow_access_process_vm = ptrace_allow_access_process_vm,
+};
+
+#endif
--- linux-2.6/kernel/Makefile.utrace-ptrace-compat
+++ linux-2.6/kernel/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_RCU_TORTURE_TEST) += rcutor
obj-$(CONFIG_RELAY) += relay.o
obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o
obj-$(CONFIG_TASKSTATS) += taskstats.o
+obj-$(CONFIG_UTRACE) += utrace.o
ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
--- linux-2.6/fs/binfmt_elf_fdpic.c.utrace-ptrace-compat
+++ linux-2.6/fs/binfmt_elf_fdpic.c
@@ -421,13 +421,6 @@ static int load_elf_fdpic_binary(struct
entryaddr = interp_params.entry_addr ?: exec_params.entry_addr;
start_thread(regs, entryaddr, current->mm->start_stack);
- if (unlikely(current->ptrace & PT_PTRACED)) {
- if (current->ptrace & PT_TRACE_EXEC)
- ptrace_notify((PTRACE_EVENT_EXEC << 8) | SIGTRAP);
- else
- send_sig(SIGTRAP, current, 0);
- }
-
retval = 0;
error:
--- linux-2.6/fs/binfmt_som.c.utrace-ptrace-compat
+++ linux-2.6/fs/binfmt_som.c
@@ -271,8 +271,6 @@ load_som_binary(struct linux_binprm * bp
map_hpux_gateway_page(current,current->mm);
start_thread_som(regs, som_entry, bprm->p);
- if (current->ptrace & PT_PTRACED)
- send_sig(SIGTRAP, current, 0);
return 0;
/* error cleanup */
--- linux-2.6/fs/proc/base.c.utrace-ptrace-compat
+++ linux-2.6/fs/proc/base.c
@@ -67,6 +67,7 @@
#include <linux/mount.h>
#include <linux/security.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/seccomp.h>
#include <linux/cpuset.h>
#include <linux/audit.h>
@@ -405,13 +406,6 @@ static int proc_root_link(struct inode *
return result;
}
-#define MAY_PTRACE(task) \
- (task == current || \
- (task->parent == current && \
- (task->ptrace & PT_PTRACED) && \
- (task->state == TASK_STOPPED || task->state == TASK_TRACED) && \
- security_ptrace(current,task) == 0))
-
static int proc_pid_environ(struct task_struct *task, char * buffer)
{
int res = 0;
@@ -736,7 +730,8 @@ static ssize_t mem_read(struct file * fi
if (!task)
goto out_no_task;
- if (!MAY_PTRACE(task) || !ptrace_may_attach(task))
+ if (!tracehook_allow_access_process_vm(task)
+ || !ptrace_may_attach(task))
goto out;
ret = -ENOMEM;
@@ -762,7 +757,8 @@ static ssize_t mem_read(struct file * fi
this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count;
retval = access_process_vm(task, src, page, this_len, 0);
- if (!retval || !MAY_PTRACE(task) || !ptrace_may_attach(task)) {
+ if (!retval || !tracehook_allow_access_process_vm(task)
+ || !ptrace_may_attach(task)) {
if (!ret)
ret = -EIO;
break;
@@ -806,7 +802,8 @@ static ssize_t mem_write(struct file * f
if (!task)
goto out_no_task;
- if (!MAY_PTRACE(task) || !ptrace_may_attach(task))
+ if (!tracehook_allow_access_process_vm(task)
+ || !ptrace_may_attach(task))
goto out;
copied = -ENOMEM;
--- linux-2.6/fs/proc/array.c.utrace-ptrace-compat
+++ linux-2.6/fs/proc/array.c
@@ -73,6 +73,7 @@
#include <linux/file.h>
#include <linux/times.h>
#include <linux/cpuset.h>
+#include <linux/tracehook.h>
#include <linux/rcupdate.h>
#include <linux/delayacct.h>
@@ -158,10 +159,17 @@ static inline const char * get_task_stat
static inline char * task_state(struct task_struct *p, char *buffer)
{
+ struct task_struct *tracer;
+ pid_t tracer_pid;
struct group_info *group_info;
int g;
struct fdtable *fdt = NULL;
+ rcu_read_lock();
+ tracer = tracehook_tracer_task(p);
+ tracer_pid = tracer == NULL ? 0 : tracer->pid;
+ rcu_read_unlock();
+
read_lock(&tasklist_lock);
buffer += sprintf(buffer,
"State:\t%s\n"
@@ -175,8 +183,8 @@ static inline char * task_state(struct t
get_task_state(p),
(p->sleep_avg/1024)*100/(1020000000/1024),
p->tgid,
- p->pid, pid_alive(p) ? p->group_leader->real_parent->tgid : 0,
- pid_alive(p) && p->ptrace ? p->parent->pid : 0,
+ p->pid, pid_alive(p) ? p->group_leader->parent->tgid : 0,
+ tracer_pid,
p->uid, p->euid, p->suid, p->fsuid,
p->gid, p->egid, p->sgid, p->fsgid);
read_unlock(&tasklist_lock);
@@ -386,7 +394,7 @@ static int do_task_stat(struct task_stru
stime = cputime_add(stime, task->signal->stime);
}
}
- ppid = pid_alive(task) ? task->group_leader->real_parent->tgid : 0;
+ ppid = pid_alive(task) ? task->group_leader->parent->tgid : 0;
read_unlock(&tasklist_lock);
if (!whole || num_threads<2)
--- linux-2.6/fs/binfmt_aout.c.utrace-ptrace-compat
+++ linux-2.6/fs/binfmt_aout.c
@@ -445,12 +445,6 @@ beyond_if:
regs->gp = ex.a_gpvalue;
#endif
start_thread(regs, ex.a_entry, current->mm->start_stack);
- if (unlikely(current->ptrace & PT_PTRACED)) {
- if (current->ptrace & PT_TRACE_EXEC)
- ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP);
- else
- send_sig(SIGTRAP, current, 0);
- }
return 0;
}
--- linux-2.6/fs/binfmt_flat.c.utrace-ptrace-compat
+++ linux-2.6/fs/binfmt_flat.c
@@ -897,9 +897,6 @@ static int load_flat_binary(struct linux
start_thread(regs, start_addr, current->mm->start_stack);
- if (current->ptrace & PT_PTRACED)
- send_sig(SIGTRAP, current, 0);
-
return 0;
}
--- linux-2.6/fs/binfmt_elf.c.utrace-ptrace-compat
+++ linux-2.6/fs/binfmt_elf.c
@@ -1015,12 +1015,6 @@ static int load_elf_binary(struct linux_
#endif
start_thread(regs, elf_entry, bprm->p);
- if (unlikely(current->ptrace & PT_PTRACED)) {
- if (current->ptrace & PT_TRACE_EXEC)
- ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP);
- else
- send_sig(SIGTRAP, current, 0);
- }
retval = 0;
out:
kfree(loc);
--- linux-2.6/fs/exec.c.utrace-ptrace-compat
+++ linux-2.6/fs/exec.c
@@ -41,7 +41,7 @@
#include <linux/module.h>
#include <linux/namei.h>
#include <linux/proc_fs.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/mount.h>
#include <linux/security.h>
#include <linux/syscalls.h>
@@ -958,13 +958,7 @@ EXPORT_SYMBOL(prepare_binprm);
static int unsafe_exec(struct task_struct *p)
{
- int unsafe = 0;
- if (p->ptrace & PT_PTRACED) {
- if (p->ptrace & PT_PTRACE_CAP)
- unsafe |= LSM_UNSAFE_PTRACE_CAP;
- else
- unsafe |= LSM_UNSAFE_PTRACE;
- }
+ int unsafe = tracehook_unsafe_exec(p);
if (atomic_read(&p->fs->count) > 1 ||
atomic_read(&p->files->count) > 1 ||
atomic_read(&p->sighand->count) > 1)
@@ -1089,6 +1083,7 @@ int search_binary_handler(struct linux_b
bprm->file = NULL;
current->did_exec = 1;
proc_exec_connector(current);
+ tracehook_report_exec(bprm, regs);
return retval;
}
read_lock(&binfmt_lock);
--- linux-2.6/drivers/connector/cn_proc.c.utrace-ptrace-compat
+++ linux-2.6/drivers/connector/cn_proc.c
@@ -62,8 +62,8 @@ void proc_fork_connector(struct task_str
ktime_get_ts(&ts); /* get high res monotonic timestamp */
ev->timestamp_ns = timespec_to_ns(&ts);
ev->what = PROC_EVENT_FORK;
- ev->event_data.fork.parent_pid = task->real_parent->pid;
- ev->event_data.fork.parent_tgid = task->real_parent->tgid;
+ ev->event_data.fork.parent_pid = task->parent->pid;
+ ev->event_data.fork.parent_tgid = task->parent->tgid;
ev->event_data.fork.child_pid = task->pid;
ev->event_data.fork.child_tgid = task->tgid;
--- linux-2.6/init/Kconfig.utrace-ptrace-compat
+++ linux-2.6/init/Kconfig
@@ -511,6 +511,35 @@ config STOP_MACHINE
Need stop_machine() primitive.
endmenu
+menu "Process debugging support"
+
+config UTRACE
+ bool "Infrastructure for tracing and debugging user processes"
+ default y
+ help
+ Enable the utrace process tracing interface.
+ This is an internal kernel interface to track events in user
+ threads, extract and change user thread state. This interface
+ is exported to kernel modules, and is also used to implement ptrace.
+ If you disable this, no facilities for debugging user processes
+ will be available, nor the facilities used by UML and other
+ 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"
source "block/Kconfig"
endmenu
--- linux-2.6/arch/alpha/kernel/entry.S.utrace-ptrace-compat
+++ linux-2.6/arch/alpha/kernel/entry.S
@@ -879,14 +879,14 @@ sys_getxpid:
/* See linux/kernel/timer.c sys_getppid for discussion
about this loop. */
ldq $3, TASK_GROUP_LEADER($2)
- ldq $4, TASK_REAL_PARENT($3)
+ ldq $4, TASK_PARENT($3)
ldl $0, TASK_TGID($2)
1: ldl $1, TASK_TGID($4)
#ifdef CONFIG_SMP
mov $4, $5
mb
ldq $3, TASK_GROUP_LEADER($2)
- ldq $4, TASK_REAL_PARENT($3)
+ ldq $4, TASK_PARENT($3)
cmpeq $4, $5, $5
beq $5, 1b
#endif
--- linux-2.6/arch/alpha/kernel/asm-offsets.c.utrace-ptrace-compat
+++ linux-2.6/arch/alpha/kernel/asm-offsets.c
@@ -27,7 +27,7 @@ void foo(void)
DEFINE(TASK_EUID, offsetof(struct task_struct, euid));
DEFINE(TASK_GID, offsetof(struct task_struct, gid));
DEFINE(TASK_EGID, offsetof(struct task_struct, egid));
- DEFINE(TASK_REAL_PARENT, offsetof(struct task_struct, real_parent));
+ DEFINE(TASK_PARENT, offsetof(struct task_struct, parent));
DEFINE(TASK_GROUP_LEADER, offsetof(struct task_struct, group_leader));
DEFINE(TASK_TGID, offsetof(struct task_struct, tgid));
BLANK();
--- linux-2.6/arch/i386/kernel/i387.c.utrace-ptrace-compat
+++ linux-2.6/arch/i386/kernel/i387.c
@@ -222,14 +222,10 @@ void set_fpu_twd( struct task_struct *ts
* FXSR floating point environment conversions.
*/
-static int convert_fxsr_to_user( struct _fpstate __user *buf,
- struct i387_fxsave_struct *fxsave )
+static inline void
+convert_fxsr_env_to_i387(unsigned long env[7],
+ struct i387_fxsave_struct *fxsave)
{
- unsigned long env[7];
- struct _fpreg __user *to;
- struct _fpxreg *from;
- int i;
-
env[0] = (unsigned long)fxsave->cwd | 0xffff0000ul;
env[1] = (unsigned long)fxsave->swd | 0xffff0000ul;
env[2] = twd_fxsr_to_i387(fxsave);
@@ -237,7 +233,17 @@ static int convert_fxsr_to_user( struct
env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16);
env[5] = fxsave->foo;
env[6] = fxsave->fos;
+}
+
+static int convert_fxsr_to_user(struct _fpstate __user *buf,
+ struct i387_fxsave_struct *fxsave)
+{
+ unsigned long env[7];
+ struct _fpreg __user *to;
+ struct _fpxreg *from;
+ int i;
+ convert_fxsr_env_to_i387(env, fxsave);
if ( __copy_to_user( buf, env, 7 * sizeof(unsigned long) ) )
return 1;
@@ -255,6 +261,20 @@ static int convert_fxsr_to_user( struct
return 0;
}
+static inline void
+convert_fxsr_env_from_i387(struct i387_fxsave_struct *fxsave,
+ const unsigned long env[7])
+{
+ fxsave->cwd = (unsigned short)(env[0] & 0xffff);
+ fxsave->swd = (unsigned short)(env[1] & 0xffff);
+ fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff));
+ fxsave->fip = env[3];
+ fxsave->fop = (unsigned short)((env[4] & 0xffff0000ul) >> 16);
+ fxsave->fcs = (env[4] & 0xffff);
+ fxsave->foo = env[5];
+ fxsave->fos = env[6];
+}
+
static int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave,
struct _fpstate __user *buf )
{
@@ -266,14 +286,7 @@ static int convert_fxsr_from_user( struc
if ( __copy_from_user( env, buf, 7 * sizeof(long) ) )
return 1;
- fxsave->cwd = (unsigned short)(env[0] & 0xffff);
- fxsave->swd = (unsigned short)(env[1] & 0xffff);
- fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff));
- fxsave->fip = env[3];
- fxsave->fop = (unsigned short)((env[4] & 0xffff0000ul) >> 16);
- fxsave->fcs = (env[4] & 0xffff);
- fxsave->foo = env[5];
- fxsave->fos = env[6];
+ convert_fxsr_env_from_i387(fxsave, env);
to = (struct _fpxreg *) &fxsave->st_space[0];
from = &buf->_st[0];
@@ -388,88 +401,82 @@ int restore_i387( struct _fpstate __user
* ptrace request handlers.
*/
-static inline int get_fpregs_fsave( struct user_i387_struct __user *buf,
- struct task_struct *tsk )
+static inline void get_fpregs_fsave(struct user_i387_struct *buf,
+ struct task_struct *tsk)
{
- return __copy_to_user( buf, &tsk->thread.i387.fsave,
- sizeof(struct user_i387_struct) );
+ memcpy(buf, &tsk->thread.i387.fsave, sizeof(struct user_i387_struct));
}
-static inline int get_fpregs_fxsave( struct user_i387_struct __user *buf,
- struct task_struct *tsk )
+static inline void get_fpregs_fxsave(struct user_i387_struct *buf,
+ struct task_struct *tsk)
{
- return convert_fxsr_to_user( (struct _fpstate __user *)buf,
- &tsk->thread.i387.fxsave );
+ struct _fpreg *to;
+ const struct _fpxreg *from;
+ unsigned int i;
+
+ convert_fxsr_env_to_i387((unsigned long *) buf,
+ &tsk->thread.i387.fxsave);
+
+ to = (struct _fpreg *) buf->st_space;
+ from = (const struct _fpxreg *) &tsk->thread.i387.fxsave.st_space[0];
+ for (i = 0; i < 8; i++, to++, from++)
+ *to = *(const struct _fpreg *) from;
}
-int get_fpregs( struct user_i387_struct __user *buf, struct task_struct *tsk )
+int get_fpregs(struct user_i387_struct *buf, struct task_struct *tsk)
{
if ( HAVE_HWFP ) {
- if ( cpu_has_fxsr ) {
- return get_fpregs_fxsave( buf, tsk );
- } else {
- return get_fpregs_fsave( buf, tsk );
- }
+ if (cpu_has_fxsr)
+ get_fpregs_fxsave(buf, tsk);
+ else
+ get_fpregs_fsave(buf, tsk);
+ return 0;
} else {
return save_i387_soft( &tsk->thread.i387.soft,
(struct _fpstate __user *)buf );
}
}
-static inline int set_fpregs_fsave( struct task_struct *tsk,
- struct user_i387_struct __user *buf )
+static inline void set_fpregs_fsave(struct task_struct *tsk,
+ const struct user_i387_struct *buf)
{
- return __copy_from_user( &tsk->thread.i387.fsave, buf,
- sizeof(struct user_i387_struct) );
+ memcpy(&tsk->thread.i387.fsave, buf, sizeof(struct user_i387_struct));
}
-static inline int set_fpregs_fxsave( struct task_struct *tsk,
- struct user_i387_struct __user *buf )
+static inline void set_fpregs_fxsave(struct task_struct *tsk,
+ const struct user_i387_struct *buf)
{
- return convert_fxsr_from_user( &tsk->thread.i387.fxsave,
- (struct _fpstate __user *)buf );
+ struct _fpxreg *to;
+ const struct _fpreg *from;
+ unsigned int i;
+
+ convert_fxsr_env_from_i387(&tsk->thread.i387.fxsave,
+ (unsigned long *) buf);
+
+ to = (struct _fpxreg *) &tsk->thread.i387.fxsave.st_space[0];
+ from = (const struct _fpreg *) buf->st_space;
+ for (i = 0; i < 8; i++, to++, from++)
+ *(struct _fpreg *) to = *from;
}
-int set_fpregs( struct task_struct *tsk, struct user_i387_struct __user *buf )
+int set_fpregs(struct task_struct *tsk, const struct user_i387_struct *buf)
{
if ( HAVE_HWFP ) {
- if ( cpu_has_fxsr ) {
- return set_fpregs_fxsave( tsk, buf );
- } else {
- return set_fpregs_fsave( tsk, buf );
- }
+ if (cpu_has_fxsr)
+ set_fpregs_fxsave(tsk, buf);
+ else
+ set_fpregs_fsave(tsk, buf);
+ return 0;
} else {
return restore_i387_soft( &tsk->thread.i387.soft,
(struct _fpstate __user *)buf );
}
}
-int get_fpxregs( struct user_fxsr_struct __user *buf, struct task_struct *tsk )
+void updated_fpxregs(struct task_struct *tsk)
{
- if ( cpu_has_fxsr ) {
- if (__copy_to_user( buf, &tsk->thread.i387.fxsave,
- sizeof(struct user_fxsr_struct) ))
- return -EFAULT;
- return 0;
- } else {
- return -EIO;
- }
-}
-
-int set_fpxregs( struct task_struct *tsk, struct user_fxsr_struct __user *buf )
-{
- int ret = 0;
-
- if ( cpu_has_fxsr ) {
- if (__copy_from_user( &tsk->thread.i387.fxsave, buf,
- sizeof(struct user_fxsr_struct) ))
- ret = -EFAULT;
- /* mxcsr reserved bits must be masked to zero for security reasons */
- tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
- } else {
- ret = -EIO;
- }
- return ret;
+ /* mxcsr reserved bits must be masked to zero for security reasons */
+ tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
}
/*
--- linux-2.6/arch/i386/kernel/signal.c.utrace-ptrace-compat
+++ linux-2.6/arch/i386/kernel/signal.c
@@ -19,7 +19,7 @@
#include <linux/stddef.h>
#include <linux/personality.h>
#include <linux/suspend.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/elf.h>
#include <asm/processor.h>
#include <asm/ucontext.h>
@@ -385,16 +385,6 @@ static int setup_frame(int sig, struct k
regs->xss = __USER_DS;
regs->xcs = __USER_CS;
- /*
- * Clear TF when entering the signal handler, but
- * notify any tracer that was single-stepping it.
- * The tracer may want to single-step inside the
- * handler too.
- */
- regs->eflags &= ~TF_MASK;
- if (test_thread_flag(TIF_SINGLESTEP))
- ptrace_notify(SIGTRAP);
-
#if DEBUG_SIG
printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
current->comm, current->pid, frame, regs->eip, frame->pretcode);
@@ -479,16 +469,6 @@ static int setup_rt_frame(int sig, struc
regs->xss = __USER_DS;
regs->xcs = __USER_CS;
- /*
- * Clear TF when entering the signal handler, but
- * notify any tracer that was single-stepping it.
- * The tracer may want to single-step inside the
- * handler too.
- */
- regs->eflags &= ~TF_MASK;
- if (test_thread_flag(TIF_SINGLESTEP))
- ptrace_notify(SIGTRAP);
-
#if DEBUG_SIG
printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
current->comm, current->pid, frame, regs->eip, frame->pretcode);
@@ -533,14 +513,12 @@ handle_signal(unsigned long sig, siginfo
}
/*
- * If TF is set due to a debugger (PT_DTRACE), clear the TF flag so
+ * If TF is set due to a debugger (TIF_FORCED_TF), clear the TF flag so
* that register information in the sigcontext is correct.
*/
if (unlikely(regs->eflags & TF_MASK)
- && likely(current->ptrace & PT_DTRACE)) {
- current->ptrace &= ~PT_DTRACE;
+ && likely(test_and_clear_thread_flag(TIF_FORCED_TF)))
regs->eflags &= ~TF_MASK;
- }
/* Set up the stack frame */
if (ka->sa.sa_flags & SA_SIGINFO)
@@ -557,6 +535,17 @@ handle_signal(unsigned long sig, siginfo
spin_unlock_irq(¤t->sighand->siglock);
}
+ if (ret) {
+ /*
+ * Clear TF when entering the signal handler, but
+ * notify any tracer that was single-stepping it.
+ * The tracer may want to single-step inside the
+ * handler too.
+ */
+ regs->eflags &= ~TF_MASK;
+ tracehook_report_handle_signal(sig, ka, oldset, regs);
+ }
+
return ret;
}
--- linux-2.6/arch/i386/kernel/vm86.c.utrace-ptrace-compat
+++ linux-2.6/arch/i386/kernel/vm86.c
@@ -529,13 +529,6 @@ int handle_vm86_trap(struct kernel_vm86_
}
if (trapno !=1)
return 1; /* we let this handle by the calling routine */
- if (current->ptrace & PT_PTRACED) {
- unsigned long flags;
- spin_lock_irqsave(¤t->sighand->siglock, flags);
- sigdelset(¤t->blocked, SIGTRAP);
- recalc_sigpending();
- spin_unlock_irqrestore(¤t->sighand->siglock, flags);
- }
send_sig(SIGTRAP, current, 1);
current->thread.trap_no = trapno;
current->thread.error_code = error_code;
--- linux-2.6/arch/i386/kernel/process.c.utrace-ptrace-compat
+++ linux-2.6/arch/i386/kernel/process.c
@@ -751,9 +751,6 @@ asmlinkage int sys_execve(struct pt_regs
(char __user * __user *) regs.edx,
®s);
if (error == 0) {
- task_lock(current);
- current->ptrace &= ~PT_DTRACE;
- task_unlock(current);
/* Make sure we don't return using sysenter.. */
set_thread_flag(TIF_IRET);
}
--- linux-2.6/arch/i386/kernel/ptrace.c.utrace-ptrace-compat
+++ linux-2.6/arch/i386/kernel/ptrace.c
@@ -12,12 +12,15 @@
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/audit.h>
#include <linux/seccomp.h>
#include <linux/signal.h>
+#include <linux/module.h>
+#include <asm/tracehook.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/system.h>
@@ -27,10 +30,6 @@
#include <asm/ldt.h>
#include <asm/desc.h>
-/*
- * does not yet catch signals sent when the child dies.
- * in exit.c or in signal.c.
- */
/*
* Determines which flags the user has access to [1 = access, 0 = no access].
@@ -39,9 +38,6 @@
*/
#define FLAG_MASK 0x00050dd5
-/* set's the trap flag. */
-#define TRAP_FLAG 0x100
-
/*
* Offset of eflags on child stack..
*/
@@ -114,6 +110,7 @@ static int putreg(struct task_struct *ch
case EFL:
value &= FLAG_MASK;
value |= get_stack_long(child, EFL_OFFSET) & ~FLAG_MASK;
+ clear_tsk_thread_flag(child, TIF_FORCED_TF);
break;
}
if (regno > GS*4)
@@ -134,6 +131,10 @@ static unsigned long getreg(struct task_
case GS:
retval = child->thread.gs;
break;
+ case EFL:
+ if (test_tsk_thread_flag(child, TIF_FORCED_TF))
+ retval &= ~X86_EFLAGS_TF;
+ goto fetch;
case DS:
case ES:
case SS:
@@ -141,10 +142,12 @@ static unsigned long getreg(struct task_
retval = 0xffff;
/* fall through */
default:
+ fetch:
if (regno > GS*4)
regno -= 2*4;
regno = regno - sizeof(struct pt_regs);
retval &= get_stack_long(child, regno);
+ break;
}
return retval;
}
@@ -222,7 +225,7 @@ static inline int is_at_popf(struct task
return 0;
}
-static void set_singlestep(struct task_struct *child)
+void tracehook_enable_single_step(struct task_struct *child)
{
struct pt_regs *regs = get_child_regs(child);
@@ -236,11 +239,11 @@ static void set_singlestep(struct task_s
/*
* If TF was already set, don't do anything else
*/
- if (regs->eflags & TRAP_FLAG)
+ if (regs->eflags & X86_EFLAGS_TF)
return;
/* Set TF on the kernel stack.. */
- regs->eflags |= TRAP_FLAG;
+ regs->eflags |= X86_EFLAGS_TF;
/*
* ..but if TF is changed by the instruction we will trace,
@@ -250,43 +253,323 @@ static void set_singlestep(struct task_s
if (is_at_popf(child, regs))
return;
- child->ptrace |= PT_DTRACE;
+ set_tsk_thread_flag(child, TIF_FORCED_TF);
}
-static void clear_singlestep(struct task_struct *child)
+void tracehook_disable_single_step(struct task_struct *child)
{
/* Always clear TIF_SINGLESTEP... */
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
/* But touch TF only if it was set by us.. */
- if (child->ptrace & PT_DTRACE) {
+ if (test_and_clear_tsk_thread_flag(child, TIF_FORCED_TF)) {
struct pt_regs *regs = get_child_regs(child);
- regs->eflags &= ~TRAP_FLAG;
- child->ptrace &= ~PT_DTRACE;
+ regs->eflags &= ~X86_EFLAGS_TF;
}
}
-/*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure the single step bit is not set.
- */
-void ptrace_disable(struct task_struct *child)
+
+static int
+genregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (kbuf) {
+ unsigned long *kp = kbuf;
+ while (count > 0) {
+ *kp++ = getreg(target, pos);
+ pos += 4;
+ count -= 4;
+ }
+ }
+ else {
+ unsigned long __user *up = ubuf;
+ while (count > 0) {
+ if (__put_user(getreg(target, pos), up++))
+ return -EFAULT;
+ pos += 4;
+ count -= 4;
+ }
+ }
+
+ return 0;
+}
+
+static int
+genregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret = 0;
+
+ if (kbuf) {
+ const unsigned long *kp = kbuf;
+ while (!ret && count > 0) {
+ ret = putreg(target, pos, *kp++);
+ pos += 4;
+ count -= 4;
+ }
+ }
+ else {
+ int ret = 0;
+ const unsigned long __user *up = ubuf;
+ while (!ret && count > 0) {
+ unsigned long val;
+ ret = __get_user(val, up++);
+ if (!ret)
+ ret = putreg(target, pos, val);
+ pos += 4;
+ count -= 4;
+ }
+ }
+
+ return ret;
+}
+
+static int
+fpregs_active(struct task_struct *target, const struct utrace_regset *regset)
+{
+ return tsk_used_math(target) ? regset->n : 0;
+}
+
+static int
+fpregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ struct user_i387_struct fp;
+ int ret;
+
+ if (tsk_used_math(target)) {
+ if (target == current)
+ unlazy_fpu(target);
+ }
+ else
+ init_fpu(target);
+
+ ret = get_fpregs(&fp, target);
+ if (ret == 0)
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &fp, 0, -1);
+
+ return ret;
+}
+
+static int
+fpregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct user_i387_struct fp;
+ int ret;
+
+ if (tsk_used_math(target)) {
+ if (target == current)
+ unlazy_fpu(target);
+ }
+ else if (pos == 0 && count == sizeof(fp))
+ set_stopped_child_used_math(target);
+ else
+ init_fpu(target);
+
+ if (pos > 0 || count < sizeof(fp)) {
+ ret = get_fpregs(&fp, target);
+ if (ret == 0)
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &fp, 0, -1);
+ if (ret)
+ return ret;
+ kbuf = &fp;
+ }
+ else if (kbuf == NULL) {
+ if (__copy_from_user(&fp, ubuf, sizeof(fp)))
+ return -EFAULT;
+ kbuf = &fp;
+ }
+
+ return set_fpregs(target, kbuf);
+}
+
+static int
+fpxregs_active(struct task_struct *target, const struct utrace_regset *regset)
+{
+ return !cpu_has_fxsr ? -ENODEV : tsk_used_math(target) ? regset->n : 0;
+}
+
+static int
+fpxregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (!cpu_has_fxsr)
+ return -ENODEV;
+
+ if (tsk_used_math(target))
+ unlazy_fpu(target);
+ else
+ init_fpu(target);
+
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.i387.fxsave, 0, -1);
+}
+
+static int
+fpxregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+
+ if (!cpu_has_fxsr)
+ return -ENODEV;
+
+ if (tsk_used_math(target))
+ unlazy_fpu(target);
+ else if (pos == 0 && count == sizeof(target->thread.i387.fxsave))
+ set_stopped_child_used_math(target);
+ else
+ init_fpu(target);
+
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.i387.fxsave, 0, -1);
+
+ updated_fpxregs(target);
+
+ return ret;
+}
+
+
+static int
+dbregs_active(struct task_struct *tsk, const struct utrace_regset *regset)
+{
+ if (tsk->thread.debugreg[DR_CONTROL] | tsk->thread.debugreg[DR_STATUS])
+ return 8;
+ return 0;
+}
+
+static int
+dbregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
{
- clear_singlestep(child);
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
+ /*
+ * The hardware updates the status register on a debug trap,
+ * but do_debug (traps.c) save it for us when that happens.
+ * So whether the target is current or not, thread.debugreg is good.
+ */
+
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ target->thread.debugreg, 0, -1);
+}
+
+static int
+dbregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ for (pos >>= 2, count >>= 2; count > 0; --count, ++pos) {
+ unsigned long val;
+ unsigned int i;
+
+ if (kbuf) {
+ val = *(const unsigned long *) kbuf;
+ kbuf += sizeof(unsigned long);
+ }
+ else {
+ if (__get_user(val, (unsigned long __user *) ubuf))
+ return -EFAULT;
+ ubuf += sizeof(unsigned long);
+ }
+
+ if (pos < 4) {
+ if (val >= TASK_SIZE - 3)
+ return -EIO;
+ goto set;
+ }
+ else if (pos < 6) {
+ if (val != 0)
+ return -EIO;
+ continue;
+ }
+ else if (pos < 7)
+ goto set;
+
+ /* Sanity-check data. Take one half-byte at once with
+ * check = (val >> (16 + 4*i)) & 0xf. It contains the
+ * R/Wi and LENi bits; bits 0 and 1 are R/Wi, and bits
+ * 2 and 3 are LENi. Given a list of invalid values,
+ * we do mask |= 1 << invalid_value, so that
+ * (mask >> check) & 1 is a correct test for invalid
+ * values.
+ *
+ * R/Wi contains the type of the breakpoint /
+ * watchpoint, LENi contains the length of the watched
+ * data in the watchpoint case.
+ *
+ * The invalid values are:
+ * - LENi == 0x10 (undefined), so mask |= 0x0f00.
+ * - R/Wi == 0x10 (break on I/O reads or writes), so
+ * mask |= 0x4444.
+ * - R/Wi == 0x00 && LENi != 0x00, so we have mask |=
+ * 0x1110.
+ *
+ * Finally, mask = 0x0f00 | 0x4444 | 0x1110 == 0x5f54.
+ *
+ * See the Intel Manual "System Programming Guide",
+ * 15.2.4
+ *
+ * Note that LENi == 0x10 is defined on x86_64 in long
+ * mode (i.e. even for 32-bit userspace software, but
+ * 64-bit kernel), so the x86_64 mask value is 0x5454.
+ * See the AMD manual no. 24593 (AMD64 System
+ * Programming)*/
+ val &= ~DR_CONTROL_RESERVED;
+ for (i = 0; i < 4; i++)
+ if ((0x5f54 >> ((val >> (16 + 4*i)) & 0xf)) & 1)
+ return -EIO;
+ if (val)
+ set_tsk_thread_flag(target, TIF_DEBUG);
+ else
+ clear_tsk_thread_flag(target, TIF_DEBUG);
+
+ set:
+ target->thread.debugreg[pos] = val;
+ if (target == current)
+ switch (pos) {
+#define DBREG(n) case n: set_debugreg(target->thread.debugreg[n], n); break
+ DBREG(0);
+ DBREG(1);
+ DBREG(2);
+ DBREG(3);
+ DBREG(6);
+ DBREG(7);
+#undef DBREG
+ }
+ }
+
+ return 0;
}
+
/*
* Perform get_thread_area on behalf of the traced child.
*/
static int
-ptrace_get_thread_area(struct task_struct *child,
- int idx, struct user_desc __user *user_desc)
+tls_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
{
- struct user_desc info;
- struct desc_struct *desc;
+ struct user_desc info, *ip;
+ const struct desc_struct *desc;
/*
* Get the current Thread-Local Storage area:
@@ -308,23 +591,29 @@ ptrace_get_thread_area(struct task_struc
#define GET_PRESENT(desc) (((desc)->b >> 15) & 1)
#define GET_USEABLE(desc) (((desc)->b >> 20) & 1)
- if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
- return -EINVAL;
-
- desc = child->thread.tls_array + idx - GDT_ENTRY_TLS_MIN;
-
- info.entry_number = idx;
- info.base_addr = GET_BASE(desc);
- info.limit = GET_LIMIT(desc);
- info.seg_32bit = GET_32BIT(desc);
- info.contents = GET_CONTENTS(desc);
- info.read_exec_only = !GET_WRITABLE(desc);
- info.limit_in_pages = GET_LIMIT_PAGES(desc);
- info.seg_not_present = !GET_PRESENT(desc);
- info.useable = GET_USEABLE(desc);
-
- if (copy_to_user(user_desc, &info, sizeof(info)))
- return -EFAULT;
+ desc = &target->thread.tls_array[pos / sizeof(struct user_desc)];
+ ip = kbuf ?: &info;
+ memset(ip, 0, sizeof *ip);
+ for (; count > 0; count -= sizeof(struct user_desc), ++desc) {
+ ip->entry_number = (desc - &target->thread.tls_array[0]
+ + GDT_ENTRY_TLS_MIN);
+ ip->base_addr = GET_BASE(desc);
+ ip->limit = GET_LIMIT(desc);
+ ip->seg_32bit = GET_32BIT(desc);
+ ip->contents = GET_CONTENTS(desc);
+ ip->read_exec_only = !GET_WRITABLE(desc);
+ ip->limit_in_pages = GET_LIMIT_PAGES(desc);
+ ip->seg_not_present = !GET_PRESENT(desc);
+ ip->useable = GET_USEABLE(desc);
+
+ if (kbuf)
+ ++ip;
+ else {
+ if (__copy_to_user(ubuf, &info, sizeof(info)))
+ return -EFAULT;
+ ubuf += sizeof(info);
+ }
+ }
return 0;
}
@@ -333,308 +622,154 @@ ptrace_get_thread_area(struct task_struc
* Perform set_thread_area on behalf of the traced child.
*/
static int
-ptrace_set_thread_area(struct task_struct *child,
- int idx, struct user_desc __user *user_desc)
+tls_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
{
struct user_desc info;
struct desc_struct *desc;
-
- if (copy_from_user(&info, user_desc, sizeof(info)))
- return -EFAULT;
-
- if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
- return -EINVAL;
-
- desc = child->thread.tls_array + idx - GDT_ENTRY_TLS_MIN;
- if (LDT_empty(&info)) {
- desc->a = 0;
- desc->b = 0;
- } else {
- desc->a = LDT_entry_a(&info);
- desc->b = LDT_entry_b(&info);
+ struct desc_struct newtls[GDT_ENTRY_TLS_ENTRIES];
+ unsigned int i;
+ int cpu;
+
+ pos /= sizeof(struct user_desc);
+ count /= sizeof(struct user_desc);
+
+ desc = newtls;
+ for (i = 0; i < count; ++i, ++desc) {
+ const struct user_desc *ip;
+ if (kbuf) {
+ ip = kbuf;
+ kbuf += sizeof(struct user_desc);
+ }
+ else {
+ ip = &info;
+ if (__copy_from_user(&info, ubuf, sizeof(info)))
+ return -EFAULT;
+ ubuf += sizeof(struct user_desc);
+ }
+
+ if (LDT_empty(ip)) {
+ desc->a = 0;
+ desc->b = 0;
+ } else {
+ desc->a = LDT_entry_a(ip);
+ desc->b = LDT_entry_b(ip);
+ }
}
+ /*
+ * We must not get preempted while modifying the TLS.
+ */
+ cpu = get_cpu();
+ memcpy(&target->thread.tls_array[pos], newtls,
+ count * sizeof(newtls[0]));
+ if (target == current)
+ load_TLS(&target->thread, cpu);
+ put_cpu();
+
return 0;
}
-long arch_ptrace(struct task_struct *child, long request, long addr, long data)
-{
- struct user * dummy = NULL;
- int i, ret;
- unsigned long __user *datap = (unsigned long __user *)data;
-
- switch (request) {
- /* when I and D space are separate, these will need to be fixed. */
- 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, datap);
- break;
- }
-
- /* read the word at location addr in the USER area. */
- case PTRACE_PEEKUSR: {
- unsigned long tmp;
-
- ret = -EIO;
- if ((addr & 3) || addr < 0 ||
- addr > sizeof(struct user) - 3)
- break;
-
- tmp = 0; /* Default return condition */
- if(addr < FRAME_SIZE*sizeof(long))
- tmp = getreg(child, addr);
- if(addr >= (long) &dummy->u_debugreg[0] &&
- addr <= (long) &dummy->u_debugreg[7]){
- addr -= (long) &dummy->u_debugreg[0];
- addr = addr >> 2;
- tmp = child->thread.debugreg[addr];
- }
- ret = put_user(tmp, datap);
- break;
- }
-
- /* when I and D space are separate, this will have to be fixed. */
- 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_POKEUSR: /* write the word at location addr in the USER area */
- ret = -EIO;
- if ((addr & 3) || addr < 0 ||
- addr > sizeof(struct user) - 3)
- break;
-
- if (addr < FRAME_SIZE*sizeof(long)) {
- ret = putreg(child, addr, data);
- break;
- }
- /* We need to be very careful here. We implicitly
- want to modify a portion of the task_struct, and we
- have to be selective about what portions we allow someone
- to modify. */
-
- ret = -EIO;
- if(addr >= (long) &dummy->u_debugreg[0] &&
- addr <= (long) &dummy->u_debugreg[7]){
-
- if(addr == (long) &dummy->u_debugreg[4]) break;
- if(addr == (long) &dummy->u_debugreg[5]) break;
- if(addr < (long) &dummy->u_debugreg[4] &&
- ((unsigned long) data) >= TASK_SIZE-3) break;
-
- /* Sanity-check data. Take one half-byte at once with
- * check = (val >> (16 + 4*i)) & 0xf. It contains the
- * R/Wi and LENi bits; bits 0 and 1 are R/Wi, and bits
- * 2 and 3 are LENi. Given a list of invalid values,
- * we do mask |= 1 << invalid_value, so that
- * (mask >> check) & 1 is a correct test for invalid
- * values.
- *
- * R/Wi contains the type of the breakpoint /
- * watchpoint, LENi contains the length of the watched
- * data in the watchpoint case.
- *
- * The invalid values are:
- * - LENi == 0x10 (undefined), so mask |= 0x0f00.
- * - R/Wi == 0x10 (break on I/O reads or writes), so
- * mask |= 0x4444.
- * - R/Wi == 0x00 && LENi != 0x00, so we have mask |=
- * 0x1110.
- *
- * Finally, mask = 0x0f00 | 0x4444 | 0x1110 == 0x5f54.
- *
- * See the Intel Manual "System Programming Guide",
- * 15.2.4
- *
- * Note that LENi == 0x10 is defined on x86_64 in long
- * mode (i.e. even for 32-bit userspace software, but
- * 64-bit kernel), so the x86_64 mask value is 0x5454.
- * See the AMD manual no. 24593 (AMD64 System
- * Programming)*/
-
- if(addr == (long) &dummy->u_debugreg[7]) {
- data &= ~DR_CONTROL_RESERVED;
- for(i=0; i<4; i++)
- if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
- goto out_tsk;
- if (data)
- set_tsk_thread_flag(child, TIF_DEBUG);
- else
- clear_tsk_thread_flag(child, TIF_DEBUG);
- }
- addr -= (long) &dummy->u_debugreg;
- addr = addr >> 2;
- child->thread.debugreg[addr] = data;
- ret = 0;
- }
- break;
-
- case PTRACE_SYSEMU: /* continue and stop at next syscall, which will not be executed */
- case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
- case PTRACE_CONT: /* restart after signal. */
- ret = -EIO;
- if (!valid_signal(data))
- break;
- if (request == PTRACE_SYSEMU) {
- set_tsk_thread_flag(child, TIF_SYSCALL_EMU);
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- } else if (request == PTRACE_SYSCALL) {
- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
- } else {
- clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- }
- child->exit_code = data;
- /* make sure the single step bit is not set. */
- clear_singlestep(child);
- wake_up_process(child);
- ret = 0;
- break;
/*
- * make the child exit. Best I can do is send it a sigkill.
- * perhaps it should be put in the status that it wants to
- * exit.
+ * Determine how many TLS slots are in use.
*/
- case PTRACE_KILL:
- ret = 0;
- if (child->exit_state == EXIT_ZOMBIE) /* already dead */
- break;
- child->exit_code = SIGKILL;
- /* make sure the single step bit is not set. */
- clear_singlestep(child);
- wake_up_process(child);
- break;
-
- case PTRACE_SYSEMU_SINGLESTEP: /* Same as SYSEMU, but singlestep if not syscall */
- case PTRACE_SINGLESTEP: /* set the trap flag. */
- ret = -EIO;
- if (!valid_signal(data))
- break;
-
- if (request == PTRACE_SYSEMU_SINGLESTEP)
- set_tsk_thread_flag(child, TIF_SYSCALL_EMU);
- else
- clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
-
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- set_singlestep(child);
- child->exit_code = data;
- /* give it a chance to run. */
- wake_up_process(child);
- ret = 0;
- break;
-
- case PTRACE_DETACH:
- /* detach a process that was attached. */
- ret = ptrace_detach(child, data);
- break;
-
- case PTRACE_GETREGS: { /* Get all gp regs from the child. */
- if (!access_ok(VERIFY_WRITE, datap, FRAME_SIZE*sizeof(long))) {
- ret = -EIO;
- break;
- }
- for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
- __put_user(getreg(child, i), datap);
- datap++;
- }
- ret = 0;
- break;
- }
-
- case PTRACE_SETREGS: { /* Set all gp regs in the child. */
- unsigned long tmp;
- if (!access_ok(VERIFY_READ, datap, FRAME_SIZE*sizeof(long))) {
- ret = -EIO;
- break;
- }
- for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
- __get_user(tmp, datap);
- putreg(child, i, tmp);
- datap++;
- }
- ret = 0;
- break;
- }
-
- case PTRACE_GETFPREGS: { /* Get the child FPU state. */
- if (!access_ok(VERIFY_WRITE, datap,
- sizeof(struct user_i387_struct))) {
- ret = -EIO;
- break;
- }
- ret = 0;
- if (!tsk_used_math(child))
- init_fpu(child);
- get_fpregs((struct user_i387_struct __user *)data, child);
- break;
- }
-
- case PTRACE_SETFPREGS: { /* Set the child FPU state. */
- if (!access_ok(VERIFY_READ, datap,
- sizeof(struct user_i387_struct))) {
- ret = -EIO;
- break;
- }
- set_stopped_child_used_math(child);
- set_fpregs(child, (struct user_i387_struct __user *)data);
- ret = 0;
- break;
- }
-
- case PTRACE_GETFPXREGS: { /* Get the child extended FPU state. */
- if (!access_ok(VERIFY_WRITE, datap,
- sizeof(struct user_fxsr_struct))) {
- ret = -EIO;
+static int
+tls_active(struct task_struct *target, const struct utrace_regset *regset)
+{
+ int i;
+ for (i = GDT_ENTRY_TLS_ENTRIES; i > 0; --i) {
+ struct desc_struct *desc = &target->thread.tls_array[i - 1];
+ if ((desc->a | desc->b) != 0)
break;
- }
- if (!tsk_used_math(child))
- init_fpu(child);
- ret = get_fpxregs((struct user_fxsr_struct __user *)data, child);
- break;
}
+ return i;
+}
- case PTRACE_SETFPXREGS: { /* Set the child extended FPU state. */
- if (!access_ok(VERIFY_READ, datap,
- sizeof(struct user_fxsr_struct))) {
- ret = -EIO;
- break;
- }
- set_stopped_child_used_math(child);
- ret = set_fpxregs(child, (struct user_fxsr_struct __user *)data);
- break;
- }
+/*
+ * These are our native regset flavors.
+ * XXX ioperm? vm86?
+ */
+static const struct utrace_regset native_regsets[] = {
+ {
+ .n = FRAME_SIZE, .size = sizeof(long), .align = sizeof(long),
+ .get = genregs_get, .set = genregs_set
+ },
+ {
+ .n = sizeof(struct user_i387_struct) / sizeof(long),
+ .size = sizeof(long), .align = sizeof(long),
+ .active = fpregs_active,
+ .get = fpregs_get, .set = fpregs_set
+ },
+ {
+ .n = sizeof(struct user_fxsr_struct) / sizeof(long),
+ .size = sizeof(long), .align = sizeof(long),
+ .active = fpxregs_active,
+ .get = fpxregs_get, .set = fpxregs_set
+ },
+ {
+ .n = GDT_ENTRY_TLS_ENTRIES,
+ .bias = GDT_ENTRY_TLS_MIN,
+ .size = sizeof(struct user_desc),
+ .align = sizeof(struct user_desc),
+ .active = tls_active, .get = tls_get, .set = tls_set
+ },
+ {
+ .n = 8, .size = sizeof(long), .align = sizeof(long),
+ .active = dbregs_active,
+ .get = dbregs_get, .set = dbregs_set
+ },
+};
+
+const struct utrace_regset_view utrace_i386_native = {
+ .name = "i386", .e_machine = EM_386,
+ .regsets = native_regsets,
+ .n = sizeof native_regsets / sizeof native_regsets[0],
+};
+EXPORT_SYMBOL_GPL(utrace_i386_native);
+
+#ifdef CONFIG_PTRACE
+static const struct ptrace_layout_segment i386_uarea[] = {
+ {0, FRAME_SIZE*4, 0, 0},
+ {offsetof(struct user, u_debugreg[0]),
+ offsetof(struct user, u_debugreg[8]), 4, 0},
+ {0, 0, -1, 0}
+};
+
+fastcall int arch_ptrace(long *req, struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ unsigned long addr, unsigned long data, long *val)
+{
+ switch (*req) {
+ case PTRACE_PEEKUSR:
+ return ptrace_peekusr(child, engine, i386_uarea, addr, data);
+ case PTRACE_POKEUSR:
+ return ptrace_pokeusr(child, engine, i386_uarea, addr, data);
+ case PTRACE_GETREGS:
+ return ptrace_whole_regset(child, engine, data, 0, 0);
+ case PTRACE_SETREGS:
+ return ptrace_whole_regset(child, engine, data, 0, 1);
+ case PTRACE_GETFPREGS:
+ return ptrace_whole_regset(child, engine, data, 1, 0);
+ case PTRACE_SETFPREGS:
+ return ptrace_whole_regset(child, engine, data, 1, 1);
+ case PTRACE_GETFPXREGS:
+ return ptrace_whole_regset(child, engine, data, 2, 0);
+ case PTRACE_SETFPXREGS:
+ return ptrace_whole_regset(child, engine, data, 2, 1);
case PTRACE_GET_THREAD_AREA:
- ret = ptrace_get_thread_area(child, addr,
- (struct user_desc __user *) data);
- break;
-
case PTRACE_SET_THREAD_AREA:
- ret = ptrace_set_thread_area(child, addr,
- (struct user_desc __user *) data);
- break;
-
- default:
- ret = ptrace_request(child, request, addr, data);
- break;
+ return ptrace_onereg_access(child, engine,
+ utrace_native_view(current), 3,
+ addr, (void __user *)data,
+ *req == PTRACE_SET_THREAD_AREA);
}
- out_tsk:
- return ret;
+ return -ENOSYS;
}
+#endif
void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
{
@@ -658,78 +793,24 @@ void send_sigtrap(struct task_struct *ts
* - triggered by current->work.syscall_trace
*/
__attribute__((regparm(3)))
-int do_syscall_trace(struct pt_regs *regs, int entryexit)
+void do_syscall_trace(struct pt_regs *regs, int entryexit)
{
- int is_sysemu = test_thread_flag(TIF_SYSCALL_EMU);
- /*
- * With TIF_SYSCALL_EMU set we want to ignore TIF_SINGLESTEP for syscall
- * interception
- */
- int is_singlestep = !is_sysemu && test_thread_flag(TIF_SINGLESTEP);
- int ret = 0;
-
/* do the secure computing check first */
if (!entryexit)
secure_computing(regs->orig_eax);
- if (unlikely(current->audit_context)) {
- if (entryexit)
- audit_syscall_exit(AUDITSC_RESULT(regs->eax),
- regs->eax);
- /* Debug traps, when using PTRACE_SINGLESTEP, must be sent only
- * on the syscall exit path. Normally, when TIF_SYSCALL_AUDIT is
- * not used, entry.S will call us only on syscall exit, not
- * entry; so when TIF_SYSCALL_AUDIT is used we must avoid
- * calling send_sigtrap() on syscall entry.
- *
- * Note that when PTRACE_SYSEMU_SINGLESTEP is used,
- * is_singlestep is false, despite his name, so we will still do
- * the correct thing.
- */
- else if (is_singlestep)
- goto out;
- }
-
- if (!(current->ptrace & PT_PTRACED))
- goto out;
+ if (unlikely(current->audit_context) && entryexit)
+ audit_syscall_exit(AUDITSC_RESULT(regs->eax), regs->eax);
- /* If a process stops on the 1st tracepoint with SYSCALL_TRACE
- * and then is resumed with SYSEMU_SINGLESTEP, it will come in
- * here. We have to check this and return */
- if (is_sysemu && entryexit)
- return 0;
-
- /* Fake a debug trap */
- if (is_singlestep)
- send_sigtrap(current, regs, 0);
-
- if (!test_thread_flag(TIF_SYSCALL_TRACE) && !is_sysemu)
- goto out;
-
- /* the 0x80 provides a way for the tracing parent to distinguish
- between a syscall stop and SIGTRAP delivery */
- /* Note that the debugger could change the result of test_thread_flag!*/
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80:0));
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall(regs, entryexit);
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
+ if (test_thread_flag(TIF_SINGLESTEP) && entryexit) {
+ send_sigtrap(current, regs, 0); /* XXX */
+ tracehook_report_syscall_step(regs);
}
- ret = is_sysemu;
-out:
+
if (unlikely(current->audit_context) && !entryexit)
audit_syscall_entry(AUDIT_ARCH_I386, regs->orig_eax,
regs->ebx, regs->ecx, regs->edx, regs->esi);
- if (ret == 0)
- return 0;
-
- regs->orig_eax = -1; /* force skip of syscall restarting */
- if (unlikely(current->audit_context))
- audit_syscall_exit(AUDITSC_RESULT(regs->eax), regs->eax);
- return 1;
}
--- linux-2.6/arch/i386/kernel/entry.S.utrace-ptrace-compat
+++ linux-2.6/arch/i386/kernel/entry.S
@@ -314,7 +314,7 @@ sysenter_past_esp:
GET_THREAD_INFO(%ebp)
/* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */
- testw $(_TIF_SYSCALL_EMU|_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT),TI_flags(%ebp)
+ testw $(_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT),TI_flags(%ebp)
jnz syscall_trace_entry
cmpl $(nr_syscalls), %eax
jae syscall_badsys
@@ -348,7 +348,7 @@ ENTRY(system_call)
no_singlestep:
# system call tracing in operation / emulation
/* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */
- testw $(_TIF_SYSCALL_EMU|_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT),TI_flags(%ebp)
+ testw $(_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT),TI_flags(%ebp)
jnz syscall_trace_entry
cmpl $(nr_syscalls), %eax
jae syscall_badsys
@@ -476,9 +476,6 @@ syscall_trace_entry:
movl %esp, %eax
xorl %edx,%edx
call do_syscall_trace
- cmpl $0, %eax
- jne resume_userspace # ret != 0 -> running under PTRACE_SYSEMU,
- # so must skip actual syscall
movl ORIG_EAX(%esp), %eax
cmpl $(nr_syscalls), %eax
jnae syscall_call
--- linux-2.6/arch/arm26/kernel/ptrace.c.utrace-ptrace-compat
+++ linux-2.6/arch/arm26/kernel/ptrace.c
@@ -653,30 +653,16 @@ asmlinkage void syscall_trace(int why, s
{
unsigned long ip;
- if (!test_thread_flag(TIF_SYSCALL_TRACE))
- return;
- if (!(current->ptrace & PT_PTRACED))
- return;
+ if (test_thread_flag(TIF_SYSCALL_TRACE)) {
+ /*
+ * Save IP. IP is used to denote syscall entry/exit:
+ * IP = 0 -> entry, = 1 -> exit
+ */
+ ip = regs->ARM_ip;
+ regs->ARM_ip = why;
- /*
- * Save IP. IP is used to denote syscall entry/exit:
- * IP = 0 -> entry, = 1 -> exit
- */
- ip = regs->ARM_ip;
- regs->ARM_ip = why;
+ tracehook_report_syscall(regs, why);
- /* the 0x80 provides a way for the tracing parent to distinguish
- between a syscall stop and SIGTRAP delivery */
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
- ? 0x80 : 0));
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
+ regs->ARM_ip = ip;
}
- regs->ARM_ip = ip;
}
--- linux-2.6/arch/mips/kernel/ptrace.c.utrace-ptrace-compat
+++ linux-2.6/arch/mips/kernel/ptrace.c
@@ -475,26 +475,9 @@ asmlinkage void do_syscall_trace(struct
audit_syscall_exit(AUDITSC_RESULT(regs->regs[2]),
regs->regs[2]);
- if (!(current->ptrace & PT_PTRACED))
- goto out;
- if (!test_thread_flag(TIF_SYSCALL_TRACE))
- goto out;
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall(regs, entryexit);
- /* The 0x80 provides a way for the tracing parent to distinguish
- between a syscall stop and SIGTRAP delivery */
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ?
- 0x80 : 0));
-
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
- }
- out:
if (unlikely(current->audit_context) && !entryexit)
audit_syscall_entry(audit_arch(), regs->regs[2],
regs->regs[4], regs->regs[5],
--- linux-2.6/arch/mips/kernel/sysirix.c.utrace-ptrace-compat
+++ linux-2.6/arch/mips/kernel/sysirix.c
@@ -582,7 +582,7 @@ out:
asmlinkage int irix_getpid(struct pt_regs *regs)
{
- regs->regs[3] = current->real_parent->pid;
+ regs->regs[3] = current->parent->pid;
return current->pid;
}
--- linux-2.6/arch/powerpc/lib/sstep.c.utrace-ptrace-compat
+++ linux-2.6/arch/powerpc/lib/sstep.c
@@ -12,6 +12,9 @@
#include <linux/ptrace.h>
#include <asm/sstep.h>
#include <asm/processor.h>
+#ifdef CONFIG_PPC64
+#include <asm/paca.h>
+#endif
extern char system_call_common[];
--- linux-2.6/arch/powerpc/platforms/cell/spufs/run.c.utrace-ptrace-compat
+++ linux-2.6/arch/powerpc/platforms/cell/spufs/run.c
@@ -51,6 +51,7 @@ static inline int spu_run_fini(struct sp
if (signal_pending(current))
ret = -ERESTARTSYS;
+#if 0 /* XXX */
if (unlikely(current->ptrace & PT_PTRACED)) {
if ((*status & SPU_STATUS_STOPPED_BY_STOP)
&& (*status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) {
@@ -58,6 +59,7 @@ static inline int spu_run_fini(struct sp
ret = -ERESTARTSYS;
}
}
+#endif
return ret;
}
--- linux-2.6/arch/powerpc/kernel/ptrace32.c.utrace-ptrace-compat
+++ linux-2.6/arch/powerpc/kernel/ptrace32.c
@@ -1,436 +0,0 @@
-/*
- * ptrace for 32-bit processes running on a 64-bit kernel.
- *
- * PowerPC version
- * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
- *
- * Derived from "arch/m68k/kernel/ptrace.c"
- * Copyright (C) 1994 by Hamish Macdonald
- * Taken from linux/kernel/ptrace.c and modified for M680x0.
- * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
- *
- * Modified by Cort Dougan (cort@hq.fsmlabs.com)
- * and Paul Mackerras (paulus@samba.org).
- *
- * This file is subject to the terms and conditions of the GNU General
- * Public License. See the file COPYING in the main directory of
- * this archive for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/smp.h>
-#include <linux/smp_lock.h>
-#include <linux/errno.h>
-#include <linux/ptrace.h>
-#include <linux/user.h>
-#include <linux/security.h>
-#include <linux/signal.h>
-
-#include <asm/uaccess.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/system.h>
-
-#include "ptrace-common.h"
-
-/*
- * does not yet catch signals sent when the child dies.
- * in exit.c or in signal.c.
- */
-
-long compat_sys_ptrace(int request, int pid, unsigned long addr,
- unsigned long data)
-{
- struct task_struct *child;
- int ret;
-
- lock_kernel();
- if (request == PTRACE_TRACEME) {
- ret = ptrace_traceme();
- goto out;
- }
-
- child = ptrace_get_task_struct(pid);
- if (IS_ERR(child)) {
- ret = PTR_ERR(child);
- goto out;
- }
-
- if (request == PTRACE_ATTACH) {
- ret = ptrace_attach(child);
- goto out_tsk;
- }
-
- ret = ptrace_check_attach(child, request == PTRACE_KILL);
- if (ret < 0)
- goto out_tsk;
-
- switch (request) {
- /* when I and D space are separate, these will need to be fixed. */
- case PTRACE_PEEKTEXT: /* read word at location addr. */
- case PTRACE_PEEKDATA: {
- unsigned int tmp;
- int copied;
-
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
- ret = -EIO;
- if (copied != sizeof(tmp))
- break;
- ret = put_user(tmp, (u32 __user *)data);
- 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;
- }
-
- /* Read a register (specified by ADDR) out of the "user area" */
- case PTRACE_PEEKUSR: {
- int index;
- unsigned long tmp;
-
- ret = -EIO;
- /* convert to index and check */
- index = (unsigned long) addr >> 2;
- if ((addr & 3) || (index > PT_FPSCR32))
- break;
-
- if (index < PT_FPR0) {
- tmp = get_reg(child, index);
- } else {
- flush_fp_to_thread(child);
- /*
- * the user space code considers the floating point
- * to be an array of unsigned int (32 bits) - the
- * index passed in is based on this assumption.
- */
- tmp = ((unsigned int *)child->thread.fpr)[index - PT_FPR0];
- }
- ret = put_user((unsigned int)tmp, (u32 __user *)data);
- break;
- }
-
- /*
- * Read 4 bytes out of the other process' pt_regs area
- * data is a pointer specifying where the user wants the
- * 4 bytes copied into
- * addr is the offset into the other process' pt_regs structure
- * that is to be read
- * (this is run in a 32-bit process looking at a 64-bit process)
- */
- case PPC_PTRACE_PEEKUSR_3264: {
- u32 index;
- u32 reg32bits;
- u64 tmp;
- u32 numReg;
- u32 part;
-
- ret = -EIO;
- /* Determine which register the user wants */
- index = (u64)addr >> 2;
- numReg = index / 2;
- /* Determine which part of the register the user wants */
- if (index % 2)
- part = 1; /* want the 2nd half of the register (right-most). */
- else
- part = 0; /* want the 1st half of the register (left-most). */
-
- /* Validate the input - check to see if address is on the wrong boundary or beyond the end of the user area */
- if ((addr & 3) || numReg > PT_FPSCR)
- break;
-
- if (numReg >= PT_FPR0) {
- flush_fp_to_thread(child);
- tmp = ((unsigned long int *)child->thread.fpr)[numReg - PT_FPR0];
- } else { /* register within PT_REGS struct */
- tmp = get_reg(child, numReg);
- }
- reg32bits = ((u32*)&tmp)[part];
- ret = put_user(reg32bits, (u32 __user *)data);
- break;
- }
-
- /* If I and D space are separate, this will have to be fixed. */
- case PTRACE_POKETEXT: /* write the word at location addr. */
- case PTRACE_POKEDATA: {
- unsigned int tmp;
- tmp = data;
- ret = 0;
- if (access_process_vm(child, addr, &tmp, sizeof(tmp), 1)
- == sizeof(tmp))
- break;
- ret = -EIO;
- 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;
- }
-
- /* write the word at location addr in the USER area */
- case PTRACE_POKEUSR: {
- unsigned long index;
-
- ret = -EIO;
- /* convert to index and check */
- index = (unsigned long) addr >> 2;
- if ((addr & 3) || (index > PT_FPSCR32))
- break;
-
- if (index == PT_ORIG_R3)
- break;
- if (index < PT_FPR0) {
- ret = put_reg(child, index, data);
- } else {
- flush_fp_to_thread(child);
- /*
- * the user space code considers the floating point
- * to be an array of unsigned int (32 bits) - the
- * index passed in is based on this assumption.
- */
- ((unsigned int *)child->thread.fpr)[index - PT_FPR0] = data;
- ret = 0;
- }
- break;
- }
-
- /*
- * Write 4 bytes into the other process' pt_regs area
- * data is the 4 bytes that the user wants written
- * addr is the offset into the other process' pt_regs structure
- * that is to be written into
- * (this is run in a 32-bit process looking at a 64-bit process)
- */
- case PPC_PTRACE_POKEUSR_3264: {
- u32 index;
- u32 numReg;
-
- ret = -EIO;
- /* Determine which register the user wants */
- index = (u64)addr >> 2;
- numReg = index / 2;
- /*
- * Validate the input - check to see if address is on the
- * wrong boundary or beyond the end of the user area
- */
- if ((addr & 3) || (numReg > PT_FPSCR))
- break;
- /* Insure it is a register we let them change */
- if ((numReg == PT_ORIG_R3)
- || ((numReg > PT_CCR) && (numReg < PT_FPR0)))
- break;
- if (numReg >= PT_FPR0) {
- flush_fp_to_thread(child);
- }
- if (numReg == PT_MSR)
- data = (data & MSR_DEBUGCHANGE)
- | (child->thread.regs->msr & ~MSR_DEBUGCHANGE);
- ((u32*)child->thread.regs)[index] = data;
- ret = 0;
- break;
- }
-
- case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
- case PTRACE_CONT: { /* restart after signal. */
- ret = -EIO;
- if (!valid_signal(data))
- break;
- if (request == PTRACE_SYSCALL)
- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- else
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- child->exit_code = data;
- /* make sure the single step bit is not set. */
- clear_single_step(child);
- wake_up_process(child);
- ret = 0;
- break;
- }
-
- /*
- * make the child exit. Best I can do is send it a sigkill.
- * perhaps it should be put in the status that it wants to
- * exit.
- */
- case PTRACE_KILL: {
- ret = 0;
- if (child->exit_state == EXIT_ZOMBIE) /* already dead */
- break;
- child->exit_code = SIGKILL;
- /* make sure the single step bit is not set. */
- clear_single_step(child);
- wake_up_process(child);
- break;
- }
-
- case PTRACE_SINGLESTEP: { /* set the trap flag. */
- ret = -EIO;
- if (!valid_signal(data))
- break;
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- set_single_step(child);
- child->exit_code = data;
- /* give it a chance to run. */
- wake_up_process(child);
- ret = 0;
- break;
- }
-
- case PTRACE_GET_DEBUGREG: {
- ret = -EINVAL;
- /* We only support one DABR and no IABRS at the moment */
- if (addr > 0)
- break;
- ret = put_user(child->thread.dabr, (u32 __user *)data);
- break;
- }
-
- case PTRACE_SET_DEBUGREG:
- ret = ptrace_set_debugreg(child, addr, data);
- break;
-
- case PTRACE_DETACH:
- ret = ptrace_detach(child, data);
- break;
-
- case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */
- int i;
- unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
- unsigned int __user *tmp = (unsigned int __user *)addr;
-
- for (i = 0; i < 32; i++) {
- ret = put_user(*reg, tmp);
- if (ret)
- break;
- reg++;
- tmp++;
- }
- break;
- }
-
- case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */
- int i;
- unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
- unsigned int __user *tmp = (unsigned int __user *)addr;
-
- for (i = 0; i < 32; i++) {
- ret = get_user(*reg, tmp);
- if (ret)
- break;
- reg++;
- tmp++;
- }
- break;
- }
-
- case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */
- int i;
- unsigned long *reg = &((unsigned long *)child->thread.fpr)[0];
- unsigned int __user *tmp = (unsigned int __user *)addr;
-
- flush_fp_to_thread(child);
-
- for (i = 0; i < 32; i++) {
- ret = put_user(*reg, tmp);
- if (ret)
- break;
- reg++;
- tmp++;
- }
- break;
- }
-
- case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */
- int i;
- unsigned long *reg = &((unsigned long *)child->thread.fpr)[0];
- unsigned int __user *tmp = (unsigned int __user *)addr;
-
- flush_fp_to_thread(child);
-
- for (i = 0; i < 32; i++) {
- ret = get_user(*reg, tmp);
- if (ret)
- break;
- reg++;
- tmp++;
- }
- break;
- }
-
- case PTRACE_GETEVENTMSG:
- ret = put_user(child->ptrace_message, (unsigned int __user *) data);
- break;
-
-#ifdef CONFIG_ALTIVEC
- case PTRACE_GETVRREGS:
- /* Get the child altivec register state. */
- flush_altivec_to_thread(child);
- ret = get_vrregs((unsigned long __user *)data, child);
- break;
-
- case PTRACE_SETVRREGS:
- /* Set the child altivec register state. */
- flush_altivec_to_thread(child);
- ret = set_vrregs(child, (unsigned long __user *)data);
- break;
-#endif
-
- default:
- ret = ptrace_request(child, request, addr, data);
- break;
- }
-out_tsk:
- put_task_struct(child);
-out:
- unlock_kernel();
- return ret;
-}
--- linux-2.6/arch/powerpc/kernel/ptrace-common.h.utrace-ptrace-compat
+++ linux-2.6/arch/powerpc/kernel/ptrace-common.h
@@ -1,161 +0,0 @@
-/*
- * Copyright (c) 2002 Stephen Rothwell, IBM Coproration
- * Extracted from ptrace.c and ptrace32.c
- *
- * This file is subject to the terms and conditions of the GNU General
- * Public License. See the file README.legal in the main directory of
- * this archive for more details.
- */
-
-#ifndef _PPC64_PTRACE_COMMON_H
-#define _PPC64_PTRACE_COMMON_H
-
-#include <asm/system.h>
-
-/*
- * Set of msr bits that gdb can change on behalf of a process.
- */
-#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1)
-
-/*
- * Get contents of register REGNO in task TASK.
- */
-static inline unsigned long get_reg(struct task_struct *task, int regno)
-{
- unsigned long tmp = 0;
-
- /*
- * Put the correct FP bits in, they might be wrong as a result
- * of our lazy FP restore.
- */
- if (regno == PT_MSR) {
- tmp = ((unsigned long *)task->thread.regs)[PT_MSR];
- tmp |= task->thread.fpexc_mode;
- } else if (regno < (sizeof(struct pt_regs) / sizeof(unsigned long))) {
- tmp = ((unsigned long *)task->thread.regs)[regno];
- }
-
- return tmp;
-}
-
-/*
- * Write contents of register REGNO in task TASK.
- */
-static inline int put_reg(struct task_struct *task, int regno,
- unsigned long data)
-{
- if (regno < PT_SOFTE) {
- if (regno == PT_MSR)
- data = (data & MSR_DEBUGCHANGE)
- | (task->thread.regs->msr & ~MSR_DEBUGCHANGE);
- ((unsigned long *)task->thread.regs)[regno] = data;
- return 0;
- }
- return -EIO;
-}
-
-static inline void set_single_step(struct task_struct *task)
-{
- struct pt_regs *regs = task->thread.regs;
- if (regs != NULL)
- regs->msr |= MSR_SE;
- set_tsk_thread_flag(task, TIF_SINGLESTEP);
-}
-
-static inline void clear_single_step(struct task_struct *task)
-{
- struct pt_regs *regs = task->thread.regs;
- if (regs != NULL)
- regs->msr &= ~MSR_SE;
- clear_tsk_thread_flag(task, TIF_SINGLESTEP);
-}
-
-#ifdef CONFIG_ALTIVEC
-/*
- * Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go.
- * The transfer totals 34 quadword. Quadwords 0-31 contain the
- * corresponding vector registers. Quadword 32 contains the vscr as the
- * last word (offset 12) within that quadword. Quadword 33 contains the
- * vrsave as the first word (offset 0) within the quadword.
- *
- * This definition of the VMX state is compatible with the current PPC32
- * ptrace interface. This allows signal handling and ptrace to use the
- * same structures. This also simplifies the implementation of a bi-arch
- * (combined (32- and 64-bit) gdb.
- */
-
-/*
- * Get contents of AltiVec register state in task TASK
- */
-static inline int get_vrregs(unsigned long __user *data,
- struct task_struct *task)
-{
- unsigned long regsize;
-
- /* copy AltiVec registers VR[0] .. VR[31] */
- regsize = 32 * sizeof(vector128);
- if (copy_to_user(data, task->thread.vr, regsize))
- return -EFAULT;
- data += (regsize / sizeof(unsigned long));
-
- /* copy VSCR */
- regsize = 1 * sizeof(vector128);
- if (copy_to_user(data, &task->thread.vscr, regsize))
- return -EFAULT;
- data += (regsize / sizeof(unsigned long));
-
- /* copy VRSAVE */
- if (put_user(task->thread.vrsave, (u32 __user *)data))
- return -EFAULT;
-
- return 0;
-}
-
-/*
- * Write contents of AltiVec register state into task TASK.
- */
-static inline int set_vrregs(struct task_struct *task,
- unsigned long __user *data)
-{
- unsigned long regsize;
-
- /* copy AltiVec registers VR[0] .. VR[31] */
- regsize = 32 * sizeof(vector128);
- if (copy_from_user(task->thread.vr, data, regsize))
- return -EFAULT;
- data += (regsize / sizeof(unsigned long));
-
- /* copy VSCR */
- regsize = 1 * sizeof(vector128);
- if (copy_from_user(&task->thread.vscr, data, regsize))
- return -EFAULT;
- data += (regsize / sizeof(unsigned long));
-
- /* copy VRSAVE */
- if (get_user(task->thread.vrsave, (u32 __user *)data))
- return -EFAULT;
-
- return 0;
-}
-#endif
-
-static inline int ptrace_set_debugreg(struct task_struct *task,
- unsigned long addr, unsigned long data)
-{
- /* We only support one DABR and no IABRS at the moment */
- if (addr > 0)
- return -EINVAL;
-
- /* The bottom 3 bits are flags */
- if ((data & ~0x7UL) >= TASK_SIZE)
- return -EIO;
-
- /* Ensure translation is on */
- if (data && !(data & DABR_TRANSLATION))
- return -EIO;
-
- task->thread.dabr = data;
- return 0;
-}
-
-#endif /* _PPC64_PTRACE_COMMON_H */
--- linux-2.6/arch/powerpc/kernel/sys_ppc32.c.utrace-ptrace-compat
+++ linux-2.6/arch/powerpc/kernel/sys_ppc32.c
@@ -429,11 +429,6 @@ long compat_sys_execve(unsigned long a0,
error = compat_do_execve(filename, compat_ptr(a1), compat_ptr(a2), regs);
- if (error == 0) {
- task_lock(current);
- current->ptrace &= ~PT_DTRACE;
- task_unlock(current);
- }
putname(filename);
out:
--- linux-2.6/arch/powerpc/kernel/signal_64.c.utrace-ptrace-compat
+++ linux-2.6/arch/powerpc/kernel/signal_64.c
@@ -24,6 +24,7 @@
#include <linux/stddef.h>
#include <linux/elf.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/module.h>
#include <asm/sigcontext.h>
@@ -461,6 +462,9 @@ static int handle_signal(unsigned long s
spin_unlock_irq(¤t->sighand->siglock);
}
+ if (ret)
+ tracehook_report_handle_signal(sig, ka, oldset, regs);
+
return ret;
}
--- linux-2.6/arch/powerpc/kernel/process.c.utrace-ptrace-compat
+++ linux-2.6/arch/powerpc/kernel/process.c
@@ -814,11 +814,6 @@ int sys_execve(unsigned long a0, unsigne
flush_spe_to_thread(current);
error = do_execve(filename, (char __user * __user *) a1,
(char __user * __user *) a2, regs);
- if (error == 0) {
- task_lock(current);
- current->ptrace &= ~PT_DTRACE;
- task_unlock(current);
- }
putname(filename);
out:
return error;
--- linux-2.6/arch/powerpc/kernel/signal_32.c.utrace-ptrace-compat
+++ linux-2.6/arch/powerpc/kernel/signal_32.c
@@ -25,6 +25,7 @@
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/elf.h>
+#include <linux/tracehook.h>
#ifdef CONFIG_PPC64
#include <linux/syscalls.h>
#include <linux/compat.h>
@@ -631,6 +632,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
@@ -1218,5 +1271,8 @@ no_signal:
clear_thread_flag(TIF_RESTORE_SIGMASK);
}
+ if (ret)
+ tracehook_report_handle_signal(signr, &ka, oldset, regs);
+
return ret;
}
--- linux-2.6/arch/powerpc/kernel/ptrace.c.utrace-ptrace-compat
+++ linux-2.6/arch/powerpc/kernel/ptrace.c
@@ -22,128 +22,239 @@
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/signal.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
-#ifdef CONFIG_PPC32
+#include <linux/elf.h>
#include <linux/module.h>
-#endif
#include <asm/uaccess.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/system.h>
+#include <asm/tracehook.h>
-#ifdef CONFIG_PPC64
-#include "ptrace-common.h"
-#endif
-
-#ifdef CONFIG_PPC32
/*
* Set of msr bits that gdb can change on behalf of a process.
*/
-#if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
+#ifdef CONFIG_PPC64
+#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1)
+#elif defined(CONFIG_40x) || defined(CONFIG_BOOKE)
#define MSR_DEBUGCHANGE 0
-#else
+#else /* CONFIG_PPC32 */
#define MSR_DEBUGCHANGE (MSR_SE | MSR_BE)
-#endif
-#endif /* CONFIG_PPC32 */
+#endif /* CONFIG_PPC64 */
/*
- * does not yet catch signals sent when the child dies.
- * in exit.c or in signal.c.
+ * Last register that can be changed via ptrace.
*/
+#ifdef CONFIG_PPC64
+#define PT_LAST PT_SOFTE
+#else
+#define PT_LAST PT_MQ
+#endif
+
+static int
+genregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (target->thread.regs == NULL)
+ return -EIO;
#ifdef CONFIG_PPC32
-/*
- * Get contents of register REGNO in task TASK.
- */
-static inline unsigned long get_reg(struct task_struct *task, int regno)
+ CHECK_FULL_REGS(target->thread.regs);
+#endif
+
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ target->thread.regs, 0, -1);
+}
+
+static int
+genregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ unsigned long msr_save;
+ int ret = 0;
+
+ if (target->thread.regs == NULL)
+ return -EIO;
+
+#ifdef CONFIG_PPC32
+ CHECK_FULL_REGS(target->thread.regs);
+#endif
+
+ /*
+ * Just ignore attempts to set the registers beyond PT_LAST.
+ * They are read-only.
+ */
+
+ msr_save = target->thread.regs->msr &~ MSR_DEBUGCHANGE;
+
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ target->thread.regs, 0,
+ (PT_LAST + 1) * sizeof(long));
+
+ target->thread.regs->msr &= MSR_DEBUGCHANGE;
+ target->thread.regs->msr |= msr_save;
+
+ return ret;
+}
+
+static int
+fpregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
{
- if (regno < sizeof(struct pt_regs) / sizeof(unsigned long)
- && task->thread.regs != NULL)
- return ((unsigned long *)task->thread.regs)[regno];
- return (0);
+ BUILD_BUG_ON(offsetof(struct thread_struct, fpscr)
+ != offsetof(struct thread_struct, fpr[32]));
+
+ flush_fp_to_thread(target);
+
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fpr, 0, -1);
}
-/*
- * Write contents of register REGNO in task TASK.
- */
-static inline int put_reg(struct task_struct *task, int regno,
- unsigned long data)
+static int
+fpregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
{
- if (regno <= PT_MQ && task->thread.regs != NULL) {
- if (regno == PT_MSR)
- data = (data & MSR_DEBUGCHANGE)
- | (task->thread.regs->msr & ~MSR_DEBUGCHANGE);
- ((unsigned long *)task->thread.regs)[regno] = data;
- return 0;
- }
- return -EIO;
+ return utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fpr, 0, -1);
}
#ifdef CONFIG_ALTIVEC
/*
- * Get contents of AltiVec register state in task TASK
+ * Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go.
+ * The transfer totals 34 quadword. Quadwords 0-31 contain the
+ * corresponding vector registers. Quadword 32 contains the vscr as the
+ * last word (offset 12) within that quadword. Quadword 33 contains the
+ * vrsave as the first word (offset 0) within the quadword.
+ *
+ * This definition of the VMX state is compatible with the current PPC32
+ * ptrace interface. This allows signal handling and ptrace to use the
+ * same structures. This also simplifies the implementation of a bi-arch
+ * (combined (32- and 64-bit) gdb.
*/
-static inline int get_vrregs(unsigned long __user *data, struct task_struct *task)
-{
- int i, j;
- if (!access_ok(VERIFY_WRITE, data, 133 * sizeof(unsigned long)))
- return -EFAULT;
+static int
+vrregs_active(struct task_struct *target, const struct utrace_regset *regset)
+{
+ flush_altivec_to_thread(target);
+ return target->thread.used_vr ? regset->n : 0;
+}
- /* copy AltiVec registers VR[0] .. VR[31] */
- for (i = 0; i < 32; i++)
- for (j = 0; j < 4; j++, data++)
- if (__put_user(task->thread.vr[i].u[j], data))
- return -EFAULT;
+static int
+vrregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ BUILD_BUG_ON(offsetof(struct thread_struct, vscr)
+ != offsetof(struct thread_struct, vr[32]));
+ BUILD_BUG_ON(offsetof(struct thread_struct, vscr) + sizeof(vector128)
+ != offsetof(struct thread_struct, vrsave));
- /* copy VSCR */
- for (i = 0; i < 4; i++, data++)
- if (__put_user(task->thread.vscr.u[i], data))
- return -EFAULT;
-
- /* copy VRSAVE */
- if (__put_user(task->thread.vrsave, data))
- return -EFAULT;
+ flush_altivec_to_thread(target);
- return 0;
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.vr, 0, -1);
}
-/*
- * Write contents of AltiVec register state into task TASK.
- */
-static inline int set_vrregs(struct task_struct *task, unsigned long __user *data)
+static int
+vrregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
{
- int i, j;
+ flush_altivec_to_thread(target);
- if (!access_ok(VERIFY_READ, data, 133 * sizeof(unsigned long)))
- return -EFAULT;
+ return utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.vr, 0, -1);
+}
+#endif /* CONFIG_ALTIVEC */
- /* copy AltiVec registers VR[0] .. VR[31] */
- for (i = 0; i < 32; i++)
- for (j = 0; j < 4; j++, data++)
- if (__get_user(task->thread.vr[i].u[j], data))
- return -EFAULT;
+#ifdef CONFIG_PPC64
+/* We only support one DABR and no IABRS at the moment */
+
+static int
+set_thread_dabr(struct task_struct *tsk, unsigned long dabr)
+{
+ /* The bottom 3 bits are flags */
+ if ((dabr & ~0x7UL) >= TASK_SIZE)
+ return -EIO;
- /* copy VSCR */
- for (i = 0; i < 4; i++, data++)
- if (__get_user(task->thread.vscr.u[i], data))
- return -EFAULT;
-
- /* copy VRSAVE */
- if (__get_user(task->thread.vrsave, data))
- return -EFAULT;
+ /* Ensure translation is on */
+ if (dabr && !(dabr & DABR_TRANSLATION))
+ return -EIO;
+ tsk->thread.dabr = dabr;
return 0;
}
-#endif
-#ifdef CONFIG_SPE
+static int
+debugreg_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.dabr, 0, -1);
+}
+
+static int
+debugreg_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ unsigned long dabr;
+ int ret;
+
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf, &dabr, 0, -1);
+ if (ret == 0)
+ ret = set_thread_dabr(target, dabr);
+
+ return ret;
+}
+
+static int
+ppc32_dabr_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ u32 dabr = target->thread.dabr;
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf, &dabr, 0, -1);
+}
+
+static int
+ppc32_dabr_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ u32 dabr;
+ int ret;
+
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf, &dabr, 0, -1);
+ if (ret == 0)
+ ret = set_thread_dabr(target, dabr);
+
+ return ret;
+}
+#endif /* CONFIG_PPC64 */
+#ifdef CONFIG_SPE
/*
* For get_evrregs/set_evrregs functions 'data' has the following layout:
*
@@ -154,375 +265,447 @@ static inline int set_vrregs(struct task
* }
*/
-/*
- * Get contents of SPE register state in task TASK.
- */
-static inline int get_evrregs(unsigned long *data, struct task_struct *task)
+static int
+evrregs_active(struct task_struct *target, const struct utrace_regset *regset)
{
- int i;
+ if (target->thread.regs->msr & MSR_SPE)
+ giveup_spe(target);
+ return target->thread.used_spe ? regset->n : 0;
+}
- if (!access_ok(VERIFY_WRITE, data, 35 * sizeof(unsigned long)))
- return -EFAULT;
+static int
+evrregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ BUILD_BUG_ON(offsetof(struct thread_struct, acc)
+ != offsetof(struct thread_struct, evr[32]));
+ BUILD_BUG_ON(offsetof(struct thread_struct, acc) + sizeof(u64)
+ != offsetof(struct thread_struct, spefscr));
- /* copy SPEFSCR */
- if (__put_user(task->thread.spefscr, &data[34]))
- return -EFAULT;
+ if (target->thread.regs->msr & MSR_SPE)
+ giveup_spe(target);
- /* copy SPE registers EVR[0] .. EVR[31] */
- for (i = 0; i < 32; i++, data++)
- if (__put_user(task->thread.evr[i], data))
- return -EFAULT;
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.evr, 0, -1);
+}
- /* copy ACC */
- if (__put_user64(task->thread.acc, (unsigned long long *)data))
- return -EFAULT;
+static int
+evrregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ /* this is to clear the MSR_SPE bit to force a reload
+ * of register state from memory */
+ if (target->thread.regs->msr & MSR_SPE)
+ giveup_spe(target);
- return 0;
+ return utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.evr, 0, -1);
}
+#endif /* CONFIG_SPE */
+
/*
- * Write contents of SPE register state into task TASK.
+ * These are our native regset flavors.
*/
-static inline int set_evrregs(struct task_struct *task, unsigned long *data)
-{
- int i;
+static const struct utrace_regset native_regsets[] = {
+ {
+ .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long),
+ .get = genregs_get, .set = genregs_set
+ },
+ {
+ .n = ELF_NFPREG,
+ .size = sizeof(double), .align = sizeof(double),
+ .get = fpregs_get, .set = fpregs_set
+ },
+#ifdef CONFIG_ALTIVEC
+ {
+ .n = 33*4+1, .size = sizeof(u32), .align = sizeof(u32),
+ .active = vrregs_active, .get = vrregs_get, .set = vrregs_set
+ },
+#endif
+#ifdef CONFIG_SPE
+ {
+ .n = 35, .size = sizeof(long), .align = sizeof(long),
+ .active = evrregs_active,
+ .get = evrregs_get, .set = evrregs_set
+ },
+#endif
+#ifdef CONFIG_PPC64
+ {
+ .n = 1, .size = sizeof(long), .align = sizeof(long),
+ .get = debugreg_get, .set = debugreg_set
+ },
+#endif
+};
+
+const struct utrace_regset_view utrace_ppc_native_view = {
+ .name = UTS_MACHINE, .e_machine = ELF_ARCH,
+ .regsets = native_regsets,
+ .n = sizeof native_regsets / sizeof native_regsets[0],
+};
+EXPORT_SYMBOL_GPL(utrace_ppc_native_view);
+
- if (!access_ok(VERIFY_READ, data, 35 * sizeof(unsigned long)))
- return -EFAULT;
+#ifdef CONFIG_PPC64
+#include <linux/compat.h>
- /* copy SPEFSCR */
- if (__get_user(task->thread.spefscr, &data[34]))
- return -EFAULT;
-
- /* copy SPE registers EVR[0] .. EVR[31] */
- for (i = 0; i < 32; i++, data++)
- if (__get_user(task->thread.evr[i], data))
- return -EFAULT;
- /* copy ACC */
- if (__get_user64(task->thread.acc, (unsigned long long*)data))
- return -EFAULT;
+static int
+ppc32_gpr_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ unsigned long *regs = (unsigned long *) target->thread.regs;
+
+ if (regs == NULL)
+ return -EIO;
+
+ regs += pos / sizeof(u32);
+
+ if (kbuf) {
+ u32 *out = kbuf;
+ for (; count > 0; count -= sizeof(u32))
+ *out++ = *regs++;
+ }
+ else {
+ u32 __user *out = ubuf;
+ for (; count > 0; count -= sizeof(u32))
+ if (put_user((u32) *regs++, out++))
+ return -EFAULT;
+ }
return 0;
}
-#endif /* CONFIG_SPE */
-static inline void
-set_single_step(struct task_struct *task)
+static int
+ppc32_gpr_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
{
- struct pt_regs *regs = task->thread.regs;
+ unsigned long *regs = (unsigned long *) target->thread.regs;
- if (regs != NULL) {
-#if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
- task->thread.dbcr0 = DBCR0_IDM | DBCR0_IC;
- regs->msr |= MSR_DE;
-#else
- regs->msr |= MSR_SE;
-#endif
- }
-}
+ if (regs == NULL)
+ return -EIO;
-static inline void
-clear_single_step(struct task_struct *task)
-{
- struct pt_regs *regs = task->thread.regs;
+ /*
+ * Just ignore attempts to set the registers beyond PT_LAST.
+ * They are read-only.
+ */
+ if (count > (PT_LAST + 1) * sizeof(u32) - pos)
+ count = (PT_LAST + 1) * sizeof(u32) - pos;
- if (regs != NULL) {
-#if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
- task->thread.dbcr0 = 0;
- regs->msr &= ~MSR_DE;
-#else
- regs->msr &= ~MSR_SE;
-#endif
+ pos /= sizeof(u32);
+
+ if (kbuf) {
+ const u32 *in = kbuf;
+ for (; count > 0; count -= sizeof(u32), ++pos, ++in) {
+ if (pos == PT_MSR)
+ regs[pos] = ((regs[pos] &~ MSR_DEBUGCHANGE)
+ | (*in & MSR_DEBUGCHANGE));
+ else
+ regs[pos] = *in;
+ }
}
+ else {
+ const u32 __user *in = kbuf;
+ for (; count > 0; count -= sizeof(u32), ++pos) {
+ u32 val;
+ if (get_user(val, in++))
+ return -EFAULT;
+ else if (pos == PT_MSR)
+ regs[pos] = ((regs[pos] &~ MSR_DEBUGCHANGE)
+ | (val & MSR_DEBUGCHANGE));
+ else
+ regs[pos] = val;
+ }
+ }
+
+ return 0;
}
-#endif /* CONFIG_PPC32 */
/*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure single step bits etc are not set.
+ * These are the regset flavors matching the CONFIG_PPC32 native set.
*/
-void ptrace_disable(struct task_struct *child)
-{
- /* make sure the single step bit is not set. */
- clear_single_step(child);
-}
-
-long arch_ptrace(struct task_struct *child, long request, long addr, long data)
-{
- int ret = -EPERM;
-
- switch (request) {
- /* when I and D space are separate, these will need to be fixed. */
- 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;
- }
+static const struct utrace_regset ppc32_regsets[] = {
+ {
+ .n = ELF_NGREG,
+ .size = sizeof(compat_long_t), .align = sizeof(compat_long_t),
+ .get = ppc32_gpr_get, .set = ppc32_gpr_set
+ },
+ {
+ .n = ELF_NFPREG,
+ .size = sizeof(double), .align = sizeof(double),
+ .get = fpregs_get, .set = fpregs_set
+ },
+#ifdef CONFIG_ALTIVEC
+ {
+ .n = 33*4+1, .size = sizeof(u32), .align = sizeof(u32),
+ .active = vrregs_active, .get = vrregs_get, .set = vrregs_set
+ },
+#endif
+ {
+ .n = 1,
+ .size = sizeof(compat_long_t), .align = sizeof(compat_long_t),
+ .get = ppc32_dabr_get, .set = ppc32_dabr_set
+ },
+};
+
+const struct utrace_regset_view utrace_ppc32_view = {
+ .name = "ppc", .e_machine = EM_PPC,
+ .regsets = ppc32_regsets,
+ .n = sizeof ppc32_regsets / sizeof ppc32_regsets[0],
+};
+EXPORT_SYMBOL_GPL(utrace_ppc32_view);
+#endif
- /* read the word at location addr in the USER area. */
- case PTRACE_PEEKUSR: {
- unsigned long index, tmp;
- ret = -EIO;
- /* convert to index and check */
-#ifdef CONFIG_PPC32
- index = (unsigned long) addr >> 2;
- if ((addr & 3) || (index > PT_FPSCR)
- || (child->thread.regs == NULL))
+#ifdef CONFIG_PTRACE
+static const struct ptrace_layout_segment ppc_uarea[] = {
+ {0, PT_FPR0 * sizeof(long), 0, 0},
+ {PT_FPR0 * sizeof(long), (PT_FPSCR + 1) * sizeof(long), 1, 0},
+ {0, 0, -1, 0}
+};
+
+fastcall int arch_ptrace(long *request, struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ unsigned long addr, unsigned long data, long *val)
+{
+ switch (*request) {
+ case PTRACE_PEEKUSR:
+ return ptrace_peekusr(child, engine, ppc_uarea, addr, data);
+ case PTRACE_POKEUSR:
+ return ptrace_pokeusr(child, engine, ppc_uarea, addr, data);
+ case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
+ case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
+ return ptrace_regset_access(child, engine,
+ utrace_native_view(current), 0,
+ 0, 32 * sizeof(long),
+ (void __user *)addr,
+ *request == PPC_PTRACE_SETREGS);
+ case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */
+ case PPC_PTRACE_SETFPREGS: /* Get FPRs 0 - 31. */
+ return ptrace_regset_access(child, engine,
+ utrace_native_view(current), 1,
+ 0, 32 * sizeof(double),
+ (void __user *)addr,
+ *request == PPC_PTRACE_SETFPREGS);
+#ifdef CONFIG_PPC64
+ case PTRACE_GET_DEBUGREG:
+ case PTRACE_SET_DEBUGREG:
+ return ptrace_onereg_access(child, engine,
+ utrace_native_view(current), 3,
+ addr, (unsigned long __user *)data,
+ *request == PTRACE_SET_DEBUGREG);
+#endif /* CONFIG_PPC64 */
+#ifdef CONFIG_ALTIVEC
+ case PTRACE_GETVRREGS:
+ return ptrace_whole_regset(child, engine, data, 2, 0);
+ case PTRACE_SETVRREGS:
+ return ptrace_whole_regset(child, engine, data, 2, 1);
+#endif
+#ifdef CONFIG_SPE
+#ifdef CONFIG_ALTIVEC
+#define REGSET_EVR 3
#else
- index = (unsigned long) addr >> 3;
- if ((addr & 7) || (index > PT_FPSCR))
+#define REGSET_EVR 2
#endif
- break;
-
-#ifdef CONFIG_PPC32
- CHECK_FULL_REGS(child->thread.regs);
+ case PTRACE_GETEVRREGS:
+ return ptrace_whole_regset(child, engine, data, REGSET_EVR, 0);
+ case PTRACE_SETEVRREGS:
+ return ptrace_whole_regset(child, engine, data, REGSET_EVR, 1);
#endif
- if (index < PT_FPR0) {
- tmp = get_reg(child, (int) index);
- } else {
- flush_fp_to_thread(child);
- tmp = ((unsigned long *)child->thread.fpr)[index - PT_FPR0];
- }
- ret = put_user(tmp,(unsigned long __user *) data);
- break;
}
+ return -ENOSYS;
+}
- /* If I and D space are separate, this will have to be fixed. */
- 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;
-
- /* write the word at location addr in the USER area */
- case PTRACE_POKEUSR: {
- unsigned long index;
+#ifdef CONFIG_COMPAT
+#include <linux/mm.h>
+#include <asm/uaccess.h>
- ret = -EIO;
- /* convert to index and check */
-#ifdef CONFIG_PPC32
- index = (unsigned long) addr >> 2;
- if ((addr & 3) || (index > PT_FPSCR)
- || (child->thread.regs == NULL))
-#else
- index = (unsigned long) addr >> 3;
- if ((addr & 7) || (index > PT_FPSCR))
+static const struct ptrace_layout_segment ppc32_uarea[] = {
+ {0, PT_FPR0 * sizeof(u32), 0, 0},
+ {PT_FPR0 * sizeof(u32), (PT_FPSCR32 + 1) * sizeof(u32), 1, 0},
+ {0, 0, -1, 0}
+};
+
+fastcall int arch_compat_ptrace(compat_long_t *request,
+ struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ compat_ulong_t addr, compat_ulong_t data,
+ compat_long_t *val)
+{
+ void __user *uaddr = (void __user *) (unsigned long) addr;
+ int ret = -ENOSYS;
+
+ switch (*request) {
+ case PTRACE_PEEKUSR:
+ return ptrace_compat_peekusr(child, engine, ppc32_uarea,
+ addr, data);
+ case PTRACE_POKEUSR:
+ return ptrace_compat_pokeusr(child, engine, ppc32_uarea,
+ addr, data);
+
+ case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
+ case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
+ return ptrace_regset_access(child, engine,
+ utrace_native_view(current), 0,
+ 0, 32 * sizeof(compat_long_t),
+ uaddr,
+ *request == PPC_PTRACE_SETREGS);
+ case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */
+ case PPC_PTRACE_SETFPREGS: /* Get FPRs 0 - 31. */
+ return ptrace_regset_access(child, engine,
+ utrace_native_view(current), 1,
+ 0, 32 * sizeof(double),
+ uaddr,
+ *request == PPC_PTRACE_SETFPREGS);
+#ifdef CONFIG_ALTIVEC
+ case PTRACE_GETVRREGS:
+ return ptrace_whole_regset(child, engine, data, 2, 0);
+ case PTRACE_SETVRREGS:
+ return ptrace_whole_regset(child, engine, data, 2, 1);
#endif
- break;
+ case PTRACE_GET_DEBUGREG:
+ case PTRACE_SET_DEBUGREG:
+ return ptrace_onereg_access(child, engine,
+ utrace_native_view(current), 3,
+ addr,
+ (unsigned long __user *)
+ (unsigned long) data,
+ *request == PTRACE_SET_DEBUGREG);
-#ifdef CONFIG_PPC32
- CHECK_FULL_REGS(child->thread.regs);
-#endif
- if (index == PT_ORIG_R3)
- break;
- if (index < PT_FPR0) {
- ret = put_reg(child, index, data);
- } else {
- flush_fp_to_thread(child);
- ((unsigned long *)child->thread.fpr)[index - PT_FPR0] = data;
- ret = 0;
- }
- 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;
- case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
- case PTRACE_CONT: { /* restart after signal. */
ret = -EIO;
- if (!valid_signal(data))
+
+ /* Get the addr in the other process that we want to read */
+ if (get_user(addrOthers, ((u32 __user * __user *)
+ (unsigned long) addr)) != 0)
break;
- if (request == PTRACE_SYSCALL)
- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- else
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- child->exit_code = data;
- /* make sure the single step bit is not set. */
- clear_single_step(child);
- wake_up_process(child);
- ret = 0;
- break;
- }
-/*
- * make the child exit. Best I can do is send it a sigkill.
- * perhaps it should be put in the status that it wants to
- * exit.
- */
- case PTRACE_KILL: {
- ret = 0;
- if (child->exit_state == EXIT_ZOMBIE) /* already dead */
+ copied = access_process_vm(child, (u64)addrOthers, &tmp,
+ sizeof(tmp), 0);
+ if (copied != sizeof(tmp))
break;
- child->exit_code = SIGKILL;
- /* make sure the single step bit is not set. */
- clear_single_step(child);
- wake_up_process(child);
+ ret = put_user(tmp, (u32 __user *)(unsigned long)data);
break;
}
- case PTRACE_SINGLESTEP: { /* set the trap flag. */
+ /*
+ * 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 (!valid_signal(data))
+ if (get_user(addrOthers, ((u32 __user * __user *)
+ (unsigned long) addr)) != 0)
break;
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- set_single_step(child);
- child->exit_code = data;
- /* give it a chance to run. */
- wake_up_process(child);
ret = 0;
- break;
- }
-
-#ifdef CONFIG_PPC64
- case PTRACE_GET_DEBUGREG: {
- ret = -EINVAL;
- /* We only support one DABR and no IABRS at the moment */
- if (addr > 0)
+ if (access_process_vm(child, (u64)addrOthers, &tmp,
+ sizeof(tmp), 1) == sizeof(tmp))
break;
- ret = put_user(child->thread.dabr,
- (unsigned long __user *)data);
+ ret = -EIO;
break;
}
- case PTRACE_SET_DEBUGREG:
- ret = ptrace_set_debugreg(child, addr, data);
- break;
-#endif
+ /*
+ * 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;
- case PTRACE_DETACH:
- ret = ptrace_detach(child, data);
- break;
+ ret = -EIO;
+ if ((addr & 3) || addr > PT_FPSCR*8)
+ break;
- case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */
- int i;
- unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
- unsigned long __user *tmp = (unsigned long __user *)addr;
-
- for (i = 0; i < 32; i++) {
- ret = put_user(*reg, tmp);
- if (ret)
- break;
- reg++;
- tmp++;
+ 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), ®.whole, NULL);
+ if (ret == 0)
+ ret = put_user(reg.half[(addr >> 2) & 1],
+ (u32 __user *)(unsigned long)data);
break;
}
- case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */
- int i;
- unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
- unsigned long __user *tmp = (unsigned long __user *)addr;
-
- for (i = 0; i < 32; i++) {
- ret = get_user(*reg, tmp);
- if (ret)
- break;
- reg++;
- tmp++;
- }
- 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;
- case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */
- int i;
- unsigned long *reg = &((unsigned long *)child->thread.fpr)[0];
- unsigned long __user *tmp = (unsigned long __user *)addr;
-
- flush_fp_to_thread(child);
-
- for (i = 0; i < 32; i++) {
- ret = put_user(*reg, tmp);
- if (ret)
- break;
- reg++;
- tmp++;
- }
- break;
- }
+ ret = -EIO;
+ if ((addr & 3) || addr > PT_FPSCR*8)
+ break;
- case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */
- int i;
- unsigned long *reg = &((unsigned long *)child->thread.fpr)[0];
- unsigned long __user *tmp = (unsigned long __user *)addr;
-
- flush_fp_to_thread(child);
-
- for (i = 0; i < 32; i++) {
- ret = get_user(*reg, tmp);
- if (ret)
- break;
- reg++;
- tmp++;
+ 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), ®.whole, NULL);
+ BUG_ON(ret);
+ reg.half[(addr >> 2) & 1] = data;
+ ret = (*regset->set)(child, regset, addr &~ 7,
+ sizeof(reg.whole), ®.whole, NULL);
break;
}
-
-#ifdef CONFIG_ALTIVEC
- case PTRACE_GETVRREGS:
- /* Get the child altivec register state. */
- flush_altivec_to_thread(child);
- ret = get_vrregs((unsigned long __user *)data, child);
- break;
-
- case PTRACE_SETVRREGS:
- /* Set the child altivec register state. */
- flush_altivec_to_thread(child);
- ret = set_vrregs(child, (unsigned long __user *)data);
- break;
-#endif
-#ifdef CONFIG_SPE
- case PTRACE_GETEVRREGS:
- /* Get the child spe register state. */
- if (child->thread.regs->msr & MSR_SPE)
- giveup_spe(child);
- ret = get_evrregs((unsigned long __user *)data, child);
- break;
-
- case PTRACE_SETEVRREGS:
- /* Set the child spe register state. */
- /* this is to clear the MSR_SPE bit to force a reload
- * of register state from memory */
- if (child->thread.regs->msr & MSR_SPE)
- giveup_spe(child);
- ret = set_evrregs(child, (unsigned long __user *)data);
- break;
-#endif
-
- default:
- ret = ptrace_request(child, request, addr, data);
- break;
}
-
return ret;
}
+#endif /* CONFIG_COMPAT */
+#endif /* CONFIG_PTRACE */
-static void do_syscall_trace(void)
-{
- /* the 0x80 provides a way for the tracing parent to distinguish
- between a syscall stop and SIGTRAP delivery */
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
- ? 0x80 : 0));
-
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
- }
-}
void do_syscall_trace_enter(struct pt_regs *regs)
{
@@ -530,9 +713,8 @@ void do_syscall_trace_enter(struct pt_re
secure_computing(regs->gpr[0]);
#endif
- if (test_thread_flag(TIF_SYSCALL_TRACE)
- && (current->ptrace & PT_PTRACED))
- do_syscall_trace();
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall(regs, 0);
if (unlikely(current->audit_context))
audit_syscall_entry(
@@ -556,10 +738,13 @@ void do_syscall_trace_leave(struct pt_re
audit_syscall_exit((regs->ccr&0x1000)?AUDITSC_FAILURE:AUDITSC_SUCCESS,
regs->result);
- if ((test_thread_flag(TIF_SYSCALL_TRACE)
- || test_thread_flag(TIF_SINGLESTEP))
- && (current->ptrace & PT_PTRACED))
- do_syscall_trace();
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall(regs, 1);
+
+ if (test_thread_flag(TIF_SINGLESTEP)) {
+ force_sig(SIGTRAP, current); /* XXX */
+ tracehook_report_syscall_step(regs);
+ }
}
#ifdef CONFIG_PPC32
--- linux-2.6/arch/powerpc/kernel/Makefile.utrace-ptrace-compat
+++ linux-2.6/arch/powerpc/kernel/Makefile
@@ -10,12 +10,14 @@ CFLAGS_prom_init.o += -fPIC
CFLAGS_btext.o += -fPIC
endif
+CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
+
obj-y := semaphore.o cputable.o ptrace.o syscalls.o \
irq.o align.o signal_32.o pmc.o vdso.o \
init_task.o process.o systbl.o idle.o
obj-y += vdso32/
obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \
- signal_64.o ptrace32.o \
+ signal_64.o \
paca.o cpu_setup_power4.o \
firmware.o sysfs.o
obj-$(CONFIG_PPC64) += vdso64/
--- linux-2.6/arch/powerpc/kernel/asm-offsets.c.utrace-ptrace-compat
+++ linux-2.6/arch/powerpc/kernel/asm-offsets.c
@@ -58,7 +58,6 @@ int main(void)
DEFINE(AUDITCONTEXT, offsetof(struct task_struct, audit_context));
#else
DEFINE(THREAD_INFO, offsetof(struct task_struct, thread_info));
- DEFINE(PTRACE, offsetof(struct task_struct, ptrace));
#endif /* CONFIG_PPC64 */
DEFINE(KSP, offsetof(struct thread_struct, ksp));
@@ -79,7 +78,6 @@ int main(void)
DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall));
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
DEFINE(THREAD_DBCR0, offsetof(struct thread_struct, dbcr0));
- DEFINE(PT_PTRACED, PT_PTRACED);
#endif
#ifdef CONFIG_SPE
DEFINE(THREAD_EVR0, offsetof(struct thread_struct, evr[0]));
--- linux-2.6/arch/sparc64/kernel/signal.c.utrace-ptrace-compat
+++ linux-2.6/arch/sparc64/kernel/signal.c
@@ -23,6 +23,7 @@
#include <linux/smp_lock.h>
#include <linux/binfmts.h>
#include <linux/bitops.h>
+#include <linux/tracehook.h>
#include <asm/uaccess.h>
#include <asm/ptrace.h>
@@ -491,6 +492,7 @@ static inline void handle_signal(unsigne
sigaddset(¤t->blocked,signr);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
+ tracehook_report_handle_signal(signr, ka, oldset, regs);
}
static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs,
--- linux-2.6/arch/sparc64/kernel/systbls.S.utrace-ptrace-compat
+++ linux-2.6/arch/sparc64/kernel/systbls.S
@@ -24,7 +24,7 @@ sys_call_table32:
/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys32_chown16, sys32_mknod
/*15*/ .word sys_chmod, sys32_lchown16, sparc_brk, sys32_perfctr, sys32_lseek
/*20*/ .word sys_getpid, sys_capget, sys_capset, sys32_setuid16, sys32_getuid16
-/*25*/ .word sys32_vmsplice, sys_ptrace, sys_alarm, sys32_sigaltstack, sys32_pause
+/*25*/ .word sys32_vmsplice, compat_sys_ptrace, sys_alarm, sys32_sigaltstack, sys32_pause
/*30*/ .word compat_sys_utime, sys_lchown, sys_fchown, sys32_access, sys32_nice
.word sys_chown, sys_sync, sys32_kill, compat_sys_newstat, sys32_sendfile
/*40*/ .word compat_sys_newlstat, sys_dup, sys_pipe, compat_sys_times, sys_getuid
@@ -166,7 +166,7 @@ sunos_sys_table:
.word sys_chmod, sys32_lchown16, sunos_brk
.word sunos_nosys, sys32_lseek, sunos_getpid
.word sunos_nosys, sunos_nosys, sunos_nosys
- .word sunos_getuid, sunos_nosys, sys_ptrace
+ .word sunos_getuid, sunos_nosys, compat_sys_ptrace
.word sunos_nosys, sunos_nosys, sunos_nosys
.word sunos_nosys, sunos_nosys, sunos_nosys
.word sys_access, sunos_nosys, sunos_nosys
--- linux-2.6/arch/sparc64/kernel/signal32.c.utrace-ptrace-compat
+++ linux-2.6/arch/sparc64/kernel/signal32.c
@@ -21,6 +21,7 @@
#include <linux/binfmts.h>
#include <linux/compat.h>
#include <linux/bitops.h>
+#include <linux/tracehook.h>
#include <asm/uaccess.h>
#include <asm/ptrace.h>
@@ -1236,6 +1237,7 @@ static inline void handle_signal32(unsig
sigaddset(¤t->blocked,signr);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
+ tracehook_report_handle_signal(signr, ka, oldset, regs);
}
static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs,
--- linux-2.6/arch/sparc64/kernel/sys_sparc32.c.utrace-ptrace-compat
+++ linux-2.6/arch/sparc64/kernel/sys_sparc32.c
@@ -740,9 +740,6 @@ asmlinkage long sparc32_execve(struct pt
current_thread_info()->xfsr[0] = 0;
current_thread_info()->fpsaved[0] = 0;
regs->tstate &= ~TSTATE_PEF;
- task_lock(current);
- current->ptrace &= ~PT_DTRACE;
- task_unlock(current);
}
out:
return error;
--- linux-2.6/arch/sparc64/kernel/process.c.utrace-ptrace-compat
+++ linux-2.6/arch/sparc64/kernel/process.c
@@ -808,9 +808,6 @@ asmlinkage int sparc_execve(struct pt_re
current_thread_info()->xfsr[0] = 0;
current_thread_info()->fpsaved[0] = 0;
regs->tstate &= ~TSTATE_PEF;
- task_lock(current);
- current->ptrace &= ~PT_DTRACE;
- task_unlock(current);
}
out:
return error;
--- linux-2.6/arch/sparc64/kernel/ptrace.c.utrace-ptrace-compat
+++ linux-2.6/arch/sparc64/kernel/ptrace.c
@@ -1,6 +1,6 @@
-/* ptrace.c: Sparc process tracing support.
+/* ptrace.c: Sparc64 process tracing support.
*
- * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu)
+ * Copyright (C) 1996, 2006 David S. Miller (davem@davemloft.net)
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*
* Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
@@ -11,103 +11,597 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/sched.h>
#include <linux/mm.h>
-#include <linux/errno.h>
-#include <linux/ptrace.h>
-#include <linux/user.h>
-#include <linux/smp.h>
-#include <linux/smp_lock.h>
#include <linux/security.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
-#include <linux/signal.h>
+#include <linux/tracehook.h>
+#include <linux/elf.h>
+#include <linux/ptrace.h>
#include <asm/asi.h>
#include <asm/pgtable.h>
#include <asm/system.h>
-#include <asm/uaccess.h>
-#include <asm/psrcompat.h>
-#include <asm/visasm.h>
#include <asm/spitfire.h>
#include <asm/page.h>
#include <asm/cpudata.h>
+#include <asm/psrcompat.h>
+
+#define GENREG_G0 0
+#define GENREG_O0 8
+#define GENREG_L0 16
+#define GENREG_I0 24
+#define GENREG_TSTATE 32
+#define GENREG_TPC 33
+#define GENREG_TNPC 34
+#define GENREG_Y 35
+
+#define SPARC64_NGREGS 36
+
+static int genregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ struct pt_regs *regs = task_pt_regs(target);
+ int err;
+
+ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf, regs->u_regs,
+ GENREG_G0 * 8, GENREG_L0 * 8);
-/* Returning from ptrace is a bit tricky because the syscall return
- * low level code assumes any value returned which is negative and
- * is a valid errno will mean setting the condition codes to indicate
- * an error return. This doesn't work, so we have this hook.
+ if (err == 0 && count > 0 && pos < (GENREG_TSTATE * 8)) {
+ struct thread_info *t = task_thread_info(target);
+ unsigned long rwindow[16], fp, *win;
+ int wsaved;
+
+ if (target == current)
+ flushw_user();
+
+ wsaved = __thread_flag_byte_ptr(t)[TI_FLAG_BYTE_WSAVED];
+ fp = regs->u_regs[UREG_FP] + STACK_BIAS;
+ if (wsaved && t->rwbuf_stkptrs[wsaved - 1] == fp)
+ win = &t->reg_window[wsaved - 1].locals[0];
+ else {
+ if (target == current) {
+ if (copy_from_user(rwindow,
+ (void __user *) fp,
+ 16 * sizeof(long)))
+ err = -EFAULT;
+ } else
+ err = access_process_vm(target, fp, rwindow,
+ 16 * sizeof(long), 0);
+ if (err)
+ return err;
+ win = rwindow;
+ }
+
+ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ win, GENREG_L0 * 8,
+ GENREG_TSTATE * 8);
+ }
+
+ if (err == 0)
+ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ ®s->tstate, GENREG_TSTATE * 8,
+ GENREG_Y * 8);
+ if (err == 0 && count > 0) {
+ if (kbuf)
+ *(unsigned long *) kbuf = regs->y;
+ else if (put_user(regs->y, (unsigned long __user *) ubuf))
+ return -EFAULT;
+ }
+
+ return err;
+}
+
+/* Consistent with signal handling, we only allow userspace to
+ * modify the %asi, %icc, and %xcc fields of the %tstate register.
*/
-static inline void pt_error_return(struct pt_regs *regs, unsigned long error)
+#define TSTATE_DEBUGCHANGE (TSTATE_ASI | TSTATE_ICC | TSTATE_XCC)
+
+static int genregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
{
- regs->u_regs[UREG_I0] = error;
- regs->tstate |= (TSTATE_ICARRY | TSTATE_XCARRY);
- regs->tpc = regs->tnpc;
- regs->tnpc += 4;
+ struct pt_regs *regs = task_pt_regs(target);
+ unsigned long tstate_save;
+ int err;
+
+ err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf, regs->u_regs,
+ GENREG_G0 * 8, GENREG_L0 * 8);
+
+ if (err == 0 && count > 0 && pos < (GENREG_TSTATE * 8)) {
+ unsigned long fp = regs->u_regs[UREG_FP] + STACK_BIAS;
+ unsigned long rwindow[16], *winbuf;
+ unsigned int copy = (GENREG_TSTATE * 8) - pos;
+ unsigned int off;
+ int err;
+
+ if (target == current)
+ flushw_user();
+
+ if (count < copy)
+ copy = count;
+ off = pos - (GENREG_L0 * 8);
+
+ if (kbuf) {
+ winbuf = (unsigned long *) kbuf;
+ kbuf += copy;
+ }
+ else {
+ winbuf = rwindow;
+ if (copy_from_user(winbuf, ubuf, copy))
+ return -EFAULT;
+ ubuf += copy;
+ }
+ count -= copy;
+ pos += copy;
+
+ if (target == current)
+ err = copy_to_user((void __user *) fp + off,
+ winbuf, copy);
+ else
+ err = access_process_vm(target, fp + off,
+ winbuf, copy, 1);
+ }
+
+ tstate_save = regs->tstate &~ TSTATE_DEBUGCHANGE;
+
+ if (err == 0)
+ err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ ®s->tstate, GENREG_TSTATE * 8,
+ GENREG_Y * 8);
+
+ regs->tstate &= TSTATE_DEBUGCHANGE;
+ regs->tstate |= tstate_save;
+
+ if (err == 0 && count > 0) {
+ if (kbuf)
+ regs->y = *(unsigned long *) kbuf;
+ else if (get_user(regs->y, (unsigned long __user *) ubuf))
+ return -EFAULT;
+ }
+
+ return err;
}
-static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
+#define FPREG_F0 0
+#define FPREG_FSR 32
+#define FPREG_GSR 33
+#define FPREG_FPRS 34
+
+#define SPARC64_NFPREGS 35
+
+static int fpregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
{
- regs->u_regs[UREG_I0] = value;
- regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY);
- regs->tpc = regs->tnpc;
- regs->tnpc += 4;
+ struct thread_info *t = task_thread_info(target);
+ int err;
+
+ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ t->fpregs, FPREG_F0 * 8, FPREG_FSR * 8);
+
+ if (err == 0)
+ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &t->xfsr[0], FPREG_FSR * 8,
+ FPREG_GSR * 8);
+
+ if (err == 0)
+ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &t->gsr[0], FPREG_GSR * 8,
+ FPREG_FPRS * 8);
+
+ if (err == 0 && count > 0) {
+ struct pt_regs *regs = task_pt_regs(target);
+
+ if (kbuf)
+ *(unsigned long *) kbuf = regs->fprs;
+ else if (put_user(regs->fprs, (unsigned long __user *) ubuf))
+ return -EFAULT;
+ }
+
+ return err;
}
-static inline void
-pt_succ_return_linux(struct pt_regs *regs, unsigned long value, void __user *addr)
+static int fpregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
{
- if (test_thread_flag(TIF_32BIT)) {
- if (put_user(value, (unsigned int __user *) addr)) {
- pt_error_return(regs, EFAULT);
- return;
- }
- } else {
- if (put_user(value, (long __user *) addr)) {
- pt_error_return(regs, EFAULT);
- return;
- }
+ struct thread_info *t = task_thread_info(target);
+ int err;
+
+ err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ t->fpregs, FPREG_F0 * 8, FPREG_FSR * 8);
+
+ if (err == 0)
+ err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &t->xfsr[0], FPREG_FSR * 8,
+ FPREG_GSR * 8);
+
+ if (err == 0)
+ err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &t->gsr[0], FPREG_GSR * 8,
+ FPREG_FPRS * 8);
+
+ if (err == 0 && count > 0) {
+ struct pt_regs *regs = task_pt_regs(target);
+
+ if (kbuf)
+ regs->fprs = *(unsigned long *) kbuf;
+ else if (get_user(regs->fprs, (unsigned long __user *) ubuf))
+ return -EFAULT;
}
- regs->u_regs[UREG_I0] = 0;
- regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY);
- regs->tpc = regs->tnpc;
- regs->tnpc += 4;
-}
-
-static void
-pt_os_succ_return (struct pt_regs *regs, unsigned long val, void __user *addr)
-{
- if (current->personality == PER_SUNOS)
- pt_succ_return (regs, val);
- else
- pt_succ_return_linux (regs, val, addr);
-}
-
-/* #define ALLOW_INIT_TRACING */
-/* #define DEBUG_PTRACE */
-
-#ifdef DEBUG_PTRACE
-char *pt_rq [] = {
- /* 0 */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR",
- /* 4 */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT",
- /* 8 */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH",
- /* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS",
- /* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT",
- /* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown",
- /* 24 */ "SYSCALL", ""
+
+ return err;
+}
+
+static const struct utrace_regset native_regsets[] = {
+ {
+ .n = SPARC64_NGREGS,
+ .size = sizeof(long), .align = sizeof(long),
+ .get = genregs_get, .set = genregs_set
+ },
+ {
+ .n = SPARC64_NFPREGS,
+ .size = sizeof(long), .align = sizeof(long),
+ .get = fpregs_get, .set = fpregs_set
+ },
};
-#endif
-/*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure single step bits etc are not set.
- */
-void ptrace_disable(struct task_struct *child)
+const struct utrace_regset_view utrace_sparc64_native_view = {
+ .name = UTS_MACHINE, .e_machine = ELF_ARCH,
+ .regsets = native_regsets,
+ .n = sizeof native_regsets / sizeof native_regsets[0],
+};
+EXPORT_SYMBOL_GPL(utrace_sparc64_native_view);
+
+#ifdef CONFIG_COMPAT
+
+#define GENREG32_G0 0
+#define GENREG32_O0 8
+#define GENREG32_L0 16
+#define GENREG32_I0 24
+#define GENREG32_PSR 32
+#define GENREG32_PC 33
+#define GENREG32_NPC 34
+#define GENREG32_Y 35
+#define GENREG32_WIM 36
+#define GENREG32_TBR 37
+
+#define SPARC32_NGREGS 38
+
+static int genregs32_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
{
- /* nothing to do */
+ struct pt_regs *regs = task_pt_regs(target);
+
+ while (count > 0 && pos < (GENREG32_L0 * 4)) {
+ u32 val = regs->u_regs[(pos - (GENREG32_G0*4))/sizeof(u32)];
+ if (kbuf) {
+ *(u32 *) kbuf = val;
+ kbuf += sizeof(u32);
+ } else if (put_user(val, (u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+ }
+
+ if (count > 0 && pos < (GENREG32_PSR * 4)) {
+ struct thread_info *t = task_thread_info(target);
+ unsigned long fp;
+ u32 rwindow[16];
+ int wsaved;
+
+ if (target == current)
+ flushw_user();
+
+ wsaved = __thread_flag_byte_ptr(t)[TI_FLAG_BYTE_WSAVED];
+ fp = regs->u_regs[UREG_FP] & 0xffffffffUL;
+ if (wsaved && t->rwbuf_stkptrs[wsaved - 1] == fp) {
+ int i;
+ for (i = 0; i < 8; i++)
+ rwindow[i + 0] =
+ t->reg_window[wsaved-1].locals[i];
+ for (i = 0; i < 8; i++)
+ rwindow[i + 8] =
+ t->reg_window[wsaved-1].ins[i];
+ } else {
+ int err;
+
+ if (target == current) {
+ err = 0;
+ if (copy_from_user(rwindow, (void __user *) fp,
+ 16 * sizeof(u32)))
+ err = -EFAULT;
+ } else
+ err = access_process_vm(target, fp, rwindow,
+ 16 * sizeof(u32), 0);
+ if (err)
+ return err;
+ }
+
+ while (count > 0 && pos < (GENREG32_PSR * 4)) {
+ u32 val = rwindow[(pos - (GENREG32_L0*4))/sizeof(u32)];
+
+ if (kbuf) {
+ *(u32 *) kbuf = val;
+ kbuf += sizeof(u32);
+ } else if (put_user(val, (u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+ }
+ }
+
+ if (count > 0 && pos == (GENREG32_PSR * 4)) {
+ u32 psr = tstate_to_psr(regs->tstate);
+
+ if (kbuf) {
+ *(u32 *) kbuf = psr;
+ kbuf += sizeof(u32);
+ } else if (put_user(psr, (u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+ }
+
+ if (count > 0 && pos == (GENREG32_PC * 4)) {
+ u32 val = regs->tpc;
+
+ if (kbuf) {
+ *(u32 *) kbuf = val;
+ kbuf += sizeof(u32);
+ } else if (put_user(val, (u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+ }
+
+ if (count > 0 && pos == (GENREG32_NPC * 4)) {
+ u32 val = regs->tnpc;
+
+ if (kbuf) {
+ *(u32 *) kbuf = val;
+ kbuf += sizeof(u32);
+ } else if (put_user(val, (u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+ }
+
+ if (count > 0 && pos == (GENREG32_Y * 4)) {
+ if (kbuf) {
+ *(u32 *) kbuf = regs->y;
+ kbuf += sizeof(u32);
+ } else if (put_user(regs->y, (u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+ }
+
+ if (count > 0) {
+ if (kbuf)
+ memset(kbuf, 0, count);
+ else if (clear_user(ubuf, count))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int genregs32_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct pt_regs *regs = task_pt_regs(target);
+
+ while (count > 0 && pos < (GENREG32_L0 * 4)) {
+ unsigned long *loc;
+ loc = ®s->u_regs[(pos - (GENREG32_G0*4))/sizeof(u32)];
+ if (kbuf) {
+ *loc = *(u32 *) kbuf;
+ kbuf += sizeof(u32);
+ } else if (get_user(*loc, (u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+ }
+
+ if (count > 0 && pos < (GENREG32_PSR * 4)) {
+ unsigned long fp;
+ u32 regbuf[16];
+ unsigned int off, copy;
+ int err;
+
+ if (target == current)
+ flushw_user();
+
+ copy = (GENREG32_PSR * 4) - pos;
+ if (count < copy)
+ copy = count;
+ BUG_ON(copy > 16 * sizeof(u32));
+
+ fp = regs->u_regs[UREG_FP] & 0xffffffffUL;
+ off = pos - (GENREG32_L0 * 4);
+ if (kbuf) {
+ memcpy(regbuf, kbuf, copy);
+ kbuf += copy;
+ } else if (copy_from_user(regbuf, ubuf, copy))
+ return -EFAULT;
+ else
+ ubuf += copy;
+ pos += copy;
+ count -= copy;
+
+ if (target == current) {
+ err = 0;
+ if (copy_to_user((void __user *) fp + off,
+ regbuf, count))
+ err = -EFAULT;
+ } else
+ err = access_process_vm(target, fp + off,
+ regbuf, count, 1);
+ if (err)
+ return err;
+ }
+
+ if (count > 0 && pos == (GENREG32_PSR * 4)) {
+ unsigned long tstate, tstate_save;
+ u32 psr;
+
+ tstate_save = regs->tstate&~(TSTATE_ICC|TSTATE_XCC);
+
+ if (kbuf) {
+ psr = *(u32 *) kbuf;
+ kbuf += sizeof(u32);
+ } else if (get_user(psr, (u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+
+ tstate = psr_to_tstate_icc(psr);
+ regs->tstate = tstate_save | tstate;
+ }
+
+ if (count > 0 && pos == (GENREG32_PC * 4)) {
+ if (kbuf) {
+ regs->tpc = *(u32 *) kbuf;
+ kbuf += sizeof(u32);
+ } else if (get_user(regs->tpc, (u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+ }
+
+ if (count > 0 && pos == (GENREG32_NPC * 4)) {
+ if (kbuf) {
+ regs->tnpc = *(u32 *) kbuf;
+ kbuf += sizeof(u32);
+ } else if (get_user(regs->tnpc, (u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+ }
+
+ if (count > 0 && pos == (GENREG32_Y * 4)) {
+ if (kbuf) {
+ regs->y = *(u32 *) kbuf;
+ kbuf += sizeof(u32);
+ } else if (get_user(regs->y, (u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+ }
+
+ /* Ignore WIM and TBR */
+
+ return 0;
}
+#define FPREG32_F0 0
+#define FPREG32_FSR 32
+
+#define SPARC32_NFPREGS 33
+
+static int fpregs32_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ struct thread_info *t = task_thread_info(target);
+ int err;
+
+ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ t->fpregs, FPREG32_F0 * 4,
+ FPREG32_FSR * 4);
+
+ if (err == 0 && count > 0) {
+ if (kbuf) {
+ *(u32 *) kbuf = t->xfsr[0];
+ } else if (put_user(t->xfsr[0], (u32 __user *) ubuf))
+ return -EFAULT;
+ }
+
+ return err;
+}
+
+static int fpregs32_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct thread_info *t = task_thread_info(target);
+ int err;
+
+ err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ t->fpregs, FPREG32_F0 * 4,
+ FPREG32_FSR * 4);
+
+ if (err == 0 && count > 0) {
+ u32 fsr;
+ if (kbuf) {
+ fsr = *(u32 *) kbuf;
+ } else if (get_user(fsr, (u32 __user *) ubuf))
+ return -EFAULT;
+ t->xfsr[0] = (t->xfsr[0] & 0xffffffff00000000UL) | fsr;
+ }
+
+ return 0;
+}
+
+static const struct utrace_regset sparc32_regsets[] = {
+ {
+ .n = SPARC32_NGREGS,
+ .size = sizeof(u32), .align = sizeof(u32),
+ .get = genregs32_get, .set = genregs32_set
+ },
+ {
+ .n = SPARC32_NFPREGS,
+ .size = sizeof(u32), .align = sizeof(u32),
+ .get = fpregs32_get, .set = fpregs32_set
+ },
+};
+
+const struct utrace_regset_view utrace_sparc32_view = {
+ .name = "sparc", .e_machine = EM_SPARC,
+ .regsets = sparc32_regsets,
+ .n = sizeof sparc32_regsets / sizeof sparc32_regsets[0],
+};
+EXPORT_SYMBOL_GPL(utrace_sparc32_view);
+
+#endif /* CONFIG_COMPAT */
+
/* To get the necessary page struct, access_process_vm() first calls
* get_user_pages(). This has done a flush_dcache_page() on the
* accessed page. Then our caller (copy_{to,from}_user_page()) did
@@ -167,484 +661,124 @@ void flush_ptrace_access(struct vm_area_
}
}
-asmlinkage void do_ptrace(struct pt_regs *regs)
-{
- int request = regs->u_regs[UREG_I0];
- pid_t pid = regs->u_regs[UREG_I1];
- unsigned long addr = regs->u_regs[UREG_I2];
- unsigned long data = regs->u_regs[UREG_I3];
- unsigned long addr2 = regs->u_regs[UREG_I4];
- struct task_struct *child;
- int ret;
-
- if (test_thread_flag(TIF_32BIT)) {
- addr &= 0xffffffffUL;
- data &= 0xffffffffUL;
- addr2 &= 0xffffffffUL;
- }
- lock_kernel();
-#ifdef DEBUG_PTRACE
- {
- char *s;
-
- if ((request >= 0) && (request <= 24))
- s = pt_rq [request];
- else
- s = "unknown";
-
- if (request == PTRACE_POKEDATA && data == 0x91d02001){
- printk ("do_ptrace: breakpoint pid=%d, addr=%016lx addr2=%016lx\n",
- pid, addr, addr2);
- } else
- printk("do_ptrace: rq=%s(%d) pid=%d addr=%016lx data=%016lx addr2=%016lx\n",
- s, request, pid, addr, data, addr2);
- }
-#endif
- if (request == PTRACE_TRACEME) {
- ret = ptrace_traceme();
- pt_succ_return(regs, 0);
- goto out;
- }
-
- child = ptrace_get_task_struct(pid);
- if (IS_ERR(child)) {
- ret = PTR_ERR(child);
- pt_error_return(regs, -ret);
- goto out;
- }
-
- if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
- || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
- if (ptrace_attach(child)) {
- pt_error_return(regs, EPERM);
- goto out_tsk;
- }
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
-
- ret = ptrace_check_attach(child, request == PTRACE_KILL);
- if (ret < 0) {
- pt_error_return(regs, -ret);
- goto out_tsk;
- }
-
- if (!(test_thread_flag(TIF_32BIT)) &&
- ((request == PTRACE_READDATA64) ||
- (request == PTRACE_WRITEDATA64) ||
- (request == PTRACE_READTEXT64) ||
- (request == PTRACE_WRITETEXT64) ||
- (request == PTRACE_PEEKTEXT64) ||
- (request == PTRACE_POKETEXT64) ||
- (request == PTRACE_PEEKDATA64) ||
- (request == PTRACE_POKEDATA64))) {
- addr = regs->u_regs[UREG_G2];
- addr2 = regs->u_regs[UREG_G3];
- request -= 30; /* wheee... */
- }
-
- switch(request) {
- case PTRACE_PEEKUSR:
- if (addr != 0)
- pt_error_return(regs, EIO);
- else
- pt_succ_return(regs, 0);
- goto out_tsk;
-
- case PTRACE_PEEKTEXT: /* read word at location addr. */
- case PTRACE_PEEKDATA: {
- unsigned long tmp64;
- unsigned int tmp32;
- int res, copied;
-
- res = -EIO;
- if (test_thread_flag(TIF_32BIT)) {
- copied = access_process_vm(child, addr,
- &tmp32, sizeof(tmp32), 0);
- tmp64 = (unsigned long) tmp32;
- if (copied == sizeof(tmp32))
- res = 0;
- } else {
- copied = access_process_vm(child, addr,
- &tmp64, sizeof(tmp64), 0);
- if (copied == sizeof(tmp64))
- res = 0;
- }
- if (res < 0)
- pt_error_return(regs, -res);
- else
- pt_os_succ_return(regs, tmp64, (void __user *) data);
- goto out_tsk;
- }
-
- case PTRACE_POKETEXT: /* write the word at location addr. */
- case PTRACE_POKEDATA: {
- unsigned long tmp64;
- unsigned int tmp32;
- int copied, res = -EIO;
-
- if (test_thread_flag(TIF_32BIT)) {
- tmp32 = data;
- copied = access_process_vm(child, addr,
- &tmp32, sizeof(tmp32), 1);
- if (copied == sizeof(tmp32))
- res = 0;
- } else {
- tmp64 = data;
- copied = access_process_vm(child, addr,
- &tmp64, sizeof(tmp64), 1);
- if (copied == sizeof(tmp64))
- res = 0;
- }
- if (res < 0)
- pt_error_return(regs, -res);
- else
- pt_succ_return(regs, res);
- goto out_tsk;
- }
-
- case PTRACE_GETREGS: {
- struct pt_regs32 __user *pregs =
- (struct pt_regs32 __user *) addr;
- struct pt_regs *cregs = task_pt_regs(child);
- int rval;
-
- if (__put_user(tstate_to_psr(cregs->tstate), (&pregs->psr)) ||
- __put_user(cregs->tpc, (&pregs->pc)) ||
- __put_user(cregs->tnpc, (&pregs->npc)) ||
- __put_user(cregs->y, (&pregs->y))) {
- pt_error_return(regs, EFAULT);
- goto out_tsk;
- }
- for (rval = 1; rval < 16; rval++)
- if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) {
- pt_error_return(regs, EFAULT);
- goto out_tsk;
- }
- pt_succ_return(regs, 0);
-#ifdef DEBUG_PTRACE
- printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]);
-#endif
- goto out_tsk;
- }
-
- case PTRACE_GETREGS64: {
- struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
- struct pt_regs *cregs = task_pt_regs(child);
- unsigned long tpc = cregs->tpc;
- int rval;
-
- if ((task_thread_info(child)->flags & _TIF_32BIT) != 0)
- tpc &= 0xffffffff;
- if (__put_user(cregs->tstate, (&pregs->tstate)) ||
- __put_user(tpc, (&pregs->tpc)) ||
- __put_user(cregs->tnpc, (&pregs->tnpc)) ||
- __put_user(cregs->y, (&pregs->y))) {
- pt_error_return(regs, EFAULT);
- goto out_tsk;
- }
- for (rval = 1; rval < 16; rval++)
- if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) {
- pt_error_return(regs, EFAULT);
- goto out_tsk;
- }
- pt_succ_return(regs, 0);
-#ifdef DEBUG_PTRACE
- printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]);
-#endif
- goto out_tsk;
- }
-
- case PTRACE_SETREGS: {
- struct pt_regs32 __user *pregs =
- (struct pt_regs32 __user *) addr;
- struct pt_regs *cregs = task_pt_regs(child);
- unsigned int psr, pc, npc, y;
- int i;
-
- /* Must be careful, tracing process can only set certain
- * bits in the psr.
- */
- if (__get_user(psr, (&pregs->psr)) ||
- __get_user(pc, (&pregs->pc)) ||
- __get_user(npc, (&pregs->npc)) ||
- __get_user(y, (&pregs->y))) {
- pt_error_return(regs, EFAULT);
- goto out_tsk;
- }
- cregs->tstate &= ~(TSTATE_ICC);
- cregs->tstate |= psr_to_tstate_icc(psr);
- if (!((pc | npc) & 3)) {
- cregs->tpc = pc;
- cregs->tnpc = npc;
- }
- cregs->y = y;
- for (i = 1; i < 16; i++) {
- if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) {
- pt_error_return(regs, EFAULT);
- goto out_tsk;
- }
- }
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
-
- case PTRACE_SETREGS64: {
- struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
- struct pt_regs *cregs = task_pt_regs(child);
- unsigned long tstate, tpc, tnpc, y;
- int i;
-
- /* Must be careful, tracing process can only set certain
- * bits in the psr.
- */
- if (__get_user(tstate, (&pregs->tstate)) ||
- __get_user(tpc, (&pregs->tpc)) ||
- __get_user(tnpc, (&pregs->tnpc)) ||
- __get_user(y, (&pregs->y))) {
- pt_error_return(regs, EFAULT);
- goto out_tsk;
- }
- if ((task_thread_info(child)->flags & _TIF_32BIT) != 0) {
- tpc &= 0xffffffff;
- tnpc &= 0xffffffff;
- }
- tstate &= (TSTATE_ICC | TSTATE_XCC);
- cregs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
- cregs->tstate |= tstate;
- if (!((tpc | tnpc) & 3)) {
- cregs->tpc = tpc;
- cregs->tnpc = tnpc;
- }
- cregs->y = y;
- for (i = 1; i < 16; i++) {
- if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) {
- pt_error_return(regs, EFAULT);
- goto out_tsk;
- }
- }
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
-
- case PTRACE_GETFPREGS: {
- struct fps {
- unsigned int regs[32];
- unsigned int fsr;
- unsigned int flags;
- unsigned int extra;
- unsigned int fpqd;
- struct fq {
- unsigned int insnaddr;
- unsigned int insn;
- } fpq[16];
- };
- struct fps __user *fps = (struct fps __user *) addr;
- unsigned long *fpregs = task_thread_info(child)->fpregs;
-
- if (copy_to_user(&fps->regs[0], fpregs,
- (32 * sizeof(unsigned int))) ||
- __put_user(task_thread_info(child)->xfsr[0], (&fps->fsr)) ||
- __put_user(0, (&fps->fpqd)) ||
- __put_user(0, (&fps->flags)) ||
- __put_user(0, (&fps->extra)) ||
- clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) {
- pt_error_return(regs, EFAULT);
- goto out_tsk;
- }
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
-
- case PTRACE_GETFPREGS64: {
- struct fps {
- unsigned int regs[64];
- unsigned long fsr;
- };
- struct fps __user *fps = (struct fps __user *) addr;
- unsigned long *fpregs = task_thread_info(child)->fpregs;
-
- if (copy_to_user(&fps->regs[0], fpregs,
- (64 * sizeof(unsigned int))) ||
- __put_user(task_thread_info(child)->xfsr[0], (&fps->fsr))) {
- pt_error_return(regs, EFAULT);
- goto out_tsk;
- }
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
-
- case PTRACE_SETFPREGS: {
- struct fps {
- unsigned int regs[32];
- unsigned int fsr;
- unsigned int flags;
- unsigned int extra;
- unsigned int fpqd;
- struct fq {
- unsigned int insnaddr;
- unsigned int insn;
- } fpq[16];
- };
- struct fps __user *fps = (struct fps __user *) addr;
- unsigned long *fpregs = task_thread_info(child)->fpregs;
- unsigned fsr;
-
- if (copy_from_user(fpregs, &fps->regs[0],
- (32 * sizeof(unsigned int))) ||
- __get_user(fsr, (&fps->fsr))) {
- pt_error_return(regs, EFAULT);
- goto out_tsk;
- }
- task_thread_info(child)->xfsr[0] &= 0xffffffff00000000UL;
- task_thread_info(child)->xfsr[0] |= fsr;
- if (!(task_thread_info(child)->fpsaved[0] & FPRS_FEF))
- task_thread_info(child)->gsr[0] = 0;
- task_thread_info(child)->fpsaved[0] |= (FPRS_FEF | FPRS_DL);
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
-
- case PTRACE_SETFPREGS64: {
- struct fps {
- unsigned int regs[64];
- unsigned long fsr;
- };
- struct fps __user *fps = (struct fps __user *) addr;
- unsigned long *fpregs = task_thread_info(child)->fpregs;
-
- if (copy_from_user(fpregs, &fps->regs[0],
- (64 * sizeof(unsigned int))) ||
- __get_user(task_thread_info(child)->xfsr[0], (&fps->fsr))) {
- pt_error_return(regs, EFAULT);
- goto out_tsk;
- }
- if (!(task_thread_info(child)->fpsaved[0] & FPRS_FEF))
- task_thread_info(child)->gsr[0] = 0;
- task_thread_info(child)->fpsaved[0] |= (FPRS_FEF | FPRS_DL | FPRS_DU);
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
+#ifdef CONFIG_PTRACE
+static const struct ptrace_layout_segment sparc64_getregs_layout[] = {
+ { 0, offsetof(struct pt_regs, u_regs[15]), 0, sizeof(long) },
+ { offsetof(struct pt_regs, u_regs[15]),
+ offsetof(struct pt_regs, tstate),
+ -1, 0 },
+ { offsetof(struct pt_regs, tstate), offsetof(struct pt_regs, y),
+ 0, 32 * sizeof(long) },
+ {0, 0, -1, 0}
+};
- case PTRACE_READTEXT:
- case PTRACE_READDATA: {
- int res = ptrace_readdata(child, addr,
- (char __user *)addr2, data);
- if (res == data) {
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- if (res >= 0)
- res = -EIO;
- pt_error_return(regs, -res);
- goto out_tsk;
- }
+int arch_ptrace(long *request, struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ unsigned long addr, unsigned long data,
+ long *retval)
+{
+ void __user *uaddr = (void __user *) addr;
+ struct pt_regs *uregs = uaddr;
+ int err = -ENOSYS;
+
+ switch (*request) {
+ case PTRACE_GETREGS64:
+ err = ptrace_layout_access(child, engine,
+ &utrace_sparc64_native_view,
+ sparc64_getregs_layout,
+ 0, offsetof(struct pt_regs, y),
+ uaddr, NULL, 0);
+ if (!err &&
+ (put_user(task_pt_regs(child)->y, &uregs->y) ||
+ put_user(task_pt_regs(child)->fprs, &uregs->fprs)))
+ err = -EFAULT;
+ break;
- case PTRACE_WRITETEXT:
- case PTRACE_WRITEDATA: {
- int res = ptrace_writedata(child, (char __user *) addr2,
- addr, data);
- if (res == data) {
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- if (res >= 0)
- res = -EIO;
- pt_error_return(regs, -res);
- goto out_tsk;
- }
- case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */
- addr = 1;
-
- case PTRACE_CONT: { /* restart after signal. */
- if (!valid_signal(data)) {
- pt_error_return(regs, EIO);
- goto out_tsk;
- }
+ case PTRACE_SETREGS64:
+ err = ptrace_layout_access(child, engine,
+ &utrace_sparc64_native_view,
+ sparc64_getregs_layout,
+ 0, offsetof(struct pt_regs, y),
+ uaddr, NULL, 1);
+ if (!err &&
+ (get_user(task_pt_regs(child)->y, &uregs->y) ||
+ get_user(task_pt_regs(child)->fprs, &uregs->fprs)))
+ err = -EFAULT;
+ break;
- if (request == PTRACE_SYSCALL) {
- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- } else {
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- }
+ case PTRACE_GETFPREGS64:
+ case PTRACE_SETFPREGS64:
+ err = ptrace_regset_access(child, engine,
+ utrace_native_view(current),
+ 2, 0, 34 * sizeof(long), uaddr,
+ (*request == PTRACE_SETFPREGS64));
+ break;
- child->exit_code = data;
-#ifdef DEBUG_PTRACE
- printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", child->comm,
- child->pid, child->exit_code,
- task_pt_regs(child)->tpc,
- task_pt_regs(child)->tnpc);
+ case PTRACE_SUNDETACH:
+ *request = PTRACE_DETACH;
+ break;
-#endif
- wake_up_process(child);
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
+ default:
+ break;
+ };
+ return err;
+}
-/*
- * make the child exit. Best I can do is send it a sigkill.
- * perhaps it should be put in the status that it wants to
- * exit.
- */
- case PTRACE_KILL: {
- if (child->exit_state == EXIT_ZOMBIE) { /* already dead */
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- child->exit_code = SIGKILL;
- wake_up_process(child);
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
+#ifdef CONFIG_COMPAT
+static const struct ptrace_layout_segment sparc32_getregs_layout[] = {
+ { 0, offsetof(struct pt_regs32, u_regs[0]),
+ 0, GENREG32_PSR * sizeof(u32) },
+ { offsetof(struct pt_regs32, u_regs[0]),
+ offsetof(struct pt_regs32, u_regs[15]),
+ 0, 1 * sizeof(u32) },
+ { offsetof(struct pt_regs32, u_regs[15]), sizeof(struct pt_regs32),
+ -1, 0 },
+ {0, 0, -1, 0}
+};
- case PTRACE_SUNDETACH: { /* detach a process that was attached. */
- int error = ptrace_detach(child, data);
- if (error) {
- pt_error_return(regs, EIO);
- goto out_tsk;
- }
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
+int arch_compat_ptrace(compat_long_t *request, struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ compat_ulong_t addr, compat_ulong_t data,
+ compat_long_t *retval)
+{
+ void __user *uaddr = (void __user *) (unsigned long) addr;
+ int err = -ENOSYS;
- /* PTRACE_DUMPCORE unsupported... */
+ switch (*request) {
+ case PTRACE_GETREGS:
+ case PTRACE_SETREGS:
+ err = ptrace_layout_access(child, engine,
+ &utrace_sparc32_view,
+ sparc32_getregs_layout,
+ 0, sizeof(struct pt_regs32),
+ uaddr, NULL,
+ (*request ==
+ PTRACE_SETREGS));
+ break;
- case PTRACE_GETEVENTMSG: {
- int err;
+ case PTRACE_GETFPREGS:
+ case PTRACE_SETFPREGS:
+ err = ptrace_whole_regset(child, engine, addr, 1,
+ (*request == PTRACE_SETFPREGS));
+ break;
- if (test_thread_flag(TIF_32BIT))
- err = put_user(child->ptrace_message,
- (unsigned int __user *) data);
- else
- err = put_user(child->ptrace_message,
- (unsigned long __user *) data);
- if (err)
- pt_error_return(regs, -err);
- else
- pt_succ_return(regs, 0);
+ case PTRACE_SUNDETACH:
+ *request = PTRACE_DETACH;
break;
- }
- default: {
- int err = ptrace_request(child, request, addr, data);
- if (err)
- pt_error_return(regs, -err);
- else
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- }
-out_tsk:
- if (child)
- put_task_struct(child);
-out:
- unlock_kernel();
+ default:
+ break;
+ };
+ return err;
}
+#endif /* CONFIG_COMPAT */
+#endif /* CONFIG_PTRACE */
asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p)
{
/* do the secure computing check first */
- secure_computing(regs->u_regs[UREG_G1]);
+ if (!syscall_exit_p)
+ secure_computing(regs->u_regs[UREG_G1]);
if (unlikely(current->audit_context) && syscall_exit_p) {
unsigned long tstate = regs->tstate;
@@ -656,26 +790,9 @@ asmlinkage void syscall_trace(struct pt_
audit_syscall_exit(result, regs->u_regs[UREG_I0]);
}
- if (!(current->ptrace & PT_PTRACED))
- goto out;
-
- if (!test_thread_flag(TIF_SYSCALL_TRACE))
- goto out;
-
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
- ? 0x80 : 0));
-
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
- }
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall(regs, syscall_exit_p);
-out:
if (unlikely(current->audit_context) && !syscall_exit_p)
audit_syscall_entry((test_thread_flag(TIF_32BIT) ?
AUDIT_ARCH_SPARC :
--- linux-2.6/arch/sparc64/kernel/Makefile.utrace-ptrace-compat
+++ linux-2.6/arch/sparc64/kernel/Makefile
@@ -5,6 +5,8 @@
EXTRA_AFLAGS := -ansi
EXTRA_CFLAGS := -Werror
+CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
+
extra-y := head.o init_task.o vmlinux.lds
obj-y := process.o setup.o cpu.o idprom.o \
--- linux-2.6/arch/sparc64/kernel/entry.S.utrace-ptrace-compat
+++ linux-2.6/arch/sparc64/kernel/entry.S
@@ -1410,7 +1410,6 @@ execve_merge:
.globl sys_pipe, sys_sigpause, sys_nis_syscall
.globl sys_rt_sigreturn
- .globl sys_ptrace
.globl sys_sigaltstack
.align 32
sys_pipe: ba,pt %xcc, sparc_pipe
@@ -1453,11 +1452,6 @@ sys32_rt_sigreturn:
add %o7, 1f-.-4, %o7
nop
#endif
-sys_ptrace: add %sp, PTREGS_OFF, %o0
- call do_ptrace
- add %o7, 1f-.-4, %o7
- nop
- .align 32
1: ldx [%curptr + TI_FLAGS], %l5
andcc %l5, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT), %g0
be,pt %icc, rtrap
--- linux-2.6/arch/sparc64/kernel/binfmt_aout32.c.utrace-ptrace-compat
+++ linux-2.6/arch/sparc64/kernel/binfmt_aout32.c
@@ -335,8 +335,6 @@ beyond_if:
tsb_context_switch(current->mm);
start_thread32(regs, ex.a_entry, current->mm->start_stack);
- if (current->ptrace & PT_PTRACED)
- send_sig(SIGTRAP, current, 0);
return 0;
}
--- linux-2.6/arch/arm/kernel/ptrace.c.utrace-ptrace-compat
+++ linux-2.6/arch/arm/kernel/ptrace.c
@@ -812,34 +812,18 @@ asmlinkage int syscall_trace(int why, st
{
unsigned long ip;
- if (!test_thread_flag(TIF_SYSCALL_TRACE))
- return scno;
- if (!(current->ptrace & PT_PTRACED))
- return scno;
+ if (test_thread_flag(TIF_SYSCALL_TRACE)) {
+ /*
+ * Save IP. IP is used to denote syscall entry/exit:
+ * IP = 0 -> entry, = 1 -> exit
+ */
+ ip = regs->ARM_ip;
+ regs->ARM_ip = why;
- /*
- * Save IP. IP is used to denote syscall entry/exit:
- * IP = 0 -> entry, = 1 -> exit
- */
- ip = regs->ARM_ip;
- regs->ARM_ip = why;
+ tracehook_report_syscall(regs, why);
- current->ptrace_message = scno;
-
- /* the 0x80 provides a way for the tracing parent to distinguish
- between a syscall stop and SIGTRAP delivery */
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
- ? 0x80 : 0));
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
+ regs->ARM_ip = ip;
}
- regs->ARM_ip = ip;
- return current->ptrace_message;
+ return scno;
}
--- linux-2.6/arch/ia64/kernel/mca.c.utrace-ptrace-compat
+++ linux-2.6/arch/ia64/kernel/mca.c
@@ -1515,7 +1515,7 @@ format_mca_init_stack(void *mca_data, un
p->state = TASK_UNINTERRUPTIBLE;
cpu_set(cpu, p->cpus_allowed);
INIT_LIST_HEAD(&p->tasks);
- p->parent = p->real_parent = p->group_leader = p;
+ p->parent = p->group_leader = p;
INIT_LIST_HEAD(&p->children);
INIT_LIST_HEAD(&p->sibling);
strncpy(p->comm, type, sizeof(p->comm)-1);
--- linux-2.6/arch/ia64/kernel/signal.c.utrace-ptrace-compat
+++ linux-2.6/arch/ia64/kernel/signal.c
@@ -10,7 +10,7 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mm.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/smp.h>
@@ -471,6 +471,8 @@ handle_signal (unsigned long sig, struct
sigaddset(¤t->blocked, sig);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
+
+ tracehook_report_handle_signal(sig, ka, oldset, &scr->pt);
return 1;
}
--- linux-2.6/arch/ia64/kernel/fsys.S.utrace-ptrace-compat
+++ linux-2.6/arch/ia64/kernel/fsys.S
@@ -83,29 +83,29 @@ ENTRY(fsys_getppid)
;;
ld4 r9=[r9]
- add r17=IA64_TASK_REAL_PARENT_OFFSET,r17 // r17 = ¤t->group_leader->real_parent
+ add r17=IA64_TASK_PARENT_OFFSET,r17 // r17 = ¤t->group_leader->parent
;;
and r9=TIF_ALLWORK_MASK,r9
-1: ld8 r18=[r17] // r18 = current->group_leader->real_parent
+1: ld8 r18=[r17] // r18 = current->group_leader->parent
;;
cmp.ne p8,p0=0,r9
- add r8=IA64_TASK_TGID_OFFSET,r18 // r8 = ¤t->group_leader->real_parent->tgid
+ add r8=IA64_TASK_TGID_OFFSET,r18 // r8 = ¤t->group_leader->parent->tgid
;;
/*
* The .acq is needed to ensure that the read of tgid has returned its data before
- * we re-check "real_parent".
+ * we re-check "parent".
*/
- ld4.acq r8=[r8] // r8 = current->group_leader->real_parent->tgid
+ ld4.acq r8=[r8] // r8 = current->group_leader->parent->tgid
#ifdef CONFIG_SMP
/*
- * Re-read current->group_leader->real_parent.
+ * Re-read current->group_leader->parent.
*/
- ld8 r19=[r17] // r19 = current->group_leader->real_parent
+ ld8 r19=[r17] // r19 = current->group_leader->parent
(p8) br.spnt.many fsys_fallback_syscall
;;
- cmp.ne p6,p0=r18,r19 // did real_parent change?
+ cmp.ne p6,p0=r18,r19 // did parent change?
mov r19=0 // i must not leak kernel bits...
(p6) br.cond.spnt.few 1b // yes -> redo the read of tgid and the check
;;
--- linux-2.6/arch/ia64/kernel/ptrace.c.utrace-ptrace-compat
+++ linux-2.6/arch/ia64/kernel/ptrace.c
@@ -3,6 +3,9 @@
*
* Copyright (C) 1999-2005 Hewlett-Packard Co
* David Mosberger-Tang <davidm@hpl.hp.com>
+ * Copyright (C) 2006 Intel Co
+ * 2006-08-12 - IA64 Native Utrace implementation support added by
+ * Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
*
* Derived from the x86 and Alpha versions.
*/
@@ -12,18 +15,22 @@
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/smp_lock.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/audit.h>
#include <linux/signal.h>
+#include <linux/module.h>
+#include <asm/tracehook.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/ptrace_offsets.h>
#include <asm/rse.h>
#include <asm/system.h>
#include <asm/uaccess.h>
+#include <asm/elf.h>
#include <asm/unwind.h>
#ifdef CONFIG_PERFMON
#include <asm/perfmon.h>
@@ -547,79 +554,6 @@ ia64_sync_user_rbs (struct task_struct *
return 0;
}
-static inline int
-thread_matches (struct task_struct *thread, unsigned long addr)
-{
- unsigned long thread_rbs_end;
- struct pt_regs *thread_regs;
-
- if (ptrace_check_attach(thread, 0) < 0)
- /*
- * If the thread is not in an attachable state, we'll
- * ignore it. The net effect is that if ADDR happens
- * to overlap with the portion of the thread's
- * register backing store that is currently residing
- * on the thread's kernel stack, then ptrace() may end
- * up accessing a stale value. But if the thread
- * isn't stopped, that's a problem anyhow, so we're
- * doing as well as we can...
- */
- return 0;
-
- thread_regs = task_pt_regs(thread);
- thread_rbs_end = ia64_get_user_rbs_end(thread, thread_regs, NULL);
- if (!on_kernel_rbs(addr, thread_regs->ar_bspstore, thread_rbs_end))
- return 0;
-
- return 1; /* looks like we've got a winner */
-}
-
-/*
- * GDB apparently wants to be able to read the register-backing store
- * of any thread when attached to a given process. If we are peeking
- * or poking an address that happens to reside in the kernel-backing
- * store of another thread, we need to attach to that thread, because
- * otherwise we end up accessing stale data.
- *
- * task_list_lock must be read-locked before calling this routine!
- */
-static struct task_struct *
-find_thread_for_addr (struct task_struct *child, unsigned long addr)
-{
- struct task_struct *p;
- struct mm_struct *mm;
- struct list_head *this, *next;
- int mm_users;
-
- if (!(mm = get_task_mm(child)))
- return child;
-
- /* -1 because of our get_task_mm(): */
- mm_users = atomic_read(&mm->mm_users) - 1;
- if (mm_users <= 1)
- goto out; /* not multi-threaded */
-
- /*
- * Traverse the current process' children list. Every task that
- * one attaches to becomes a child. And it is only attached children
- * of the debugger that are of interest (ptrace_check_attach checks
- * for this).
- */
- list_for_each_safe(this, next, ¤t->children) {
- p = list_entry(this, struct task_struct, sibling);
- if (p->mm != mm)
- continue;
- if (thread_matches(p, addr)) {
- child = p;
- goto out;
- }
- }
-
- out:
- mmput(mm);
- return child;
-}
-
/*
* Write f32-f127 back to task->thread.fph if it has been modified.
*/
@@ -663,6 +597,7 @@ ia64_sync_fph (struct task_struct *task)
psr->dfh = 1;
}
+#if 0
static int
access_fr (struct unw_frame_info *info, int regnum, int hi,
unsigned long *data, int write_access)
@@ -681,6 +616,7 @@ access_fr (struct unw_frame_info *info,
*data = fpval.u.bits[hi];
return ret;
}
+#endif /* access_fr() */
/*
* Change the machine-state of CHILD such that it will return via the normal
@@ -781,321 +717,121 @@ access_nat_bits (struct task_struct *chi
return 0;
}
-static int
-access_uarea (struct task_struct *child, unsigned long addr,
- unsigned long *data, int write_access)
+
+/* "asmlinkage" so the input arguments are preserved... */
+
+asmlinkage void
+syscall_trace_enter (long arg0, long arg1, long arg2, long arg3,
+ long arg4, long arg5, long arg6, long arg7,
+ struct pt_regs regs)
{
- unsigned long *ptr, regnum, urbs_end, rnat_addr, cfm;
- struct switch_stack *sw;
- struct pt_regs *pt;
-# define pt_reg_addr(pt, reg) ((void *) \
- ((unsigned long) (pt) \
- + offsetof(struct pt_regs, reg)))
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall(®s, 0);
+ if (unlikely(current->audit_context)) {
+ long syscall;
+ int arch;
- pt = task_pt_regs(child);
- sw = (struct switch_stack *) (child->thread.ksp + 16);
+ if (IS_IA32_PROCESS(®s)) {
+ syscall = regs.r1;
+ arch = AUDIT_ARCH_I386;
+ } else {
+ syscall = regs.r15;
+ arch = AUDIT_ARCH_IA64;
+ }
- if ((addr & 0x7) != 0) {
- dprintk("ptrace: unaligned register address 0x%lx\n", addr);
- return -1;
+ audit_syscall_entry(arch, syscall, arg0, arg1, arg2, arg3);
}
- if (addr < PT_F127 + 16) {
- /* accessing fph */
- if (write_access)
- ia64_sync_fph(child);
- else
- ia64_flush_fph(child);
- ptr = (unsigned long *)
- ((unsigned long) &child->thread.fph + addr);
- } else if ((addr >= PT_F10) && (addr < PT_F11 + 16)) {
- /* scratch registers untouched by kernel (saved in pt_regs) */
- ptr = pt_reg_addr(pt, f10) + (addr - PT_F10);
- } else if (addr >= PT_F12 && addr < PT_F15 + 16) {
- /*
- * Scratch registers untouched by kernel (saved in
- * switch_stack).
- */
- ptr = (unsigned long *) ((long) sw
- + (addr - PT_NAT_BITS - 32));
- } else if (addr < PT_AR_LC + 8) {
- /* preserved state: */
- struct unw_frame_info info;
- char nat = 0;
- int ret;
+}
- unw_init_from_blocked_task(&info, child);
- if (unw_unwind_to_user(&info) < 0)
- return -1;
+/* "asmlinkage" so the input arguments are preserved... */
- switch (addr) {
- case PT_NAT_BITS:
- return access_nat_bits(child, pt, &info,
- data, write_access);
+asmlinkage void
+syscall_trace_leave (long arg0, long arg1, long arg2, long arg3,
+ long arg4, long arg5, long arg6, long arg7,
+ struct pt_regs regs)
+{
+ if (unlikely(current->audit_context)) {
+ int success = AUDITSC_RESULT(regs.r10);
+ long result = regs.r8;
- case PT_R4: case PT_R5: case PT_R6: case PT_R7:
- if (write_access) {
- /* read NaT bit first: */
- unsigned long dummy;
+ if (success != AUDITSC_SUCCESS)
+ result = -result;
+ audit_syscall_exit(success, result);
+ }
- ret = unw_get_gr(&info, (addr - PT_R4)/8 + 4,
- &dummy, &nat);
- if (ret < 0)
- return ret;
- }
- return unw_access_gr(&info, (addr - PT_R4)/8 + 4, data,
- &nat, write_access);
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall(®s, 1);
+}
- case PT_B1: case PT_B2: case PT_B3:
- case PT_B4: case PT_B5:
- return unw_access_br(&info, (addr - PT_B1)/8 + 1, data,
- write_access);
-
- case PT_AR_EC:
- return unw_access_ar(&info, UNW_AR_EC, data,
- write_access);
-
- case PT_AR_LC:
- return unw_access_ar(&info, UNW_AR_LC, data,
- write_access);
-
- default:
- if (addr >= PT_F2 && addr < PT_F5 + 16)
- return access_fr(&info, (addr - PT_F2)/16 + 2,
- (addr & 8) != 0, data,
- write_access);
- else if (addr >= PT_F16 && addr < PT_F31 + 16)
- return access_fr(&info,
- (addr - PT_F16)/16 + 16,
- (addr & 8) != 0,
- data, write_access);
- else {
- dprintk("ptrace: rejecting access to register "
- "address 0x%lx\n", addr);
- return -1;
- }
- }
- } else if (addr < PT_F9+16) {
- /* scratch state */
- switch (addr) {
- case PT_AR_BSP:
- /*
- * By convention, we use PT_AR_BSP to refer to
- * the end of the user-level backing store.
- * Use ia64_rse_skip_regs(PT_AR_BSP, -CFM.sof)
- * to get the real value of ar.bsp at the time
- * the kernel was entered.
- *
- * Furthermore, when changing the contents of
- * PT_AR_BSP (or PT_CFM) we MUST copy any
- * users-level stacked registers that are
- * stored on the kernel stack back to
- * user-space because otherwise, we might end
- * up clobbering kernel stacked registers.
- * Also, if this happens while the task is
- * blocked in a system call, which convert the
- * state such that the non-system-call exit
- * path is used. This ensures that the proper
- * state will be picked up when resuming
- * execution. However, it *also* means that
- * once we write PT_AR_BSP/PT_CFM, it won't be
- * possible to modify the syscall arguments of
- * the pending system call any longer. This
- * shouldn't be an issue because modifying
- * PT_AR_BSP/PT_CFM generally implies that
- * we're either abandoning the pending system
- * call or that we defer it's re-execution
- * (e.g., due to GDB doing an inferior
- * function call).
- */
- urbs_end = ia64_get_user_rbs_end(child, pt, &cfm);
- if (write_access) {
- if (*data != urbs_end) {
- if (ia64_sync_user_rbs(child, sw,
- pt->ar_bspstore,
- urbs_end) < 0)
- return -1;
- if (in_syscall(pt))
- convert_to_non_syscall(child,
- pt,
- cfm);
- /*
- * Simulate user-level write
- * of ar.bsp:
- */
- pt->loadrs = 0;
- pt->ar_bspstore = *data;
- }
- } else
- *data = urbs_end;
- return 0;
- case PT_CFM:
- urbs_end = ia64_get_user_rbs_end(child, pt, &cfm);
- if (write_access) {
- if (((cfm ^ *data) & PFM_MASK) != 0) {
- if (ia64_sync_user_rbs(child, sw,
- pt->ar_bspstore,
- urbs_end) < 0)
- return -1;
- if (in_syscall(pt))
- convert_to_non_syscall(child,
- pt,
- cfm);
- pt->cr_ifs = ((pt->cr_ifs & ~PFM_MASK)
- | (*data & PFM_MASK));
- }
- } else
- *data = cfm;
- return 0;
+#ifdef CONFIG_UTRACE
- case PT_CR_IPSR:
- if (write_access)
- pt->cr_ipsr = ((*data & IPSR_MASK)
- | (pt->cr_ipsr & ~IPSR_MASK));
- else
- *data = (pt->cr_ipsr & IPSR_MASK);
- return 0;
+/* Utrace implementation starts here */
- case PT_AR_RSC:
- if (write_access)
- pt->ar_rsc = *data | (3 << 2); /* force PL3 */
- else
- *data = pt->ar_rsc;
- return 0;
+typedef struct utrace_get {
+ void *kbuf;
+ void __user *ubuf;
+} utrace_get_t;
+
+typedef struct utrace_set {
+ const void *kbuf;
+ const void __user *ubuf;
+} utrace_set_t;
+
+typedef struct utrace_getset {
+ struct task_struct *target;
+ const struct utrace_regset *regset;
+ union {
+ utrace_get_t get;
+ utrace_set_t set;
+ } u;
+ unsigned int pos;
+ unsigned int count;
+ int ret;
+} utrace_getset_t;
- case PT_AR_RNAT:
- urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
- rnat_addr = (long) ia64_rse_rnat_addr((long *)
- urbs_end);
- if (write_access)
- return ia64_poke(child, sw, urbs_end,
- rnat_addr, *data);
- else
- return ia64_peek(child, sw, urbs_end,
- rnat_addr, data);
+static int
+access_elf_gpreg(struct task_struct *target, struct unw_frame_info *info,
+ unsigned long addr, unsigned long *data, int write_access)
+{
+ struct pt_regs *pt;
+ unsigned long *ptr = NULL;
+ int ret;
+ char nat=0;
- case PT_R1:
- ptr = pt_reg_addr(pt, r1);
- break;
- case PT_R2: case PT_R3:
- ptr = pt_reg_addr(pt, r2) + (addr - PT_R2);
- break;
- case PT_R8: case PT_R9: case PT_R10: case PT_R11:
- ptr = pt_reg_addr(pt, r8) + (addr - PT_R8);
- break;
- case PT_R12: case PT_R13:
- ptr = pt_reg_addr(pt, r12) + (addr - PT_R12);
- break;
- case PT_R14:
- ptr = pt_reg_addr(pt, r14);
- break;
- case PT_R15:
- ptr = pt_reg_addr(pt, r15);
- break;
- case PT_R16: case PT_R17: case PT_R18: case PT_R19:
- case PT_R20: case PT_R21: case PT_R22: case PT_R23:
- case PT_R24: case PT_R25: case PT_R26: case PT_R27:
- case PT_R28: case PT_R29: case PT_R30: case PT_R31:
- ptr = pt_reg_addr(pt, r16) + (addr - PT_R16);
- break;
- case PT_B0:
- ptr = pt_reg_addr(pt, b0);
- break;
- case PT_B6:
- ptr = pt_reg_addr(pt, b6);
- break;
- case PT_B7:
- ptr = pt_reg_addr(pt, b7);
+ pt = task_pt_regs(target);
+ switch (addr) {
+ case ELF_GR_OFFSET(1):
+ ptr = &pt->r1;
break;
- case PT_F6: case PT_F6+8: case PT_F7: case PT_F7+8:
- case PT_F8: case PT_F8+8: case PT_F9: case PT_F9+8:
- ptr = pt_reg_addr(pt, f6) + (addr - PT_F6);
+ case ELF_GR_OFFSET(2):
+ case ELF_GR_OFFSET(3):
+ ptr = (void *)&pt->r2 + (addr - ELF_GR_OFFSET(2));
break;
- case PT_AR_BSPSTORE:
- ptr = pt_reg_addr(pt, ar_bspstore);
- break;
- case PT_AR_UNAT:
- ptr = pt_reg_addr(pt, ar_unat);
- break;
- case PT_AR_PFS:
- ptr = pt_reg_addr(pt, ar_pfs);
- break;
- case PT_AR_CCV:
- ptr = pt_reg_addr(pt, ar_ccv);
- break;
- case PT_AR_FPSR:
- ptr = pt_reg_addr(pt, ar_fpsr);
+ case ELF_GR_OFFSET(4) ... ELF_GR_OFFSET(7):
+ if (write_access) {
+ /* read NaT bit first: */
+ unsigned long dummy;
+
+ ret = unw_get_gr(info, addr/8, &dummy, &nat);
+ if (ret < 0)
+ return ret;
+ }
+ return unw_access_gr(info, addr/8, data, &nat, write_access);
+ case ELF_GR_OFFSET(8) ... ELF_GR_OFFSET(11):
+ ptr = (void *)&pt->r8 + addr - ELF_GR_OFFSET(8);
break;
- case PT_CR_IIP:
- ptr = pt_reg_addr(pt, cr_iip);
+ case ELF_GR_OFFSET(12):
+ case ELF_GR_OFFSET(13):
+ ptr = (void *)&pt->r12 + addr - ELF_GR_OFFSET(12);
break;
- case PT_PR:
- ptr = pt_reg_addr(pt, pr);
+ case ELF_GR_OFFSET(14):
+ ptr = &pt->r14;
break;
- /* scratch register */
-
- default:
- /* disallow accessing anything else... */
- dprintk("ptrace: rejecting access to register "
- "address 0x%lx\n", addr);
- return -1;
- }
- } else if (addr <= PT_AR_SSD) {
- ptr = pt_reg_addr(pt, ar_csd) + (addr - PT_AR_CSD);
- } else {
- /* access debug registers */
-
- if (addr >= PT_IBR) {
- regnum = (addr - PT_IBR) >> 3;
- ptr = &child->thread.ibr[0];
- } else {
- regnum = (addr - PT_DBR) >> 3;
- ptr = &child->thread.dbr[0];
- }
-
- if (regnum >= 8) {
- dprintk("ptrace: rejecting access to register "
- "address 0x%lx\n", addr);
- return -1;
- }
-#ifdef CONFIG_PERFMON
- /*
- * Check if debug registers are used by perfmon. This
- * test must be done once we know that we can do the
- * operation, i.e. the arguments are all valid, but
- * before we start modifying the state.
- *
- * Perfmon needs to keep a count of how many processes
- * are trying to modify the debug registers for system
- * wide monitoring sessions.
- *
- * We also include read access here, because they may
- * cause the PMU-installed debug register state
- * (dbr[], ibr[]) to be reset. The two arrays are also
- * used by perfmon, but we do not use
- * IA64_THREAD_DBG_VALID. The registers are restored
- * by the PMU context switch code.
- */
- if (pfm_use_debug_registers(child)) return -1;
-#endif
-
- if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) {
- child->thread.flags |= IA64_THREAD_DBG_VALID;
- memset(child->thread.dbr, 0,
- sizeof(child->thread.dbr));
- memset(child->thread.ibr, 0,
- sizeof(child->thread.ibr));
- }
-
- ptr += regnum;
-
- if ((regnum & 1) && write_access) {
- /* don't let the user set kernel-level breakpoints: */
- *ptr = *data & ~(7UL << 56);
- return 0;
- }
+ case ELF_GR_OFFSET(15):
+ ptr = &pt->r15;
}
if (write_access)
*ptr = *data;
@@ -1104,567 +840,809 @@ access_uarea (struct task_struct *child,
return 0;
}
-static long
-ptrace_getregs (struct task_struct *child, struct pt_all_user_regs __user *ppr)
+static int
+access_elf_breg(struct task_struct *target, struct unw_frame_info *info,
+ unsigned long addr, unsigned long *data, int write_access)
{
- unsigned long psr, ec, lc, rnat, bsp, cfm, nat_bits, val;
- struct unw_frame_info info;
- struct ia64_fpreg fpval;
- struct switch_stack *sw;
struct pt_regs *pt;
- long ret, retval = 0;
- char nat = 0;
- int i;
-
- if (!access_ok(VERIFY_WRITE, ppr, sizeof(struct pt_all_user_regs)))
- return -EIO;
+ unsigned long *ptr = NULL;
- pt = task_pt_regs(child);
- sw = (struct switch_stack *) (child->thread.ksp + 16);
- unw_init_from_blocked_task(&info, child);
- if (unw_unwind_to_user(&info) < 0) {
- return -EIO;
- }
-
- if (((unsigned long) ppr & 0x7) != 0) {
- dprintk("ptrace:unaligned register address %p\n", ppr);
- return -EIO;
+ pt = task_pt_regs(target);
+ switch (addr) {
+ case ELF_BR_OFFSET(0):
+ ptr = &pt->b0;
+ break;
+ case ELF_BR_OFFSET(1) ... ELF_BR_OFFSET(5):
+ return unw_access_br(info, (addr - ELF_BR_OFFSET(0))/8,
+ data, write_access);
+ case ELF_BR_OFFSET(6):
+ ptr = &pt->b6;
+ break;
+ case ELF_BR_OFFSET(7):
+ ptr = &pt->b7;
}
+ if (write_access)
+ *ptr = *data;
+ else
+ *data = *ptr;
+ return 0;
+}
- if (access_uarea(child, PT_CR_IPSR, &psr, 0) < 0
- || access_uarea(child, PT_AR_EC, &ec, 0) < 0
- || access_uarea(child, PT_AR_LC, &lc, 0) < 0
- || access_uarea(child, PT_AR_RNAT, &rnat, 0) < 0
- || access_uarea(child, PT_AR_BSP, &bsp, 0) < 0
- || access_uarea(child, PT_CFM, &cfm, 0)
- || access_uarea(child, PT_NAT_BITS, &nat_bits, 0))
- return -EIO;
+static int
+access_elf_areg(struct task_struct *target, struct unw_frame_info *info,
+ unsigned long addr, unsigned long *data, int write_access)
+{
+ struct pt_regs *pt;
+ unsigned long cfm, urbs_end, rnat_addr;
+ unsigned long *ptr = NULL;
- /* control regs */
+ pt = task_pt_regs(target);
+ if (addr >= ELF_AR_RSC_OFFSET && addr <= ELF_AR_SSD_OFFSET) {
+ switch (addr) {
+ case ELF_AR_RSC_OFFSET:
+ /* force PL3 */
+ if (write_access)
+ pt->ar_rsc = *data | (3 << 2);
+ else
+ *data = pt->ar_rsc;
+ return 0;
+ case ELF_AR_BSP_OFFSET:
+ /*
+ * By convention, we use PT_AR_BSP to refer to
+ * the end of the user-level backing store.
+ * Use ia64_rse_skip_regs(PT_AR_BSP, -CFM.sof)
+ * to get the real value of ar.bsp at the time
+ * the kernel was entered.
+ *
+ * Furthermore, when changing the contents of
+ * PT_AR_BSP (or PT_CFM) we MUST copy any
+ * users-level stacked registers that are
+ * stored on the kernel stack back to
+ * user-space because otherwise, we might end
+ * up clobbering kernel stacked registers.
+ * Also, if this happens while the task is
+ * blocked in a system call, which convert the
+ * state such that the non-system-call exit
+ * path is used. This ensures that the proper
+ * state will be picked up when resuming
+ * execution. However, it *also* means that
+ * once we write PT_AR_BSP/PT_CFM, it won't be
+ * possible to modify the syscall arguments of
+ * the pending system call any longer. This
+ * shouldn't be an issue because modifying
+ * PT_AR_BSP/PT_CFM generally implies that
+ * we're either abandoning the pending system
+ * call or that we defer it's re-execution
+ * (e.g., due to GDB doing an inferior
+ * function call).
+ */
+ urbs_end = ia64_get_user_rbs_end(target, pt, &cfm);
+ if (write_access) {
+ if (*data != urbs_end) {
+ if (ia64_sync_user_rbs(target, info->sw,
+ pt->ar_bspstore,
+ urbs_end) < 0)
+ return -1;
+ if (in_syscall(pt))
+ convert_to_non_syscall(target,
+ pt,
+ cfm);
+ /*
+ * Simulate user-level write
+ * of ar.bsp:
+ */
+ pt->loadrs = 0;
+ pt->ar_bspstore = *data;
+ }
+ } else
+ *data = urbs_end;
+ return 0;
+ case ELF_AR_BSPSTORE_OFFSET: // ar_bsp_store
+ ptr = &pt->ar_bspstore;
+ break;
+ case ELF_AR_RNAT_OFFSET: // ar_rnat
+ urbs_end = ia64_get_user_rbs_end(target, pt, NULL);
+ rnat_addr = (long) ia64_rse_rnat_addr((long *)
+ urbs_end);
+ if (write_access)
+ return ia64_poke(target, info->sw, urbs_end,
+ rnat_addr, *data);
+ else
+ return ia64_peek(target, info->sw, urbs_end,
+ rnat_addr, data);
+ case ELF_AR_CCV_OFFSET: // ar_ccv
+ ptr = &pt->ar_ccv;
+ break;
+ case ELF_AR_UNAT_OFFSET: // ar_unat
+ ptr = &pt->ar_unat;
+ break;
+ case ELF_AR_FPSR_OFFSET: // ar_fpsr
+ ptr = &pt->ar_fpsr;
+ break;
+ case ELF_AR_PFS_OFFSET: // ar_pfs
+ ptr = &pt->ar_pfs;
+ break;
+ case ELF_AR_LC_OFFSET: // ar_lc
+ return unw_access_ar(info, UNW_AR_LC, data,
+ write_access);
+ case ELF_AR_EC_OFFSET: // ar_ec
+ return unw_access_ar(info, UNW_AR_EC, data,
+ write_access);
+ case ELF_AR_CSD_OFFSET: // ar_csd
+ ptr = &pt->ar_csd;
+ break;
+ case ELF_AR_SSD_OFFSET: // ar_ssd
+ ptr = &pt->ar_ssd;
+ }
+ } else if (addr >= ELF_CR_IIP_OFFSET && addr <= ELF_CR_IPSR_OFFSET) {
+ switch (addr) {
+ case ELF_CR_IIP_OFFSET:
+ ptr = &pt->cr_iip;
+ break;
+ case ELF_CFM_OFFSET:
+ urbs_end = ia64_get_user_rbs_end(target, pt, &cfm);
+ if (write_access) {
+ if (((cfm ^ *data) & PFM_MASK) != 0) {
+ if (ia64_sync_user_rbs(target, info->sw,
+ pt->ar_bspstore,
+ urbs_end) < 0)
+ return -1;
+ if (in_syscall(pt))
+ convert_to_non_syscall(target,
+ pt,
+ cfm);
+ pt->cr_ifs = ((pt->cr_ifs & ~PFM_MASK)
+ | (*data & PFM_MASK));
+ }
+ } else
+ *data = cfm;
+ return 0;
+ case ELF_CR_IPSR_OFFSET:
+ if (write_access)
+ pt->cr_ipsr = ((*data & IPSR_MASK)
+ | (pt->cr_ipsr & ~IPSR_MASK));
+ else
+ *data = (pt->cr_ipsr & IPSR_MASK);
+ return 0;
+ }
+ } else if (addr == ELF_NAT_OFFSET)
+ return access_nat_bits(target, pt, info,
+ data, write_access);
+ else if (addr == ELF_PR_OFFSET)
+ ptr = &pt->pr;
+ else
+ return -1;
- retval |= __put_user(pt->cr_iip, &ppr->cr_iip);
- retval |= __put_user(psr, &ppr->cr_ipsr);
+ if (write_access)
+ *ptr = *data;
+ else
+ *data = *ptr;
- /* app regs */
+ return 0;
+}
- retval |= __put_user(pt->ar_pfs, &ppr->ar[PT_AUR_PFS]);
- retval |= __put_user(pt->ar_rsc, &ppr->ar[PT_AUR_RSC]);
- retval |= __put_user(pt->ar_bspstore, &ppr->ar[PT_AUR_BSPSTORE]);
- retval |= __put_user(pt->ar_unat, &ppr->ar[PT_AUR_UNAT]);
- retval |= __put_user(pt->ar_ccv, &ppr->ar[PT_AUR_CCV]);
- retval |= __put_user(pt->ar_fpsr, &ppr->ar[PT_AUR_FPSR]);
+static int
+access_elf_reg(struct task_struct *target, struct unw_frame_info *info,
+ unsigned long addr, unsigned long *data, int write_access)
+{
+ if (addr >= ELF_GR_OFFSET(1) && addr <= ELF_GR_OFFSET(15))
+ return access_elf_gpreg(target, info, addr, data, write_access);
+ else if (addr >= ELF_BR_OFFSET(0) && addr <= ELF_BR_OFFSET(7))
+ return access_elf_breg(target, info, addr, data, write_access);
+ else
+ return access_elf_areg(target, info, addr, data, write_access);
+}
- retval |= __put_user(ec, &ppr->ar[PT_AUR_EC]);
- retval |= __put_user(lc, &ppr->ar[PT_AUR_LC]);
- retval |= __put_user(rnat, &ppr->ar[PT_AUR_RNAT]);
- retval |= __put_user(bsp, &ppr->ar[PT_AUR_BSP]);
- retval |= __put_user(cfm, &ppr->cfm);
+void do_gpregs_get(struct unw_frame_info *info, void *arg)
+{
+ struct pt_regs *pt;
+ utrace_getset_t *dst = arg;
+ elf_greg_t tmp[16];
+ unsigned int i, index, min_copy;
- /* gr1-gr3 */
+ if (unw_unwind_to_user(info) < 0)
+ return;
- retval |= __copy_to_user(&ppr->gr[1], &pt->r1, sizeof(long));
- retval |= __copy_to_user(&ppr->gr[2], &pt->r2, sizeof(long) *2);
+ /*
+ * coredump format:
+ * r0-r31
+ * NaT bits (for r0-r31; bit N == 1 iff rN is a NaT)
+ * predicate registers (p0-p63)
+ * b0-b7
+ * ip cfm user-mask
+ * ar.rsc ar.bsp ar.bspstore ar.rnat
+ * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec
+ */
- /* gr4-gr7 */
- for (i = 4; i < 8; i++) {
- if (unw_access_gr(&info, i, &val, &nat, 0) < 0)
- return -EIO;
- retval |= __put_user(val, &ppr->gr[i]);
+ /* Skip r0 */
+ if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) {
+ dst->ret = utrace_regset_copyout_zero(&dst->pos, &dst->count,
+ &dst->u.get.kbuf,
+ &dst->u.get.ubuf,
+ 0, ELF_GR_OFFSET(1));
+ if (dst->ret || dst->count == 0)
+ return;
}
- /* gr8-gr11 */
-
- retval |= __copy_to_user(&ppr->gr[8], &pt->r8, sizeof(long) * 4);
-
- /* gr12-gr15 */
-
- retval |= __copy_to_user(&ppr->gr[12], &pt->r12, sizeof(long) * 2);
- retval |= __copy_to_user(&ppr->gr[14], &pt->r14, sizeof(long));
- retval |= __copy_to_user(&ppr->gr[15], &pt->r15, sizeof(long));
-
- /* gr16-gr31 */
-
- retval |= __copy_to_user(&ppr->gr[16], &pt->r16, sizeof(long) * 16);
-
- /* b0 */
-
- retval |= __put_user(pt->b0, &ppr->br[0]);
-
- /* b1-b5 */
-
- for (i = 1; i < 6; i++) {
- if (unw_access_br(&info, i, &val, 0) < 0)
- return -EIO;
- __put_user(val, &ppr->br[i]);
+ /* gr1 - gr15 */
+ if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) {
+ index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t);
+ min_copy = ELF_GR_OFFSET(16) > (dst->pos + dst->count) ?
+ (dst->pos + dst->count) : ELF_GR_OFFSET(16);
+ for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), index++)
+ if (access_elf_reg(dst->target, info, i,
+ &tmp[index], 0) < 0) {
+ dst->ret = -EIO;
+ return;
+ }
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf, tmp,
+ ELF_GR_OFFSET(1), ELF_GR_OFFSET(16));
+ if (dst->ret || dst->count == 0)
+ return;
}
- /* b6-b7 */
-
- retval |= __put_user(pt->b6, &ppr->br[6]);
- retval |= __put_user(pt->b7, &ppr->br[7]);
-
- /* fr2-fr5 */
-
- for (i = 2; i < 6; i++) {
- if (unw_get_fr(&info, i, &fpval) < 0)
- return -EIO;
- retval |= __copy_to_user(&ppr->fr[i], &fpval, sizeof (fpval));
+ /* r16-r31 */
+ if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) {
+ pt = task_pt_regs(dst->target);
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf, &pt->r16,
+ ELF_GR_OFFSET(16), ELF_NAT_OFFSET);
+ if (dst->ret || dst->count == 0)
+ return;
}
- /* fr6-fr11 */
-
- retval |= __copy_to_user(&ppr->fr[6], &pt->f6,
- sizeof(struct ia64_fpreg) * 6);
-
- /* fp scratch regs(12-15) */
-
- retval |= __copy_to_user(&ppr->fr[12], &sw->f12,
- sizeof(struct ia64_fpreg) * 4);
-
- /* fr16-fr31 */
-
- for (i = 16; i < 32; i++) {
- if (unw_get_fr(&info, i, &fpval) < 0)
- return -EIO;
- retval |= __copy_to_user(&ppr->fr[i], &fpval, sizeof (fpval));
+ /* nat, pr, b0 - b7 */
+ if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) {
+ index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t);
+ min_copy = ELF_CR_IIP_OFFSET > (dst->pos + dst->count) ?
+ (dst->pos + dst->count) : ELF_CR_IIP_OFFSET;
+ for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), index++)
+ if (access_elf_reg(dst->target, info, i,
+ &tmp[index], 0) < 0) {
+ dst->ret = -EIO;
+ return;
+ }
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf, tmp,
+ ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET);
+ if (dst->ret || dst->count == 0)
+ return;
}
- /* fph */
-
- ia64_flush_fph(child);
- retval |= __copy_to_user(&ppr->fr[32], &child->thread.fph,
- sizeof(ppr->fr[32]) * 96);
-
- /* preds */
-
- retval |= __put_user(pt->pr, &ppr->pr);
-
- /* nat bits */
-
- retval |= __put_user(nat_bits, &ppr->nat);
-
- ret = retval ? -EIO : 0;
- return ret;
+ /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat
+ * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd
+ */
+ if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) {
+ index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t);
+ min_copy = ELF_AR_END_OFFSET > (dst->pos + dst->count) ?
+ (dst->pos + dst->count) : ELF_AR_END_OFFSET;
+ for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), index++)
+ if (access_elf_reg(dst->target, info, i,
+ &tmp[index], 0) < 0) {
+ dst->ret = -EIO;
+ return;
+ }
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf, tmp,
+ ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET);
+ }
}
-static long
-ptrace_setregs (struct task_struct *child, struct pt_all_user_regs __user *ppr)
+void do_gpregs_set(struct unw_frame_info *info, void *arg)
{
- unsigned long psr, rsc, ec, lc, rnat, bsp, cfm, nat_bits, val = 0;
- struct unw_frame_info info;
- struct switch_stack *sw;
- struct ia64_fpreg fpval;
struct pt_regs *pt;
- long ret, retval = 0;
- int i;
-
- memset(&fpval, 0, sizeof(fpval));
+ utrace_getset_t *dst = arg;
+ elf_greg_t tmp[16];
+ unsigned int i, index;
- if (!access_ok(VERIFY_READ, ppr, sizeof(struct pt_all_user_regs)))
- return -EIO;
+ if (unw_unwind_to_user(info) < 0)
+ return;
- pt = task_pt_regs(child);
- sw = (struct switch_stack *) (child->thread.ksp + 16);
- unw_init_from_blocked_task(&info, child);
- if (unw_unwind_to_user(&info) < 0) {
- return -EIO;
+ /* Skip r0 */
+ if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) {
+ dst->ret = utrace_regset_copyin_ignore(&dst->pos, &dst->count,
+ &dst->u.set.kbuf,
+ &dst->u.set.ubuf,
+ 0, ELF_GR_OFFSET(1));
+ if (dst->ret || dst->count == 0)
+ return;
}
- if (((unsigned long) ppr & 0x7) != 0) {
- dprintk("ptrace:unaligned register address %p\n", ppr);
- return -EIO;
+ /* gr1-gr15 */
+ if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) {
+ i = dst->pos;
+ index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t);
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf, tmp,
+ ELF_GR_OFFSET(1), ELF_GR_OFFSET(16));
+ if (dst->ret)
+ return;
+ for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++)
+ if (access_elf_reg(dst->target, info, i,
+ &tmp[index], 1) < 0) {
+ dst->ret = -EIO;
+ return;
+ }
+ if (dst->count == 0)
+ return;
}
- /* control regs */
-
- retval |= __get_user(pt->cr_iip, &ppr->cr_iip);
- retval |= __get_user(psr, &ppr->cr_ipsr);
-
- /* app regs */
-
- retval |= __get_user(pt->ar_pfs, &ppr->ar[PT_AUR_PFS]);
- retval |= __get_user(rsc, &ppr->ar[PT_AUR_RSC]);
- retval |= __get_user(pt->ar_bspstore, &ppr->ar[PT_AUR_BSPSTORE]);
- retval |= __get_user(pt->ar_unat, &ppr->ar[PT_AUR_UNAT]);
- retval |= __get_user(pt->ar_ccv, &ppr->ar[PT_AUR_CCV]);
- retval |= __get_user(pt->ar_fpsr, &ppr->ar[PT_AUR_FPSR]);
-
- retval |= __get_user(ec, &ppr->ar[PT_AUR_EC]);
- retval |= __get_user(lc, &ppr->ar[PT_AUR_LC]);
- retval |= __get_user(rnat, &ppr->ar[PT_AUR_RNAT]);
- retval |= __get_user(bsp, &ppr->ar[PT_AUR_BSP]);
- retval |= __get_user(cfm, &ppr->cfm);
-
- /* gr1-gr3 */
-
- retval |= __copy_from_user(&pt->r1, &ppr->gr[1], sizeof(long));
- retval |= __copy_from_user(&pt->r2, &ppr->gr[2], sizeof(long) * 2);
-
- /* gr4-gr7 */
-
- for (i = 4; i < 8; i++) {
- retval |= __get_user(val, &ppr->gr[i]);
- /* NaT bit will be set via PT_NAT_BITS: */
- if (unw_set_gr(&info, i, val, 0) < 0)
- return -EIO;
+ /* gr16-gr31 */
+ if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) {
+ pt = task_pt_regs(dst->target);
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf, &pt->r16,
+ ELF_GR_OFFSET(16), ELF_NAT_OFFSET);
+ if (dst->ret || dst->count == 0)
+ return;
}
- /* gr8-gr11 */
-
- retval |= __copy_from_user(&pt->r8, &ppr->gr[8], sizeof(long) * 4);
-
- /* gr12-gr15 */
-
- retval |= __copy_from_user(&pt->r12, &ppr->gr[12], sizeof(long) * 2);
- retval |= __copy_from_user(&pt->r14, &ppr->gr[14], sizeof(long));
- retval |= __copy_from_user(&pt->r15, &ppr->gr[15], sizeof(long));
-
- /* gr16-gr31 */
+ /* nat, pr, b0 - b7 */
+ if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) {
+ i = dst->pos;
+ index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t);
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf, tmp,
+ ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET);
+ if (dst->ret)
+ return;
+ for (; i < dst->pos; i += sizeof(elf_greg_t), index++)
+ if (access_elf_reg(dst->target, info, i,
+ &tmp[index], 1) < 0) {
+ dst->ret = -EIO;
+ return;
+ }
+ if (dst->count == 0)
+ return;
+ }
- retval |= __copy_from_user(&pt->r16, &ppr->gr[16], sizeof(long) * 16);
+ /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat
+ * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd
+ */
+ if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) {
+ i = dst->pos;
+ index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t);
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf, tmp,
+ ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET);
+ if (dst->ret)
+ return;
+ for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++)
+ if (access_elf_reg(dst->target, info, i,
+ &tmp[index], 1) < 0) {
+ dst->ret = -EIO;
+ return;
+ }
+ }
+}
- /* b0 */
+#define ELF_FP_OFFSET(i) (i * sizeof(elf_fpreg_t))
- retval |= __get_user(pt->b0, &ppr->br[0]);
+void do_fpregs_get(struct unw_frame_info *info, void *arg)
+{
+ utrace_getset_t *dst = arg;
+ struct task_struct *task = dst->target;
+ elf_fpreg_t tmp[30];
+ int index, min_copy, i;
- /* b1-b5 */
+ if (unw_unwind_to_user(info) < 0)
+ return;
- for (i = 1; i < 6; i++) {
- retval |= __get_user(val, &ppr->br[i]);
- unw_set_br(&info, i, val);
+ /* Skip pos 0 and 1 */
+ if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) {
+ dst->ret = utrace_regset_copyout_zero(&dst->pos, &dst->count,
+ &dst->u.get.kbuf,
+ &dst->u.get.ubuf,
+ 0, ELF_FP_OFFSET(2));
+ if (dst->count == 0 || dst->ret)
+ return;
}
- /* b6-b7 */
-
- retval |= __get_user(pt->b6, &ppr->br[6]);
- retval |= __get_user(pt->b7, &ppr->br[7]);
-
- /* fr2-fr5 */
-
- for (i = 2; i < 6; i++) {
- retval |= __copy_from_user(&fpval, &ppr->fr[i], sizeof(fpval));
- if (unw_set_fr(&info, i, fpval) < 0)
- return -EIO;
+ /* fr2-fr31 */
+ if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) {
+ index = (dst->pos - ELF_FP_OFFSET(2)) / sizeof(elf_fpreg_t);
+ min_copy = min(((unsigned int)ELF_FP_OFFSET(32)),
+ dst->pos + dst->count);
+ for (i = dst->pos; i < min_copy; i += sizeof(elf_fpreg_t), index++)
+ if (unw_get_fr(info, i / sizeof(elf_fpreg_t),
+ &tmp[index])) {
+ dst->ret = -EIO;
+ return;
+ }
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf, tmp,
+ ELF_FP_OFFSET(2), ELF_FP_OFFSET(32));
+ if (dst->count == 0 || dst->ret)
+ return;
}
- /* fr6-fr11 */
+ /* fph */
+ if (dst->count > 0) {
+ ia64_flush_fph(dst->target);
+ if (task->thread.flags & IA64_THREAD_FPH_VALID)
+ dst->ret = utrace_regset_copyout(
+ &dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf,
+ &dst->target->thread.fph,
+ ELF_FP_OFFSET(32), -1);
+ else
+ /* Zero fill instead. */
+ dst->ret = utrace_regset_copyout_zero(
+ &dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf,
+ ELF_FP_OFFSET(32), -1);
+ }
+}
- retval |= __copy_from_user(&pt->f6, &ppr->fr[6],
- sizeof(ppr->fr[6]) * 6);
+void do_fpregs_set(struct unw_frame_info *info, void *arg)
+{
+ utrace_getset_t *dst = arg;
+ elf_fpreg_t fpreg, tmp[30];
+ int index, start, end;
- /* fp scratch regs(12-15) */
+ if (unw_unwind_to_user(info) < 0)
+ return;
- retval |= __copy_from_user(&sw->f12, &ppr->fr[12],
- sizeof(ppr->fr[12]) * 4);
+ /* Skip pos 0 and 1 */
+ if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) {
+ dst->ret = utrace_regset_copyin_ignore(&dst->pos, &dst->count,
+ &dst->u.set.kbuf,
+ &dst->u.set.ubuf,
+ 0, ELF_FP_OFFSET(2));
+ if (dst->count == 0 || dst->ret)
+ return;
+ }
- /* fr16-fr31 */
+ /* fr2-fr31 */
+ if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) {
+ start = dst->pos;
+ end = min(((unsigned int)ELF_FP_OFFSET(32)),
+ dst->pos + dst->count);
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf, tmp,
+ ELF_FP_OFFSET(2), ELF_FP_OFFSET(32));
+ if (dst->ret)
+ return;
- for (i = 16; i < 32; i++) {
- retval |= __copy_from_user(&fpval, &ppr->fr[i],
- sizeof(fpval));
- if (unw_set_fr(&info, i, fpval) < 0)
- return -EIO;
+ if (start & 0xF) { // only write high part
+ if (unw_get_fr(info, start / sizeof(elf_fpreg_t),
+ &fpreg)) {
+ dst->ret = -EIO;
+ return;
+ }
+ tmp[start / sizeof(elf_fpreg_t) - 2].u.bits[0]
+ = fpreg.u.bits[0];
+ start &= ~0xFUL;
+ }
+ if (end & 0xF) { // only write low part
+ if (unw_get_fr(info, end / sizeof(elf_fpreg_t), &fpreg)) {
+ dst->ret = -EIO;
+ return;
+ }
+ tmp[end / sizeof(elf_fpreg_t) -2].u.bits[1]
+ = fpreg.u.bits[1];
+ end = (end + 0xF) & ~0xFUL;
+ }
+
+ for ( ; start < end ; start += sizeof(elf_fpreg_t)) {
+ index = start / sizeof(elf_fpreg_t);
+ if (unw_set_fr(info, index, tmp[index - 2])){
+ dst->ret = -EIO;
+ return;
+ }
+ }
+ if (dst->ret || dst->count == 0)
+ return;
}
/* fph */
+ if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(128)) {
+ ia64_sync_fph(dst->target);
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf,
+ &dst->u.set.ubuf,
+ &dst->target->thread.fph,
+ ELF_FP_OFFSET(32), -1);
+ }
+}
- ia64_sync_fph(child);
- retval |= __copy_from_user(&child->thread.fph, &ppr->fr[32],
- sizeof(ppr->fr[32]) * 96);
-
- /* preds */
-
- retval |= __get_user(pt->pr, &ppr->pr);
+static int
+do_regset_call(void (*call)(struct unw_frame_info *, void *),
+ struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ utrace_getset_t info = { .target = target, .regset = regset,
+ .pos = pos, .count = count,
+ .u.set = { .kbuf = kbuf, .ubuf = ubuf },
+ .ret = 0 };
- /* nat bits */
+ if (target == current)
+ unw_init_running(call, &info);
+ else {
+ struct unw_frame_info ufi;
+ memset(&ufi, 0, sizeof(ufi));
+ unw_init_from_blocked_task(&ufi, target);
+ (*call)(&ufi, &info);
+ }
- retval |= __get_user(nat_bits, &ppr->nat);
+ return info.ret;
+}
- retval |= access_uarea(child, PT_CR_IPSR, &psr, 1);
- retval |= access_uarea(child, PT_AR_RSC, &rsc, 1);
- retval |= access_uarea(child, PT_AR_EC, &ec, 1);
- retval |= access_uarea(child, PT_AR_LC, &lc, 1);
- retval |= access_uarea(child, PT_AR_RNAT, &rnat, 1);
- retval |= access_uarea(child, PT_AR_BSP, &bsp, 1);
- retval |= access_uarea(child, PT_CFM, &cfm, 1);
- retval |= access_uarea(child, PT_NAT_BITS, &nat_bits, 1);
+static int
+gpregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ return do_regset_call(do_gpregs_get, target, regset, pos, count, kbuf, ubuf);
+}
- ret = retval ? -EIO : 0;
- return ret;
+static int gpregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ return do_regset_call(do_gpregs_set, target, regset, pos, count, kbuf, ubuf);
}
/*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure the single step bit is not set.
+ * This is called to write back the register backing store.
+ * ptrace does this before it stops, so that a tracer reading the user
+ * memory after the thread stops will get the current register data.
*/
-void
-ptrace_disable (struct task_struct *child)
-{
- struct ia64_psr *child_psr = ia64_psr(task_pt_regs(child));
-
- /* make sure the single step/taken-branch trap bits are not set: */
- child_psr->ss = 0;
- child_psr->tb = 0;
+static int
+gpregs_writeback(struct task_struct *target,
+ const struct utrace_regset *regset,
+ int now)
+{
+ unsigned long urbs_end, cfm;
+ struct pt_regs *pt = task_pt_regs(target);
+ struct switch_stack *sw = (void *) (target->thread.ksp + 16);
+ urbs_end = ia64_get_user_rbs_end(target, pt, &cfm);
+ return ia64_sync_user_rbs(target, sw, pt->ar_bspstore, urbs_end);
}
-asmlinkage long
-sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data)
+static int
+fpregs_active(struct task_struct *target, const struct utrace_regset *regset)
{
- struct pt_regs *pt;
- unsigned long urbs_end, peek_or_poke;
- struct task_struct *child;
- struct switch_stack *sw;
- long ret;
-
- lock_kernel();
- ret = -EPERM;
- if (request == PTRACE_TRACEME) {
- ret = ptrace_traceme();
- goto out;
- }
-
- peek_or_poke = (request == PTRACE_PEEKTEXT
- || request == PTRACE_PEEKDATA
- || request == PTRACE_POKETEXT
- || request == PTRACE_POKEDATA);
- ret = -ESRCH;
- read_lock(&tasklist_lock);
- {
- child = find_task_by_pid(pid);
- if (child) {
- if (peek_or_poke)
- child = find_thread_for_addr(child, addr);
- get_task_struct(child);
- }
- }
- read_unlock(&tasklist_lock);
- if (!child)
- goto out;
- ret = -EPERM;
- if (pid == 1) /* no messing around with init! */
- goto out_tsk;
-
- if (request == PTRACE_ATTACH) {
- ret = ptrace_attach(child);
- goto out_tsk;
- }
-
- ret = ptrace_check_attach(child, request == PTRACE_KILL);
- if (ret < 0)
- goto out_tsk;
-
- pt = task_pt_regs(child);
- sw = (struct switch_stack *) (child->thread.ksp + 16);
-
- switch (request) {
- case PTRACE_PEEKTEXT:
- case PTRACE_PEEKDATA:
- /* read word at location addr */
- urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
- ret = ia64_peek(child, sw, urbs_end, addr, &data);
- if (ret == 0) {
- ret = data;
- /* ensure "ret" is not mistaken as an error code: */
- force_successful_syscall_return();
- }
- goto out_tsk;
-
- case PTRACE_POKETEXT:
- case PTRACE_POKEDATA:
- /* write the word at location addr */
- urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
- ret = ia64_poke(child, sw, urbs_end, addr, data);
- goto out_tsk;
-
- case PTRACE_PEEKUSR:
- /* read the word at addr in the USER area */
- if (access_uarea(child, addr, &data, 0) < 0) {
- ret = -EIO;
- goto out_tsk;
- }
- ret = data;
- /* ensure "ret" is not mistaken as an error code */
- force_successful_syscall_return();
- goto out_tsk;
-
- case PTRACE_POKEUSR:
- /* write the word at addr in the USER area */
- if (access_uarea(child, addr, &data, 1) < 0) {
- ret = -EIO;
- goto out_tsk;
- }
- ret = 0;
- goto out_tsk;
-
- case PTRACE_OLD_GETSIGINFO:
- /* for backwards-compatibility */
- ret = ptrace_request(child, PTRACE_GETSIGINFO, addr, data);
- goto out_tsk;
-
- case PTRACE_OLD_SETSIGINFO:
- /* for backwards-compatibility */
- ret = ptrace_request(child, PTRACE_SETSIGINFO, addr, data);
- goto out_tsk;
-
- case PTRACE_SYSCALL:
- /* continue and stop at next (return from) syscall */
- case PTRACE_CONT:
- /* restart after signal. */
- ret = -EIO;
- if (!valid_signal(data))
- goto out_tsk;
- if (request == PTRACE_SYSCALL)
- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- else
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- child->exit_code = data;
-
- /*
- * Make sure the single step/taken-branch trap bits
- * are not set:
- */
- ia64_psr(pt)->ss = 0;
- ia64_psr(pt)->tb = 0;
-
- wake_up_process(child);
- ret = 0;
- goto out_tsk;
-
- case PTRACE_KILL:
- /*
- * Make the child exit. Best I can do is send it a
- * sigkill. Perhaps it should be put in the status
- * that it wants to exit.
- */
- if (child->exit_state == EXIT_ZOMBIE)
- /* already dead */
- goto out_tsk;
- child->exit_code = SIGKILL;
-
- ptrace_disable(child);
- wake_up_process(child);
- ret = 0;
- goto out_tsk;
-
- case PTRACE_SINGLESTEP:
- /* let child execute for one instruction */
- case PTRACE_SINGLEBLOCK:
- ret = -EIO;
- if (!valid_signal(data))
- goto out_tsk;
-
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- if (request == PTRACE_SINGLESTEP) {
- ia64_psr(pt)->ss = 1;
- } else {
- ia64_psr(pt)->tb = 1;
- }
- child->exit_code = data;
+ return (target->thread.flags & IA64_THREAD_FPH_VALID) ? 128 : 32;
+}
- /* give it a chance to run. */
- wake_up_process(child);
- ret = 0;
- goto out_tsk;
-
- case PTRACE_DETACH:
- /* detach a process that was attached. */
- ret = ptrace_detach(child, data);
- goto out_tsk;
-
- case PTRACE_GETREGS:
- ret = ptrace_getregs(child,
- (struct pt_all_user_regs __user *) data);
- goto out_tsk;
-
- case PTRACE_SETREGS:
- ret = ptrace_setregs(child,
- (struct pt_all_user_regs __user *) data);
- goto out_tsk;
-
- default:
- ret = ptrace_request(child, request, addr, data);
- goto out_tsk;
- }
- out_tsk:
- put_task_struct(child);
- out:
- unlock_kernel();
- return ret;
+static int fpregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ return do_regset_call(do_fpregs_get, target, regset, pos, count, kbuf, ubuf);
}
+static int fpregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ return do_regset_call(do_fpregs_set, target, regset, pos, count, kbuf, ubuf);
+}
-void
-syscall_trace (void)
+static int dbregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
{
- if (!test_thread_flag(TIF_SYSCALL_TRACE))
- return;
- if (!(current->ptrace & PT_PTRACED))
- return;
- /*
- * The 0x80 provides a way for the tracing parent to
- * distinguish between a syscall stop and SIGTRAP delivery.
- */
- ptrace_notify(SIGTRAP
- | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
+ int ret;
+#ifdef CONFIG_PERFMON
/*
- * This isn't the same as continuing with a signal, but it
- * will do for normal use. strace only continues with a
- * signal if the stopping signal is not SIGTRAP. -brl
+ * Check if debug registers are used by perfmon. This
+ * test must be done once we know that we can do the
+ * operation, i.e. the arguments are all valid, but
+ * before we start modifying the state.
+ *
+ * Perfmon needs to keep a count of how many processes
+ * are trying to modify the debug registers for system
+ * wide monitoring sessions.
+ *
+ * We also include read access here, because they may
+ * cause the PMU-installed debug register state
+ * (dbr[], ibr[]) to be reset. The two arrays are also
+ * used by perfmon, but we do not use
+ * IA64_THREAD_DBG_VALID. The registers are restored
+ * by the PMU context switch code.
*/
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
+ if (pfm_use_debug_registers(target))
+ return -EIO;
+#endif
+
+ if (!(target->thread.flags & IA64_THREAD_DBG_VALID))
+ ret = utrace_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
+ 0, -1);
+ else {
+ preempt_disable();
+ if (target == current)
+ ia64_load_debug_regs(&target->thread.dbr[0]);
+ preempt_enable_no_resched();
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.dbr, 0, -1);
}
-}
-/* "asmlinkage" so the input arguments are preserved... */
+ return ret;
+}
-asmlinkage void
-syscall_trace_enter (long arg0, long arg1, long arg2, long arg3,
- long arg4, long arg5, long arg6, long arg7,
- struct pt_regs regs)
+static int dbregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
{
- if (test_thread_flag(TIF_SYSCALL_TRACE)
- && (current->ptrace & PT_PTRACED))
- syscall_trace();
-
- if (unlikely(current->audit_context)) {
- long syscall;
- int arch;
+ int i, ret;
- if (IS_IA32_PROCESS(®s)) {
- syscall = regs.r1;
- arch = AUDIT_ARCH_I386;
- } else {
- syscall = regs.r15;
- arch = AUDIT_ARCH_IA64;
- }
+#ifdef CONFIG_PERFMON
+ if (pfm_use_debug_registers(target))
+ return -EIO;
+#endif
- audit_syscall_entry(arch, syscall, arg0, arg1, arg2, arg3);
+ ret = 0;
+ if (!(target->thread.flags & IA64_THREAD_DBG_VALID)){
+ target->thread.flags |= IA64_THREAD_DBG_VALID;
+ memset(target->thread.dbr, 0, 2 * sizeof(target->thread.dbr));
+ } else if (target == current){
+ preempt_disable();
+ ia64_save_debug_regs(&target->thread.dbr[0]);
+ preempt_enable_no_resched();
}
-}
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.dbr, 0, -1);
-/* "asmlinkage" so the input arguments are preserved... */
+ for (i = 1; i < IA64_NUM_DBG_REGS; i += 2) {
+ target->thread.dbr[i] &= ~(7UL << 56);
+ target->thread.ibr[i] &= ~(7UL << 56);
+ }
-asmlinkage void
-syscall_trace_leave (long arg0, long arg1, long arg2, long arg3,
- long arg4, long arg5, long arg6, long arg7,
- struct pt_regs regs)
-{
- if (unlikely(current->audit_context)) {
- int success = AUDITSC_RESULT(regs.r10);
- long result = regs.r8;
+ if (ret)
+ return ret;
- if (success != AUDITSC_SUCCESS)
- result = -result;
- audit_syscall_exit(success, result);
+ if (target == current){
+ preempt_disable();
+ ia64_load_debug_regs(&target->thread.dbr[0]);
+ preempt_enable_no_resched();
}
+ return 0;
+}
- if (test_thread_flag(TIF_SYSCALL_TRACE)
- && (current->ptrace & PT_PTRACED))
- syscall_trace();
+static const struct utrace_regset native_regsets[] = {
+ {
+ .n = ELF_NGREG,
+ .size = sizeof(elf_greg_t), .align = sizeof(elf_greg_t),
+ .get = gpregs_get, .set = gpregs_set,
+ .writeback = gpregs_writeback
+ },
+ {
+ .n = ELF_NFPREG,
+ .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t),
+ .get = fpregs_get, .set = fpregs_set, .active = fpregs_active
+ },
+ {
+ .n = 2 * IA64_NUM_DBG_REGS, .size = sizeof(long),
+ .align = sizeof(long),
+ .get = dbregs_get, .set = dbregs_set
+ }
+};
+
+const struct utrace_regset_view utrace_ia64_native = {
+ .name = "ia64",
+ .e_machine = EM_IA_64,
+ .regsets = native_regsets,
+ .n = sizeof native_regsets / sizeof native_regsets[0],
+};
+EXPORT_SYMBOL_GPL(utrace_ia64_native);
+
+#endif /* CONFIG_UTRACE */
+
+
+#ifdef CONFIG_PTRACE
+
+#define WORD(member, num) \
+ offsetof(struct pt_all_user_regs, member), \
+ offsetof(struct pt_all_user_regs, member) + num * sizeof(long)
+static const struct ptrace_layout_segment pt_all_user_regs_layout[] = {
+ {WORD(nat, 1), 0, ELF_NAT_OFFSET},
+ {WORD(cr_iip, 1), 0, ELF_CR_IIP_OFFSET},
+ {WORD(cfm, 1), 0, ELF_CFM_OFFSET},
+ {WORD(cr_ipsr, 1), 0, ELF_CR_IPSR_OFFSET},
+ {WORD(pr, 1), 0, ELF_PR_OFFSET},
+ {WORD(gr[0], 32), 0, ELF_GR_OFFSET(0)},
+ {WORD(br[0], 8), 0, ELF_BR_OFFSET(0)},
+ {WORD(ar[PT_AUR_RSC], 4), 0, ELF_AR_RSC_OFFSET},
+ {WORD(ar[PT_AUR_CCV], 1), 0, ELF_AR_CCV_OFFSET},
+ {WORD(ar[PT_AUR_UNAT], 1), 0, ELF_AR_UNAT_OFFSET},
+ {WORD(ar[PT_AUR_FPSR], 1), 0, ELF_AR_FPSR_OFFSET},
+ {WORD(ar[PT_AUR_PFS], 3), 0, ELF_AR_PFS_OFFSET},
+ {offsetof(struct pt_all_user_regs, fr[0]),
+ offsetof(struct pt_all_user_regs, fr[128]),
+ 1, 0},
+ {0, 0, -1, 0}
+};
+#undef WORD
+
+#define NEXT(addr, sum) (addr + sum * sizeof(long))
+static const struct ptrace_layout_segment pt_uarea_layout[] = {
+ {PT_F32, PT_NAT_BITS, 1, ELF_FP_OFFSET(32)},
+ {PT_NAT_BITS, NEXT(PT_NAT_BITS, 1), 0, ELF_NAT_OFFSET},
+ {PT_F2, PT_F10, 1, ELF_FP_OFFSET(2)},
+ {PT_F10, PT_R4, 1, ELF_FP_OFFSET(10)},
+ {PT_R4, PT_B1, 0, ELF_GR_OFFSET(4)},
+ {PT_B1, PT_AR_EC, 0, ELF_BR_OFFSET(1)},
+ {PT_AR_EC, PT_AR_LC, 0, ELF_AR_EC_OFFSET},
+ {PT_AR_LC, NEXT(PT_AR_LC, 1), 0, ELF_AR_LC_OFFSET},
+ {PT_CR_IPSR, PT_CR_IIP, 0, ELF_CR_IPSR_OFFSET},
+ {PT_CR_IIP, PT_AR_UNAT, 0, ELF_CR_IIP_OFFSET},
+ {PT_AR_UNAT, PT_AR_PFS, 0, ELF_AR_UNAT_OFFSET},
+ {PT_AR_PFS, PT_AR_RSC, 0, ELF_AR_PFS_OFFSET},
+ {PT_AR_RSC, PT_AR_RNAT, 0, ELF_AR_RSC_OFFSET},
+ {PT_AR_RNAT, PT_AR_BSPSTORE, 0, ELF_AR_RNAT_OFFSET},
+ {PT_AR_BSPSTORE,PT_PR, 0, ELF_AR_BSPSTORE_OFFSET},
+ {PT_PR, PT_B6, 0, ELF_PR_OFFSET},
+ {PT_B6, PT_AR_BSP, 0, ELF_BR_OFFSET(6)},
+ {PT_AR_BSP, PT_R1, 0, ELF_AR_BSP_OFFSET},
+ {PT_R1, PT_R12, 0, ELF_GR_OFFSET(1)},
+ {PT_R12, PT_R8, 0, ELF_GR_OFFSET(12)},
+ {PT_R8, PT_R16, 0, ELF_GR_OFFSET(8)},
+ {PT_R16, PT_AR_CCV, 0, ELF_GR_OFFSET(16)},
+ {PT_AR_CCV, PT_AR_FPSR, 0, ELF_AR_CCV_OFFSET},
+ {PT_AR_FPSR, PT_B0, 0, ELF_AR_FPSR_OFFSET},
+ {PT_B0, PT_B7, 0, ELF_BR_OFFSET(0)},
+ {PT_B7, PT_F6, 0, ELF_BR_OFFSET(7)},
+ {PT_F6, PT_AR_CSD, 1, ELF_FP_OFFSET(6)},
+ {PT_AR_CSD, NEXT(PT_AR_CSD, 2), 0, ELF_AR_CSD_OFFSET},
+ {PT_DBR, NEXT(PT_DBR, 8), 2, 0},
+ {PT_IBR, NEXT(PT_IBR, 8), 2, 8 * sizeof(long)},
+ {0, 0, -1, 0}
+};
+#undef NEXT
+
+fastcall int arch_ptrace(long *request, struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ unsigned long addr, unsigned long data, long *val)
+{
+ int ret = -ENOSYS;
+ switch (*request) {
+ case PTRACE_OLD_GETSIGINFO:
+ *request = PTRACE_GETSIGINFO;
+ break;
+ case PTRACE_OLD_SETSIGINFO:
+ *request = PTRACE_SETSIGINFO;
+ break;
+
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ case PTRACE_PEEKDATA:
+ ret = access_process_vm(child, addr, val, sizeof(*val), 0);
+ ret = ret == sizeof(*val) ? 0 : -EIO;
+ break;
+
+ case PTRACE_PEEKUSR:
+ return ptrace_layout_access(child, engine,
+ utrace_native_view(current),
+ pt_uarea_layout,
+ addr, sizeof(long),
+ NULL, val, 0);
+ case PTRACE_POKEUSR:
+ return ptrace_pokeusr(child, engine,
+ pt_uarea_layout, addr, data);
+
+ case PTRACE_GETREGS:
+ case PTRACE_SETREGS:
+ return ptrace_layout_access(child, engine,
+ utrace_native_view(current),
+ pt_all_user_regs_layout,
+ 0, sizeof(struct pt_all_user_regs),
+ (void __user *) data, NULL,
+ *request == PTRACE_SETREGS);
+ }
+ return ret;
}
+
+#endif /* CONFIG_PTRACE */
--- linux-2.6/arch/ia64/kernel/asm-offsets.c.utrace-ptrace-compat
+++ linux-2.6/arch/ia64/kernel/asm-offsets.c
@@ -44,7 +44,7 @@ void foo(void)
DEFINE(IA64_TASK_GROUP_LEADER_OFFSET, offsetof (struct task_struct, group_leader));
DEFINE(IA64_TASK_PENDING_OFFSET,offsetof (struct task_struct, pending));
DEFINE(IA64_TASK_PID_OFFSET, offsetof (struct task_struct, pid));
- DEFINE(IA64_TASK_REAL_PARENT_OFFSET, offsetof (struct task_struct, real_parent));
+ DEFINE(IA64_TASK_PARENT_OFFSET, offsetof (struct task_struct, parent));
DEFINE(IA64_TASK_SIGHAND_OFFSET,offsetof (struct task_struct, sighand));
DEFINE(IA64_TASK_SIGNAL_OFFSET,offsetof (struct task_struct, signal));
DEFINE(IA64_TASK_TGID_OFFSET, offsetof (struct task_struct, tgid));
--- linux-2.6/arch/ia64/ia32/ia32_entry.S.utrace-ptrace-compat
+++ linux-2.6/arch/ia64/ia32/ia32_entry.S
@@ -236,7 +236,7 @@ ia32_syscall_table:
data8 sys_setuid /* 16-bit version */
data8 sys_getuid /* 16-bit version */
data8 compat_sys_stime /* 25 */
- data8 sys32_ptrace
+ data8 compat_sys_ptrace
data8 sys32_alarm
data8 sys_ni_syscall
data8 sys32_pause
--- linux-2.6/arch/ia64/ia32/sys_ia32.c.utrace-ptrace-compat
+++ linux-2.6/arch/ia64/ia32/sys_ia32.c
@@ -1419,25 +1419,6 @@ sys32_waitpid (int pid, unsigned int *st
return compat_sys_wait4(pid, stat_addr, options, NULL);
}
-static unsigned int
-ia32_peek (struct task_struct *child, unsigned long addr, unsigned int *val)
-{
- size_t copied;
- unsigned int ret;
-
- copied = access_process_vm(child, addr, val, sizeof(*val), 0);
- return (copied != sizeof(ret)) ? -EIO : 0;
-}
-
-static unsigned int
-ia32_poke (struct task_struct *child, unsigned long addr, unsigned int val)
-{
-
- if (access_process_vm(child, addr, &val, sizeof(val), 1) != sizeof(val))
- return -EIO;
- return 0;
-}
-
/*
* The order in which registers are stored in the ptrace regs structure
*/
@@ -1735,6 +1716,7 @@ restore_ia32_fpxstate (struct task_struc
return 0;
}
+#if 0 /* XXX */
asmlinkage long
sys32_ptrace (int request, pid_t pid, unsigned int addr, unsigned int data)
{
@@ -1842,9 +1824,11 @@ sys32_ptrace (int request, pid_t pid, un
compat_ptr(data));
break;
+#if 0 /* XXX */
case PTRACE_GETEVENTMSG:
ret = put_user(child->ptrace_message, (unsigned int __user *) compat_ptr(data));
break;
+#endif
case PTRACE_SYSCALL: /* continue, stop after next syscall */
case PTRACE_CONT: /* restart after signal. */
@@ -1865,6 +1849,7 @@ sys32_ptrace (int request, pid_t pid, un
unlock_kernel();
return ret;
}
+#endif
typedef struct {
unsigned int ss_sp;
--- linux-2.6/arch/ppc/kernel/asm-offsets.c.utrace-ptrace-compat
+++ linux-2.6/arch/ppc/kernel/asm-offsets.c
@@ -37,7 +37,6 @@ main(void)
DEFINE(THREAD, offsetof(struct task_struct, thread));
DEFINE(THREAD_INFO, offsetof(struct task_struct, thread_info));
DEFINE(MM, offsetof(struct task_struct, mm));
- DEFINE(PTRACE, offsetof(struct task_struct, ptrace));
DEFINE(KSP, offsetof(struct thread_struct, ksp));
DEFINE(PGDIR, offsetof(struct thread_struct, pgdir));
DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall));
@@ -47,7 +46,6 @@ main(void)
DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr));
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
DEFINE(THREAD_DBCR0, offsetof(struct thread_struct, dbcr0));
- DEFINE(PT_PTRACED, PT_PTRACED);
#endif
#ifdef CONFIG_ALTIVEC
DEFINE(THREAD_VR0, offsetof(struct thread_struct, vr[0]));
--- linux-2.6/arch/s390/kernel/signal.c.utrace-ptrace-compat
+++ linux-2.6/arch/s390/kernel/signal.c
@@ -25,6 +25,7 @@
#include <linux/tty.h>
#include <linux/personality.h>
#include <linux/binfmts.h>
+#include <linux/tracehook.h>
#include <asm/ucontext.h>
#include <asm/uaccess.h>
#include <asm/lowcore.h>
@@ -405,6 +406,9 @@ handle_signal(unsigned long sig, struct
spin_unlock_irq(¤t->sighand->siglock);
}
+ if (ret == 0)
+ tracehook_report_handle_signal(sig, ka, oldset, regs);
+
return ret;
}
--- linux-2.6/arch/s390/kernel/compat_signal.c.utrace-ptrace-compat
+++ linux-2.6/arch/s390/kernel/compat_signal.c
@@ -28,6 +28,7 @@
#include <asm/ucontext.h>
#include <asm/uaccess.h>
#include <asm/lowcore.h>
+#include <linux/tracehook.h>
#include "compat_linux.h"
#include "compat_ptrace.h"
@@ -580,6 +581,10 @@ handle_signal32(unsigned long sig, struc
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
}
+
+ if (ret == 0)
+ tracehook_report_handle_signal(sig, ka, oldset, regs);
+
return ret;
}
--- linux-2.6/arch/s390/kernel/process.c.utrace-ptrace-compat
+++ linux-2.6/arch/s390/kernel/process.c
@@ -331,9 +331,6 @@ asmlinkage long sys_execve(struct pt_reg
error = do_execve(filename, (char __user * __user *) regs.gprs[3],
(char __user * __user *) regs.gprs[4], ®s);
if (error == 0) {
- task_lock(current);
- current->ptrace &= ~PT_DTRACE;
- task_unlock(current);
current->thread.fp_regs.fpc = 0;
if (MACHINE_HAS_IEEE)
asm volatile("sfpc %0,%0" : : "d" (0));
--- linux-2.6/arch/s390/kernel/compat_linux.c.utrace-ptrace-compat
+++ linux-2.6/arch/s390/kernel/compat_linux.c
@@ -540,9 +540,6 @@ sys32_execve(struct pt_regs regs)
compat_ptr(regs.gprs[4]), ®s);
if (error == 0)
{
- task_lock(current);
- current->ptrace &= ~PT_DTRACE;
- task_unlock(current);
current->thread.fp_regs.fpc=0;
__asm__ __volatile__
("sr 0,0\n\t"
--- linux-2.6/arch/s390/kernel/traps.c.utrace-ptrace-compat
+++ linux-2.6/arch/s390/kernel/traps.c
@@ -18,7 +18,7 @@
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/smp.h>
@@ -338,7 +338,7 @@ static inline void __user *get_check_add
void do_single_step(struct pt_regs *regs)
{
- if ((current->ptrace & PT_PTRACED) != 0)
+ if (tracehook_consider_fatal_signal(current, SIGTRAP))
force_sig(SIGTRAP, current);
}
@@ -439,7 +439,7 @@ asmlinkage void illegal_op(struct pt_reg
if (regs->psw.mask & PSW_MASK_PSTATE) {
get_user(*((__u16 *) opcode), (__u16 __user *) location);
if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
- if (current->ptrace & PT_PTRACED)
+ if (tracehook_consider_fatal_signal(current, SIGTRAP))
force_sig(SIGTRAP, current);
else
signal = SIGILL;
--- linux-2.6/arch/s390/kernel/ptrace.c.utrace-ptrace-compat
+++ linux-2.6/arch/s390/kernel/ptrace.c
@@ -29,6 +29,8 @@
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
+#include <linux/module.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/audit.h>
@@ -41,6 +43,7 @@
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
+#include <asm/elf.h>
#ifdef CONFIG_COMPAT
#include "compat_ptrace.h"
@@ -84,34 +87,601 @@ FixPerRegisters(struct task_struct *task
per_info->control_regs.bits.storage_alt_space_ctl = 1;
else
per_info->control_regs.bits.storage_alt_space_ctl = 0;
+
+ if (task == current)
+ /*
+ * These registers are loaded in __switch_to on
+ * context switch. We must load them now if
+ * touching the current thread.
+ */
+ __ctl_load(per_info->control_regs.words.cr, 9, 11);
}
void
-set_single_step(struct task_struct *task)
+tracehook_enable_single_step(struct task_struct *task)
{
task->thread.per_info.single_step = 1;
FixPerRegisters(task);
}
void
-clear_single_step(struct task_struct *task)
+tracehook_disable_single_step(struct task_struct *task)
{
task->thread.per_info.single_step = 0;
FixPerRegisters(task);
+ clear_tsk_thread_flag(task, TIF_SINGLE_STEP);
+}
+
+int
+tracehook_single_step_enabled(struct task_struct *task)
+{
+ return task->thread.per_info.single_step;
+}
+
+
+static int
+genregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ struct pt_regs *regs = task_pt_regs(target);
+ unsigned long pswmask;
+ int ret;
+
+ /* Remove per bit from user psw. */
+ pswmask = regs->psw.mask & ~PSW_MASK_PER;
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &pswmask, PT_PSWMASK, PT_PSWADDR);
+
+ /* The rest of the PSW and the GPRs are directly on the stack. */
+ if (ret == 0)
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ ®s->psw.addr, PT_PSWADDR,
+ PT_ACR0);
+
+ /* The ACRs are kept in the thread_struct. */
+ if (ret == 0 && count > 0 && pos < PT_ORIGGPR2) {
+ if (target == current)
+ save_access_regs(target->thread.acrs);
+
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ target->thread.acrs,
+ PT_ACR0, PT_ORIGGPR2);
+ }
+
+ /* Finally, the ORIG_GPR2 value. */
+ if (ret == 0)
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ ®s->orig_gpr2, PT_ORIGGPR2, -1);
+
+ return ret;
+}
+
+static int
+genregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct pt_regs *regs = task_pt_regs(target);
+ int ret = 0;
+
+ /* Check for an invalid PSW mask. */
+ if (count > 0 && pos == PT_PSWMASK) {
+ unsigned long pswmask = regs->psw.mask;
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &pswmask, PT_PSWMASK, PT_PSWADDR);
+ if (pswmask != PSW_MASK_MERGE(PSW_USER_BITS, pswmask)
+#ifdef CONFIG_COMPAT
+ && pswmask != PSW_MASK_MERGE(PSW_USER32_BITS, pswmask)
+#endif
+ )
+ /* Invalid psw mask. */
+ return -EINVAL;
+ regs->psw.mask = pswmask;
+ FixPerRegisters(target);
+ }
+
+ /* The rest of the PSW and the GPRs are directly on the stack. */
+ if (ret == 0) {
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ ®s->psw.addr, PT_PSWADDR,
+ PT_ACR0);
+#ifndef CONFIG_64BIT
+ /* I'd like to reject addresses without the
+ high order bit but older gdb's rely on it */
+ regs->psw.addr |= PSW_ADDR_AMODE;
+#endif
+ }
+
+ /* The ACRs are kept in the thread_struct. */
+ if (ret == 0 && count > 0 && pos < PT_ORIGGPR2) {
+ if (target == current
+ && (pos != PT_ACR0 || count < sizeof(target->thread.acrs)))
+ save_access_regs(target->thread.acrs);
+
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ target->thread.acrs,
+ PT_ACR0, PT_ORIGGPR2);
+ if (ret == 0 && target == current)
+ restore_access_regs(target->thread.acrs);
+ }
+
+ /* Finally, the ORIG_GPR2 value. */
+ if (ret == 0)
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ ®s->orig_gpr2, PT_ORIGGPR2, -1);
+
+ return ret;
+}
+
+static int
+fpregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (target == current)
+ save_fp_regs(&target->thread.fp_regs);
+
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fp_regs, 0, -1);
}
+static int
+fpregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret = 0;
+
+ if (target == current && (pos != 0 || count != sizeof(s390_fp_regs)))
+ save_fp_regs(&target->thread.fp_regs);
+
+ /* If setting FPC, must validate it first. */
+ if (count > 0 && pos == 0) {
+ unsigned long fpc;
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &fpc, 0, sizeof(fpc));
+ if (ret)
+ return ret;
+
+ if ((fpc & ~((unsigned long) FPC_VALID_MASK
+ << (BITS_PER_LONG - 32))) != 0)
+ return -EINVAL;
+
+ memcpy(&target->thread.fp_regs, &fpc, sizeof(fpc));
+ }
+
+ if (ret == 0)
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fp_regs, 0, -1);
+
+ if (ret == 0 && target == current)
+ restore_fp_regs(&target->thread.fp_regs);
+
+ return ret;
+}
+
+static int
+per_info_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.per_info, 0, -1);
+}
+
+static int
+per_info_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.per_info, 0, -1);
+
+ FixPerRegisters(target);
+
+ return ret;
+}
+
+
/*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure single step bits etc are not set.
+ * These are our native regset flavors.
*/
-void
-ptrace_disable(struct task_struct *child)
+static const struct utrace_regset native_regsets[] = {
+ {
+ .size = sizeof(long), .align = sizeof(long),
+ .n = sizeof(s390_regs) / sizeof(long),
+ .get = genregs_get, .set = genregs_set
+ },
+ {
+ .size = sizeof(long), .align = sizeof(long),
+ .n = sizeof(s390_fp_regs) / sizeof(long),
+ .get = fpregs_get, .set = fpregs_set
+ },
+ {
+ .size = sizeof(long), .align = sizeof(long),
+ .n = sizeof(per_struct) / sizeof(long),
+ .get = per_info_get, .set = per_info_set
+ },
+};
+
+const struct utrace_regset_view utrace_s390_native_view = {
+ .name = UTS_MACHINE, .e_machine = ELF_ARCH,
+ .regsets = native_regsets,
+ .n = sizeof native_regsets / sizeof native_regsets[0],
+};
+EXPORT_SYMBOL_GPL(utrace_s390_native_view);
+
+
+#ifdef CONFIG_COMPAT
+static int
+s390_genregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
{
- /* make sure the single step bit is not set. */
- clear_single_step(child);
+ struct pt_regs *regs = task_pt_regs(target);
+ int ret = 0;
+
+ /* Fake a 31 bit psw mask. */
+ if (count > 0 && pos == PT_PSWMASK / 2) {
+ u32 pswmask = PSW32_MASK_MERGE(PSW32_USER_BITS,
+ (u32) (regs->psw.mask >> 32));
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &pswmask, PT_PSWMASK / 2,
+ PT_PSWADDR / 2);
+ }
+
+ /* Fake a 31 bit psw address. */
+ if (ret == 0 && count > 0 && pos == PT_PSWADDR / 2) {
+ u32 pswaddr = (u32) regs->psw.addr | PSW32_ADDR_AMODE31;
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &pswaddr, PT_PSWADDR / 2,
+ PT_GPR0 / 2);
+ }
+
+ /* The GPRs are directly on the stack. Just truncate them. */
+ while (ret == 0 && count > 0 && pos < PT_ACR0 / 2) {
+ u32 value = regs->gprs[(pos - PT_GPR0 / 2) / sizeof(u32)];
+ if (kbuf) {
+ *(u32 *) kbuf = value;
+ kbuf += sizeof(u32);
+ }
+ else if (put_user(value, (u32 __user *) ubuf))
+ ret = -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+ }
+
+ /* The ACRs are kept in the thread_struct. */
+ if (ret == 0 && count > 0 && pos < PT_ACR0 / 2 + NUM_ACRS * ACR_SIZE) {
+ if (target == current)
+ save_access_regs(target->thread.acrs);
+
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ target->thread.acrs,
+ PT_ACR0 / 2,
+ PT_ACR0 / 2 + NUM_ACRS * ACR_SIZE);
+ }
+
+ /* Finally, the ORIG_GPR2 value. */
+ if (count > 0) {
+ if (kbuf)
+ *(u32 *) kbuf = regs->orig_gpr2;
+ else if (put_user((u32) regs->orig_gpr2,
+ (u32 __user *) ubuf))
+ return -EFAULT;
+ }
+
+ return 0;
}
+static int
+s390_genregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct pt_regs *regs = task_pt_regs(target);
+ int ret = 0;
+
+ /* Check for an invalid PSW mask. */
+ if (count > 0 && pos == PT_PSWMASK / 2) {
+ u32 pswmask;
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &pswmask, PT_PSWMASK / 2,
+ PT_PSWADDR / 2);
+ if (ret)
+ return ret;
+
+ if (pswmask != PSW_MASK_MERGE(PSW_USER32_BITS, pswmask))
+ /* Invalid psw mask. */
+ return -EINVAL;
+
+ /* Build a 64 bit psw mask from 31 bit mask. */
+ regs->psw.mask = PSW_MASK_MERGE(PSW_USER32_BITS,
+ (u64) pswmask << 32);
+ FixPerRegisters(target);
+ }
+
+ /* Build a 64 bit psw address from 31 bit address. */
+ if (count > 0 && pos == PT_PSWADDR / 2) {
+ u32 pswaddr;
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &pswaddr, PT_PSWADDR / 2,
+ PT_GPR0 / 2);
+ if (ret == 0)
+ /* Build a 64 bit psw mask from 31 bit mask. */
+ regs->psw.addr = pswaddr & PSW32_ADDR_INSN;
+ }
+
+ /* The GPRs are directly onto the stack. */
+ while (ret == 0 && count > 0 && pos < PT_ACR0 / 2) {
+ u32 value;
+
+ if (kbuf) {
+ value = *(const u32 *) kbuf;
+ kbuf += sizeof(u32);
+ }
+ else if (get_user(value, (const u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+
+ regs->gprs[(pos - PT_GPR0 / 2) / sizeof(u32)] = value;
+ }
+
+ /* The ACRs are kept in the thread_struct. */
+ if (count > 0 && pos < PT_ORIGGPR2 / 2) {
+ if (target == current
+ && (pos != PT_ACR0 / 2
+ || count < sizeof(target->thread.acrs)))
+ save_access_regs(target->thread.acrs);
+
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ target->thread.acrs,
+ PT_ACR0 / 2,
+ PT_ACR0 / 2 + NUM_ACRS * ACR_SIZE);
+
+ if (ret == 0 && target == current)
+ restore_access_regs(target->thread.acrs);
+ }
+
+ /* Finally, the ORIG_GPR2 value. */
+ if (ret == 0 && count > 0) {
+ u32 value;
+ if (kbuf)
+ value = *(const u32 *) kbuf;
+ else if (get_user(value, (const u32 __user *) ubuf))
+ return -EFAULT;
+ regs->orig_gpr2 = value;
+ }
+
+ return ret;
+}
+
+
+/*
+ * This is magic. See per_struct and per_struct32.
+ * By incident the offsets in per_struct are exactly
+ * twice the offsets in per_struct32 for all fields.
+ * The 8 byte fields need special handling though,
+ * because the second half (bytes 4-7) is needed and
+ * not the first half.
+ */
+static unsigned int
+offset_from_per32(unsigned int offset)
+{
+ BUILD_BUG_ON(offsetof(per_struct32, control_regs) != 0);
+ if (offset - offsetof(per_struct32, control_regs) < 3*sizeof(u32)
+ || (offset >= offsetof(per_struct32, starting_addr) &&
+ offset <= offsetof(per_struct32, ending_addr))
+ || offset == offsetof(per_struct32, lowcore.words.address))
+ offset = offset*2 + 4;
+ else
+ offset = offset*2;
+ return offset;
+}
+
+static int
+s390_per_info_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ while (count > 0) {
+ u32 val = *(u32 *) ((char *) &target->thread.per_info
+ + offset_from_per32 (pos));
+ if (kbuf) {
+ *(u32 *) kbuf = val;
+ kbuf += sizeof(u32);
+ }
+ else if (put_user(val, (u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+ }
+ return 0;
+}
+
+static int
+s390_per_info_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ while (count > 0) {
+ u32 val;
+
+ if (kbuf) {
+ val = *(const u32 *) kbuf;
+ kbuf += sizeof(u32);
+ }
+ else if (get_user(val, (const u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+
+ *(u32 *) ((char *) &target->thread.per_info
+ + offset_from_per32 (pos)) = val;
+ }
+ return 0;
+}
+
+
+static const struct utrace_regset s390_compat_regsets[] = {
+ {
+ .size = sizeof(u32), .align = sizeof(u32),
+ .n = sizeof(s390_regs) / sizeof(long),
+ .get = s390_genregs_get, .set = s390_genregs_set
+ },
+ {
+ .size = sizeof(u32), .align = sizeof(u32),
+ .n = sizeof(s390_fp_regs) / sizeof(u32),
+ .get = fpregs_get, .set = fpregs_set
+ },
+ {
+ .size = sizeof(u32), .align = sizeof(u32),
+ .n = sizeof(per_struct) / sizeof(u32),
+ .get = s390_per_info_get, .set = s390_per_info_set
+ },
+};
+
+const struct utrace_regset_view utrace_s390_compat_view = {
+ .name = "s390", .e_machine = EM_S390,
+ .regsets = s390_compat_regsets,
+ .n = sizeof s390_compat_regsets / sizeof s390_compat_regsets[0],
+};
+EXPORT_SYMBOL_GPL(utrace_s390_compat_view);
+#endif /* CONFIG_COMPAT */
+
+
+#ifdef CONFIG_PTRACE
+static const struct ptrace_layout_segment s390_uarea[] = {
+ {PT_PSWMASK, PT_FPC, 0, 0},
+ {PT_FPC, PT_CR_9, 1, 0},
+ {PT_CR_9, PT_IEEE_IP, 2, 0},
+ {PT_IEEE_IP, sizeof(struct user), -1, -1},
+ {0, 0, -1, 0}
+};
+
+fastcall int arch_ptrace(long *request, struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ unsigned long addr, unsigned long data, long *val)
+{
+ ptrace_area parea;
+ unsigned long tmp;
+ int copied;
+
+ switch (*request) {
+ case PTRACE_PEEKUSR:
+ return ptrace_peekusr(child, engine, s390_uarea, addr, data);
+ case PTRACE_POKEUSR:
+ return ptrace_pokeusr(child, engine, s390_uarea, addr, data);
+
+ case PTRACE_PEEKUSR_AREA:
+ case PTRACE_POKEUSR_AREA:
+ if (copy_from_user(&parea, (ptrace_area __user *) addr,
+ sizeof(parea)))
+ return -EFAULT;
+ if ((parea.kernel_addr | parea.len) & (sizeof(data) - 1))
+ return -EIO;
+ return ptrace_layout_access(child, engine,
+ utrace_native_view(current),
+ s390_uarea,
+ parea.kernel_addr, parea.len,
+ (void __user *) parea.process_addr,
+ NULL,
+ *request == PTRACE_POKEUSR_AREA);
+
+ case PTRACE_PEEKTEXT:
+ case PTRACE_PEEKDATA:
+ /* Remove high order bit from address (only for 31 bit). */
+ addr &= PSW_ADDR_INSN;
+ /* read word at location addr. */
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ if (copied != sizeof(tmp))
+ return -EIO;
+ return put_user(tmp, (unsigned long __user *) data);
+
+ case PTRACE_POKETEXT:
+ case PTRACE_POKEDATA:
+ /* Remove high order bit from address (only for 31 bit). */
+ addr &= PSW_ADDR_INSN;
+ /* write the word at location addr. */
+ copied = access_process_vm(child, addr, &data, sizeof(data),1);
+ if (copied != sizeof(data))
+ return -EIO;
+ return 0;
+ }
+
+ return -ENOSYS;
+}
+
+#ifdef CONFIG_COMPAT
+static const struct ptrace_layout_segment s390_compat_uarea[] = {
+ {PT_PSWMASK / 2, PT_FPC / 2, 0, 0},
+ {PT_FPC / 2, PT_CR_9 / 2, 1, 0},
+ {PT_CR_9 / 2, PT_IEEE_IP / 2, 2, 0},
+ {PT_IEEE_IP / 2, sizeof(struct user32), -1, -1},
+ {0, 0, -1, 0}
+};
+
+fastcall int arch_compat_ptrace(compat_long_t *request,
+ struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ compat_ulong_t addr, compat_ulong_t data,
+ compat_long_t *val)
+{
+ ptrace_area_emu31 parea;
+
+ switch (*request) {
+ case PTRACE_PEEKUSR:
+ return ptrace_compat_peekusr(child, engine, s390_compat_uarea,
+ addr, data);
+ case PTRACE_POKEUSR:
+ return ptrace_compat_pokeusr(child, engine, s390_compat_uarea,
+ addr, data);
+ case PTRACE_PEEKUSR_AREA:
+ case PTRACE_POKEUSR_AREA:
+ if (copy_from_user(&parea, ((ptrace_area_emu31 __user *)
+ (unsigned long) addr),
+ sizeof(parea)))
+ return -EFAULT;
+ if ((parea.kernel_addr | parea.len) & (sizeof(data) - 1))
+ return -EIO;
+ return ptrace_layout_access(child, engine,
+ utrace_native_view(current),
+ s390_compat_uarea,
+ parea.kernel_addr, parea.len,
+ (void __user *)
+ (unsigned long) parea.process_addr,
+ NULL,
+ *request == PTRACE_POKEUSR_AREA);
+ }
+
+ return -ENOSYS;
+}
+#endif /* CONFIG_COMPAT */
+#endif /* CONFIG_PTRACE */
+
+
+#if 0 /* XXX */
+
#ifndef CONFIG_64BIT
# define __ADDR_MASK 3
#else
@@ -593,6 +1163,7 @@ do_ptrace_emu31(struct task_struct *chil
copied += sizeof(unsigned int);
}
return 0;
+#if 0 /* XXX */
case PTRACE_GETEVENTMSG:
return put_user((__u32) child->ptrace_message,
(unsigned int __user *) data);
@@ -606,6 +1177,7 @@ do_ptrace_emu31(struct task_struct *chil
return -EINVAL;
return copy_siginfo_from_user32(child->last_siginfo,
(compat_siginfo_t __user *) data);
+#endif
}
return ptrace_request(child, request, addr, data);
}
@@ -656,7 +1228,7 @@ do_ptrace(struct task_struct *child, lon
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
/* make sure the single step bit is not set. */
- clear_single_step(child);
+ tracehook_disable_single_step(child);
wake_up_process(child);
return 0;
@@ -670,7 +1242,7 @@ do_ptrace(struct task_struct *child, lon
return 0;
child->exit_code = SIGKILL;
/* make sure the single step bit is not set. */
- clear_single_step(child);
+ tracehook_disable_single_step(child);
wake_up_process(child);
return 0;
@@ -683,7 +1255,7 @@ do_ptrace(struct task_struct *child, lon
if (data)
set_tsk_thread_flag(child, TIF_SINGLE_STEP);
else
- set_single_step(child);
+ tracehook_enable_single_step(child);
/* give it a chance to run. */
wake_up_process(child);
return 0;
@@ -704,31 +1276,9 @@ do_ptrace(struct task_struct *child, lon
/* Not reached. */
return -EIO;
}
+#endif
-asmlinkage long
-sys_ptrace(long request, long pid, long addr, long data)
-{
- struct task_struct *child;
- int ret;
- lock_kernel();
- if (request == PTRACE_TRACEME) {
- ret = ptrace_traceme();
- goto out;
- }
-
- child = ptrace_get_task_struct(pid);
- if (IS_ERR(child)) {
- ret = PTR_ERR(child);
- goto out;
- }
-
- ret = do_ptrace(child, request, addr, data);
- put_task_struct(child);
-out:
- unlock_kernel();
- return ret;
-}
asmlinkage void
syscall_trace(struct pt_regs *regs, int entryexit)
@@ -736,30 +1286,17 @@ syscall_trace(struct pt_regs *regs, int
if (unlikely(current->audit_context) && entryexit)
audit_syscall_exit(AUDITSC_RESULT(regs->gprs[2]), regs->gprs[2]);
- if (!test_thread_flag(TIF_SYSCALL_TRACE))
- goto out;
- if (!(current->ptrace & PT_PTRACED))
- goto out;
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
- ? 0x80 : 0));
-
- /*
- * If the debuffer has set an invalid system call number,
- * we prepare to skip the system call restart handling.
- */
- if (!entryexit && regs->gprs[2] >= NR_syscalls)
- regs->trap = -1;
+ if (test_thread_flag(TIF_SYSCALL_TRACE)) {
+ tracehook_report_syscall(regs, entryexit);
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
+ /*
+ * If the debugger has set an invalid system call number,
+ * we prepare to skip the system call restart handling.
+ */
+ if (!entryexit && regs->gprs[2] >= NR_syscalls)
+ regs->trap = -1;
}
- out:
+
if (unlikely(current->audit_context) && !entryexit)
audit_syscall_entry(test_thread_flag(TIF_31BIT)?AUDIT_ARCH_S390:AUDIT_ARCH_S390X,
regs->gprs[2], regs->orig_gpr2, regs->gprs[3],
--- linux-2.6/arch/s390/kernel/Makefile.utrace-ptrace-compat
+++ linux-2.6/arch/s390/kernel/Makefile
@@ -34,3 +34,5 @@ obj-$(CONFIG_KEXEC) += $(S390_KEXEC_OBJS
# This is just to get the dependencies...
#
binfmt_elf32.o: $(TOPDIR)/fs/binfmt_elf.c
+
+CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
--- linux-2.6/arch/x86_64/kernel/signal.c.utrace-ptrace-compat
+++ linux-2.6/arch/x86_64/kernel/signal.c
@@ -17,7 +17,7 @@
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/wait.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/unistd.h>
#include <linux/stddef.h>
#include <linux/personality.h>
@@ -333,9 +333,6 @@ static int setup_rt_frame(int sig, struc
see include/asm-x86_64/uaccess.h for details. */
set_fs(USER_DS);
- regs->eflags &= ~TF_MASK;
- if (test_thread_flag(TIF_SINGLESTEP))
- ptrace_notify(SIGTRAP);
#ifdef DEBUG_SIG
printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
current->comm, current->pid, frame, regs->rip, frame->pretcode);
@@ -387,16 +384,12 @@ handle_signal(unsigned long sig, siginfo
}
/*
- * If TF is set due to a debugger (PT_DTRACE), clear the TF
- * flag so that register information in the sigcontext is
- * correct.
+ * If TF is set due to a debugger (TIF_FORCED_TF), clear the TF flag so
+ * that register information in the sigcontext is correct.
*/
- if (unlikely(regs->eflags & TF_MASK)) {
- if (likely(current->ptrace & PT_DTRACE)) {
- current->ptrace &= ~PT_DTRACE;
- regs->eflags &= ~TF_MASK;
- }
- }
+ if (unlikely(regs->eflags & TF_MASK)
+ && likely(test_and_clear_thread_flag(TIF_FORCED_TF)))
+ regs->eflags &= ~TF_MASK;
#ifdef CONFIG_IA32_EMULATION
if (test_thread_flag(TIF_IA32)) {
@@ -417,6 +410,17 @@ handle_signal(unsigned long sig, siginfo
spin_unlock_irq(¤t->sighand->siglock);
}
+ if (ret) {
+ /*
+ * Clear TF when entering the signal handler, but
+ * notify any tracer that was single-stepping it.
+ * The tracer may want to single-step inside the
+ * handler too.
+ */
+ regs->eflags &= ~TF_MASK;
+ tracehook_report_handle_signal(sig, ka, oldset, regs);
+ }
+
return ret;
}
--- linux-2.6/arch/x86_64/kernel/process.c.utrace-ptrace-compat
+++ linux-2.6/arch/x86_64/kernel/process.c
@@ -636,11 +636,6 @@ long sys_execve(char __user *name, char
if (IS_ERR(filename))
return error;
error = do_execve(filename, argv, envp, ®s);
- if (error == 0) {
- task_lock(current);
- current->ptrace &= ~PT_DTRACE;
- task_unlock(current);
- }
putname(filename);
return error;
}
--- linux-2.6/arch/x86_64/kernel/traps.c.utrace-ptrace-compat
+++ linux-2.6/arch/x86_64/kernel/traps.c
@@ -870,14 +870,6 @@ asmlinkage void __kprobes do_debug(struc
*/
if (!user_mode(regs))
goto clear_TF_reenable;
- /*
- * Was the TF flag set by a debugger? If so, clear it now,
- * so that register information is correct.
- */
- if (tsk->ptrace & PT_DTRACE) {
- regs->eflags &= ~TF_MASK;
- tsk->ptrace &= ~PT_DTRACE;
- }
}
/* Ok, finally something we can handle */
--- linux-2.6/arch/x86_64/kernel/ptrace.c.utrace-ptrace-compat
+++ linux-2.6/arch/x86_64/kernel/ptrace.c
@@ -13,12 +13,14 @@
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
+#include <linux/tracehook.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/audit.h>
#include <linux/seccomp.h>
#include <linux/signal.h>
+#include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
@@ -30,6 +32,7 @@
#include <asm/desc.h>
#include <asm/proto.h>
#include <asm/ia32.h>
+#include <asm/prctl.h>
/*
* does not yet catch signals sent when the child dies.
@@ -162,7 +165,7 @@ static int is_at_popf(struct task_struct
return 0;
}
-static void set_singlestep(struct task_struct *child)
+void tracehook_enable_single_step(struct task_struct *child)
{
struct pt_regs *regs = task_pt_regs(child);
@@ -192,19 +195,18 @@ static void set_singlestep(struct task_s
if (is_at_popf(child, regs))
return;
- child->ptrace |= PT_DTRACE;
+ set_tsk_thread_flag(child, TIF_FORCED_TF);
}
-static void clear_singlestep(struct task_struct *child)
+void tracehook_disable_single_step(struct task_struct *child)
{
/* Always clear TIF_SINGLESTEP... */
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
/* But touch TF only if it was set by us.. */
- if (child->ptrace & PT_DTRACE) {
+ if (test_and_clear_tsk_thread_flag(child, TIF_FORCED_TF)) {
struct pt_regs *regs = task_pt_regs(child);
regs->eflags &= ~TRAP_FLAG;
- child->ptrace &= ~PT_DTRACE;
}
}
@@ -215,7 +217,7 @@ static void clear_singlestep(struct task
*/
void ptrace_disable(struct task_struct *child)
{
- clear_singlestep(child);
+ tracehook_disable_single_step(child);
}
static int putreg(struct task_struct *child,
@@ -268,6 +270,7 @@ static int putreg(struct task_struct *ch
tmp = get_stack_long(child, EFL_OFFSET);
tmp &= ~FLAG_MASK;
value |= tmp;
+ clear_tsk_thread_flag(child, TIF_FORCED_TF);
break;
case offsetof(struct user_regs_struct,cs):
if ((value & 3) != 3)
@@ -300,303 +303,431 @@ static unsigned long getreg(struct task_
val = get_stack_long(child, regno);
if (test_tsk_thread_flag(child, TIF_IA32))
val &= 0xffffffff;
+ if (regno == (offsetof(struct user_regs_struct, eflags)
+ - sizeof(struct pt_regs))
+ && test_tsk_thread_flag(child, TIF_FORCED_TF))
+ val &= ~X86_EFLAGS_TF;
return val;
}
}
-long arch_ptrace(struct task_struct *child, long request, long addr, long data)
-{
- long i, ret;
- unsigned ui;
+static int
+genregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (kbuf) {
+ unsigned long *kp = kbuf;
+ while (count > 0) {
+ *kp++ = getreg(target, pos);
+ pos += sizeof(long);
+ count -= sizeof(long);
+ }
+ }
+ else {
+ unsigned long __user *up = ubuf;
+ while (count > 0) {
+ if (__put_user(getreg(target, pos), up++))
+ return -EFAULT;
+ pos += sizeof(long);
+ count -= sizeof(long);
+ }
+ }
- switch (request) {
- /* when I and D space are separate, these will need to be fixed. */
- 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;
+ return 0;
+}
+
+static int
+genregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret = 0;
+
+ if (kbuf) {
+ const unsigned long *kp = kbuf;
+ while (!ret && count > 0) {
+ ret = putreg(target, pos, *kp++);
+ pos += sizeof(long);
+ count -= sizeof(long);
+ }
+ }
+ else {
+ int ret = 0;
+ const unsigned long __user *up = ubuf;
+ while (!ret && count > 0) {
+ unsigned long val;
+ ret = __get_user(val, up++);
+ if (!ret)
+ ret = putreg(target, pos, val);
+ pos += sizeof(long);
+ count -= sizeof(long);
+ }
}
- /* read the word at location addr in the USER area. */
- case PTRACE_PEEKUSR: {
- unsigned long tmp;
+ return ret;
+}
- ret = -EIO;
- if ((addr & 7) ||
- addr > sizeof(struct user) - 7)
- break;
- switch (addr) {
- case 0 ... sizeof(struct user_regs_struct) - sizeof(long):
- tmp = getreg(child, addr);
- break;
- case offsetof(struct user, u_debugreg[0]):
- tmp = child->thread.debugreg0;
- break;
- case offsetof(struct user, u_debugreg[1]):
- tmp = child->thread.debugreg1;
- break;
- case offsetof(struct user, u_debugreg[2]):
- tmp = child->thread.debugreg2;
- break;
- case offsetof(struct user, u_debugreg[3]):
- tmp = child->thread.debugreg3;
- break;
- case offsetof(struct user, u_debugreg[6]):
- tmp = child->thread.debugreg6;
- break;
- case offsetof(struct user, u_debugreg[7]):
- tmp = child->thread.debugreg7;
- break;
- default:
- tmp = 0;
- break;
+static int
+dbregs_active(struct task_struct *tsk, const struct utrace_regset *regset)
+{
+ if (tsk->thread.debugreg6 | tsk->thread.debugreg7)
+ return 8;
+ return 0;
+}
+
+static int
+dbregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ for (pos >>= 3, count >>= 3; count > 0; --count, ++pos) {
+ unsigned long val;
+
+ /*
+ * The hardware updates the status register on a debug trap,
+ * but do_debug (traps.c) saves it for us when that happens.
+ * So whether the target is current or not, debugregN is good.
+ */
+ val = 0;
+ switch (pos) {
+ case 0: val = target->thread.debugreg0; break;
+ case 1: val = target->thread.debugreg1; break;
+ case 2: val = target->thread.debugreg2; break;
+ case 3: val = target->thread.debugreg3; break;
+ case 6: val = target->thread.debugreg6; break;
+ case 7: val = target->thread.debugreg7; break;
+ }
+
+ if (kbuf) {
+ *(unsigned long *) kbuf = val;
+ kbuf += sizeof(unsigned long);
+ }
+ else {
+ if (__put_user(val, (unsigned long __user *) ubuf))
+ return -EFAULT;
+ ubuf += sizeof(unsigned long);
}
- ret = put_user(tmp,(unsigned long __user *) data);
- break;
}
- /* when I and D space are separate, this will have to be fixed. */
- 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;
+ return 0;
+}
- case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
- {
- int dsize = test_tsk_thread_flag(child, TIF_IA32) ? 3 : 7;
- ret = -EIO;
- if ((addr & 7) ||
- addr > sizeof(struct user) - 7)
- break;
+static int
+dbregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ unsigned long maxaddr = TASK_SIZE_OF(target);
+ maxaddr -= test_tsk_thread_flag(target, TIF_IA32) ? 3 : 7;
+
+ for (pos >>= 3, count >>= 3; count > 0; --count, ++pos) {
+ unsigned long val;
+ unsigned int i;
+
+ if (kbuf) {
+ val = *(const unsigned long *) kbuf;
+ kbuf += sizeof(unsigned long);
+ }
+ else {
+ if (__get_user(val, (unsigned long __user *) ubuf))
+ return -EFAULT;
+ ubuf += sizeof(unsigned long);
+ }
+
+ switch (pos) {
+#define SET_DBREG(n) \
+ target->thread.debugreg##n = val; \
+ if (target == current) \
+ set_debugreg(target->thread.debugreg##n, n)
- switch (addr) {
- case 0 ... sizeof(struct user_regs_struct) - sizeof(long):
- ret = putreg(child, addr, data);
+ case 0:
+ if (val >= maxaddr)
+ return -EIO;
+ SET_DBREG(0);
break;
- /* Disallows to set a breakpoint into the vsyscall */
- case offsetof(struct user, u_debugreg[0]):
- if (data >= TASK_SIZE_OF(child) - dsize) break;
- child->thread.debugreg0 = data;
- ret = 0;
+ case 1:
+ if (val >= maxaddr)
+ return -EIO;
+ SET_DBREG(1);
break;
- case offsetof(struct user, u_debugreg[1]):
- if (data >= TASK_SIZE_OF(child) - dsize) break;
- child->thread.debugreg1 = data;
- ret = 0;
+ case 2:
+ if (val >= maxaddr)
+ return -EIO;
+ SET_DBREG(2);
break;
- case offsetof(struct user, u_debugreg[2]):
- if (data >= TASK_SIZE_OF(child) - dsize) break;
- child->thread.debugreg2 = data;
- ret = 0;
+ case 3:
+ if (val >= maxaddr)
+ return -EIO;
+ SET_DBREG(3);
break;
- case offsetof(struct user, u_debugreg[3]):
- if (data >= TASK_SIZE_OF(child) - dsize) break;
- child->thread.debugreg3 = data;
- ret = 0;
+ case 4:
+ case 5:
+ if (val != 0)
+ return -EIO;
+ break;
+ case 6:
+ if (val >> 32)
+ return -EIO;
+ SET_DBREG(6);
break;
- case offsetof(struct user, u_debugreg[6]):
- if (data >> 32)
- break;
- child->thread.debugreg6 = data;
- ret = 0;
+ case 7:
+ /*
+ * See arch/i386/kernel/ptrace.c for an explanation
+ * of this awkward check.
+ */
+ val &= ~DR_CONTROL_RESERVED;
+ for (i = 0; i < 4; i++)
+ if ((0x5554 >> ((val >> (16 + 4*i)) & 0xf))
+ & 1)
+ return -EIO;
+ SET_DBREG(7);
break;
- case offsetof(struct user, u_debugreg[7]):
- /* See arch/i386/kernel/ptrace.c for an explanation of
- * this awkward check.*/
- data &= ~DR_CONTROL_RESERVED;
- for(i=0; i<4; i++)
- if ((0x5554 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
- break;
- if (i == 4) {
- child->thread.debugreg7 = data;
- ret = 0;
- }
- break;
+#undef SET_DBREG
}
- break;
}
- case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
- case PTRACE_CONT: /* restart after signal. */
- ret = -EIO;
- if (!valid_signal(data))
- break;
- if (request == PTRACE_SYSCALL)
- set_tsk_thread_flag(child,TIF_SYSCALL_TRACE);
- else
- clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE);
- clear_tsk_thread_flag(child, TIF_SINGLESTEP);
- child->exit_code = data;
- /* make sure the single step bit is not set. */
- clear_singlestep(child);
- wake_up_process(child);
- ret = 0;
- break;
+ return 0;
+}
-#ifdef CONFIG_IA32_EMULATION
- /* This makes only sense with 32bit programs. Allow a
- 64bit debugger to fully examine them too. Better
- don't use it against 64bit processes, use
- PTRACE_ARCH_PRCTL instead. */
- case PTRACE_SET_THREAD_AREA: {
- struct user_desc __user *p;
- int old;
- p = (struct user_desc __user *)data;
- get_user(old, &p->entry_number);
- put_user(addr, &p->entry_number);
- ret = do_set_thread_area(&child->thread, p);
- put_user(old, &p->entry_number);
- break;
- case PTRACE_GET_THREAD_AREA:
- p = (struct user_desc __user *)data;
- get_user(old, &p->entry_number);
- put_user(addr, &p->entry_number);
- ret = do_get_thread_area(&child->thread, p);
- put_user(old, &p->entry_number);
- break;
- }
-#endif
- /* normal 64bit interface to access TLS data.
- Works just like arch_prctl, except that the arguments
- are reversed. */
- case PTRACE_ARCH_PRCTL:
- ret = do_arch_prctl(child, data, addr);
- break;
-/*
- * make the child exit. Best I can do is send it a sigkill.
- * perhaps it should be put in the status that it wants to
- * exit.
- */
- case PTRACE_KILL:
- ret = 0;
- if (child->exit_state == EXIT_ZOMBIE) /* already dead */
- break;
- clear_tsk_thread_flag(child, TIF_SINGLESTEP);
- child->exit_code = SIGKILL;
- /* make sure the single step bit is not set. */
- clear_singlestep(child);
- wake_up_process(child);
- break;
-
- case PTRACE_SINGLESTEP: /* set the trap flag. */
- ret = -EIO;
- if (!valid_signal(data))
- break;
- clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE);
- set_singlestep(child);
- child->exit_code = data;
- /* give it a chance to run. */
- wake_up_process(child);
- ret = 0;
- break;
-
- case PTRACE_DETACH:
- /* detach a process that was attached. */
- ret = ptrace_detach(child, data);
- break;
-
- case PTRACE_GETREGS: { /* Get all gp regs from the child. */
- if (!access_ok(VERIFY_WRITE, (unsigned __user *)data,
- sizeof(struct user_regs_struct))) {
- ret = -EIO;
- break;
- }
- ret = 0;
- for (ui = 0; ui < sizeof(struct user_regs_struct); ui += sizeof(long)) {
- ret |= __put_user(getreg(child, ui),(unsigned long __user *) data);
- data += sizeof(long);
- }
- break;
+static int
+fpregs_active(struct task_struct *target, const struct utrace_regset *regset)
+{
+ return tsk_used_math(target) ? regset->n : 0;
+}
+
+static int
+fpregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (tsk_used_math(target)) {
+ if (target == current)
+ unlazy_fpu(target);
}
+ else
+ init_fpu(target);
- case PTRACE_SETREGS: { /* Set all gp regs in the child. */
- unsigned long tmp;
- if (!access_ok(VERIFY_READ, (unsigned __user *)data,
- sizeof(struct user_regs_struct))) {
- ret = -EIO;
- break;
- }
- ret = 0;
- for (ui = 0; ui < sizeof(struct user_regs_struct); ui += sizeof(long)) {
- ret |= __get_user(tmp, (unsigned long __user *) data);
- putreg(child, ui, tmp);
- data += sizeof(long);
- }
- break;
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.i387.fxsave, 0, -1);
+}
+
+static int
+fpregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+
+ if (tsk_used_math(target)) {
+ if (target == current)
+ unlazy_fpu(target);
}
+ else if (pos == 0 && count == sizeof(struct user_i387_struct))
+ set_stopped_child_used_math(target);
+ else
+ init_fpu(target);
- case PTRACE_GETFPREGS: { /* Get the child extended FPU state. */
- if (!access_ok(VERIFY_WRITE, (unsigned __user *)data,
- sizeof(struct user_i387_struct))) {
- ret = -EIO;
- break;
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.i387.fxsave, 0, -1);
+
+ target->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
+
+ return ret;
+}
+
+static int
+fsgs_active(struct task_struct *tsk, const struct utrace_regset *regset)
+{
+ if (tsk->thread.gsindex == GS_TLS_SEL || tsk->thread.gs)
+ return 2;
+ if (tsk->thread.fsindex == FS_TLS_SEL || tsk->thread.fs)
+ return 1;
+ return 0;
+}
+
+static inline u32 read_32bit_tls(struct task_struct *t, int tls)
+{
+ struct desc_struct *desc = (void *)t->thread.tls_array;
+ desc += tls;
+ return desc->base0 |
+ (((u32)desc->base1) << 16) |
+ (((u32)desc->base2) << 24);
+}
+
+static int
+fsgs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ const unsigned long *kaddr = kbuf;
+ const unsigned long __user *uaddr = ubuf;
+ unsigned long addr;
+
+ /*
+ * XXX why the MSR reads here?
+ * Can anything change the MSRs without changing thread.fs first?
+ */
+ if (pos == 0) { /* FS */
+ if (kaddr)
+ addr = *kaddr++;
+ else if (__get_user(addr, uaddr++))
+ return -EFAULT;
+ if (target->thread.fsindex == FS_TLS_SEL)
+ addr = read_32bit_tls(target, FS_TLS);
+ else if (target == current) {
+ rdmsrl(MSR_FS_BASE, addr);
}
- ret = get_fpregs((struct user_i387_struct __user *)data, child);
- break;
+ else
+ addr = target->thread.fs;
}
- case PTRACE_SETFPREGS: { /* Set the child extended FPU state. */
- if (!access_ok(VERIFY_READ, (unsigned __user *)data,
- sizeof(struct user_i387_struct))) {
- ret = -EIO;
- break;
+ if (count > sizeof(unsigned long)) { /* GS */
+ if (kaddr)
+ addr = *kaddr;
+ else if (__get_user(addr, uaddr))
+ return -EFAULT;
+ if (target->thread.fsindex == GS_TLS_SEL)
+ addr = read_32bit_tls(target, GS_TLS);
+ else if (target == current) {
+ rdmsrl(MSR_GS_BASE, addr);
}
- set_stopped_child_used_math(child);
- ret = set_fpregs(child, (struct user_i387_struct __user *)data);
- break;
+ else
+ addr = target->thread.fs;
}
- default:
- ret = ptrace_request(child, request, addr, data);
- break;
+ return 0;
+}
+
+static int
+fsgs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ const unsigned long *kaddr = kbuf;
+ const unsigned long __user *uaddr = ubuf;
+ unsigned long addr;
+ int ret = 0;
+
+ if (pos == 0) { /* FS */
+ if (kaddr)
+ addr = *kaddr++;
+ else if (__get_user(addr, uaddr++))
+ return -EFAULT;
+ ret = do_arch_prctl(target, ARCH_SET_FS, addr);
+ }
+
+ if (!ret && count > sizeof(unsigned long)) { /* GS */
+ if (kaddr)
+ addr = *kaddr;
+ else if (__get_user(addr, uaddr))
+ return -EFAULT;
+ ret = do_arch_prctl(target, ARCH_SET_GS, addr);
}
+
return ret;
}
-static void syscall_trace(struct pt_regs *regs)
-{
-#if 0
- printk("trace %s rip %lx rsp %lx rax %d origrax %d caller %lx tiflags %x ptrace %x\n",
- current->comm,
- regs->rip, regs->rsp, regs->rax, regs->orig_rax, __builtin_return_address(0),
- current_thread_info()->flags, current->ptrace);
+/*
+ * These are our native regset flavors.
+ * XXX ioperm? vm86?
+ */
+static const struct utrace_regset native_regsets[] = {
+ {
+ .n = sizeof(struct user_regs_struct)/8, .size = 8, .align = 8,
+ .get = genregs_get, .set = genregs_set
+ },
+ {
+ .n = sizeof(struct user_i387_struct) / sizeof(long),
+ .size = sizeof(long), .align = sizeof(long),
+ .active = fpregs_active,
+ .get = fpregs_get, .set = fpregs_set
+ },
+ {
+ .n = 2, .size = sizeof(long), .align = sizeof(long),
+ .active = fsgs_active,
+ .get = fsgs_get, .set = fsgs_set
+ },
+ {
+ .n = 8, .size = sizeof(long), .align = sizeof(long),
+ .active = dbregs_active,
+ .get = dbregs_get, .set = dbregs_set
+ },
+};
+
+const struct utrace_regset_view utrace_x86_64_native = {
+ .name = "x86-64", .e_machine = EM_X86_64,
+ .regsets = native_regsets,
+ .n = sizeof native_regsets / sizeof native_regsets[0],
+};
+EXPORT_SYMBOL_GPL(utrace_x86_64_native);
+
+
+#ifdef CONFIG_PTRACE
+static const struct ptrace_layout_segment x86_64_uarea[] = {
+ {0, sizeof(struct user_regs_struct), 0, 0},
+ {offsetof(struct user, u_debugreg[0]),
+ offsetof(struct user, u_debugreg[4]), 3, 0},
+ {offsetof(struct user, u_debugreg[6]),
+ offsetof(struct user, u_debugreg[8]), 3, 6 * sizeof(long)},
+ {0, 0, -1, 0}
+};
+
+fastcall int arch_ptrace(long *req, struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ unsigned long addr, unsigned long data, long *val)
+{
+ switch (*req) {
+ case PTRACE_PEEKUSR:
+ return ptrace_peekusr(child, engine, x86_64_uarea, addr, data);
+ case PTRACE_POKEUSR:
+ return ptrace_pokeusr(child, engine, x86_64_uarea, addr, data);
+ case PTRACE_GETREGS:
+ return ptrace_whole_regset(child, engine, data, 0, 0);
+ case PTRACE_SETREGS:
+ return ptrace_whole_regset(child, engine, data, 0, 1);
+ case PTRACE_GETFPREGS:
+ return ptrace_whole_regset(child, engine, data, 1, 0);
+ case PTRACE_SETFPREGS:
+ return ptrace_whole_regset(child, engine, data, 1, 1);
+#ifdef CONFIG_IA32_EMULATION
+ case PTRACE_GET_THREAD_AREA:
+ case PTRACE_SET_THREAD_AREA:
+ return ptrace_onereg_access(child, engine,
+ &utrace_ia32_view, 3,
+ addr, (void __user *)data,
+ *req == PTRACE_SET_THREAD_AREA);
#endif
-
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
- ? 0x80 : 0));
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
+ /* 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;
}
+#endif /* CONFIG_PTRACE */
+
asmlinkage void syscall_trace_enter(struct pt_regs *regs)
{
/* do the secure computing check first */
secure_computing(regs->orig_rax);
- if (test_thread_flag(TIF_SYSCALL_TRACE)
- && (current->ptrace & PT_PTRACED))
- syscall_trace(regs);
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall(regs, 0);
if (unlikely(current->audit_context)) {
if (test_thread_flag(TIF_IA32)) {
@@ -618,8 +749,11 @@ asmlinkage void syscall_trace_leave(stru
if (unlikely(current->audit_context))
audit_syscall_exit(AUDITSC_RESULT(regs->rax), regs->rax);
- if ((test_thread_flag(TIF_SYSCALL_TRACE)
- || test_thread_flag(TIF_SINGLESTEP))
- && (current->ptrace & PT_PTRACED))
- syscall_trace(regs);
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall(regs, 1);
+
+ if (test_thread_flag(TIF_SINGLESTEP)) {
+ force_sig(SIGTRAP, current); /* XXX */
+ tracehook_report_syscall_step(regs);
+ }
}
--- linux-2.6/arch/x86_64/ia32/ptrace32.c.utrace-ptrace-compat
+++ linux-2.6/arch/x86_64/ia32/ptrace32.c
@@ -16,7 +16,11 @@
#include <linux/unistd.h>
#include <linux/mm.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
+#include <linux/module.h>
+#include <linux/elf.h>
#include <asm/ptrace.h>
+#include <asm/tracehook.h>
#include <asm/compat.h>
#include <asm/uaccess.h>
#include <asm/user32.h>
@@ -25,7 +29,8 @@
#include <asm/debugreg.h>
#include <asm/i387.h>
#include <asm/fpu32.h>
-#include <asm/ia32.h>
+#include <asm/ldt.h>
+#include <asm/desc.h>
/*
* Determines which flags the user has access to [1 = access, 0 = no access].
@@ -35,34 +40,33 @@
#define FLAG_MASK 0x54dd5UL
#define R32(l,q) \
- case offsetof(struct user32, regs.l): stack[offsetof(struct pt_regs, q)/8] = val; break
+ case offsetof(struct user_regs_struct32, l): stack[offsetof(struct pt_regs, q)/8] = val; break
static int putreg32(struct task_struct *child, unsigned regno, u32 val)
{
- int i;
__u64 *stack = (__u64 *)task_pt_regs(child);
switch (regno) {
- case offsetof(struct user32, regs.fs):
+ case offsetof(struct user_regs_struct32, fs):
if (val && (val & 3) != 3) return -EIO;
child->thread.fsindex = val & 0xffff;
break;
- case offsetof(struct user32, regs.gs):
+ case offsetof(struct user_regs_struct32, gs):
if (val && (val & 3) != 3) return -EIO;
child->thread.gsindex = val & 0xffff;
break;
- case offsetof(struct user32, regs.ds):
+ case offsetof(struct user_regs_struct32, ds):
if (val && (val & 3) != 3) return -EIO;
child->thread.ds = val & 0xffff;
break;
- case offsetof(struct user32, regs.es):
+ case offsetof(struct user_regs_struct32, es):
child->thread.es = val & 0xffff;
break;
- case offsetof(struct user32, regs.ss):
+ case offsetof(struct user_regs_struct32, ss):
if ((val & 3) != 3) return -EIO;
stack[offsetof(struct pt_regs, ss)/8] = val & 0xffff;
break;
- case offsetof(struct user32, regs.cs):
+ case offsetof(struct user_regs_struct32, cs):
if ((val & 3) != 3) return -EIO;
stack[offsetof(struct pt_regs, cs)/8] = val & 0xffff;
break;
@@ -78,53 +82,16 @@ static int putreg32(struct task_struct *
R32(eip, rip);
R32(esp, rsp);
- case offsetof(struct user32, regs.eflags): {
+ case offsetof(struct user_regs_struct32, eflags): {
__u64 *flags = &stack[offsetof(struct pt_regs, eflags)/8];
val &= FLAG_MASK;
*flags = val | (*flags & ~FLAG_MASK);
+ clear_tsk_thread_flag(child, TIF_FORCED_TF);
break;
}
- case offsetof(struct user32, u_debugreg[4]):
- case offsetof(struct user32, u_debugreg[5]):
- return -EIO;
-
- case offsetof(struct user32, u_debugreg[0]):
- child->thread.debugreg0 = val;
- break;
-
- case offsetof(struct user32, u_debugreg[1]):
- child->thread.debugreg1 = val;
- break;
-
- case offsetof(struct user32, u_debugreg[2]):
- child->thread.debugreg2 = val;
- break;
-
- case offsetof(struct user32, u_debugreg[3]):
- child->thread.debugreg3 = val;
- break;
-
- case offsetof(struct user32, u_debugreg[6]):
- child->thread.debugreg6 = val;
- break;
-
- case offsetof(struct user32, u_debugreg[7]):
- val &= ~DR_CONTROL_RESERVED;
- /* See arch/i386/kernel/ptrace.c for an explanation of
- * this awkward check.*/
- for(i=0; i<4; i++)
- if ((0x5454 >> ((val >> (16 + 4*i)) & 0xf)) & 1)
- return -EIO;
- child->thread.debugreg7 = val;
- break;
-
default:
- if (regno > sizeof(struct user32) || (regno & 3))
- return -EIO;
-
- /* Other dummy fields in the virtual user structure are ignored */
- break;
+ BUG();
}
return 0;
}
@@ -132,24 +99,25 @@ static int putreg32(struct task_struct *
#undef R32
#define R32(l,q) \
- case offsetof(struct user32, regs.l): *val = stack[offsetof(struct pt_regs, q)/8]; break
+ case offsetof(struct user_regs_struct32, l): val = stack[offsetof(struct pt_regs, q)/8]; break
-static int getreg32(struct task_struct *child, unsigned regno, u32 *val)
+static int getreg32(struct task_struct *child, unsigned regno)
{
__u64 *stack = (__u64 *)task_pt_regs(child);
+ u32 val;
switch (regno) {
- case offsetof(struct user32, regs.fs):
- *val = child->thread.fsindex;
+ case offsetof(struct user_regs_struct32, fs):
+ val = child->thread.fsindex;
break;
- case offsetof(struct user32, regs.gs):
- *val = child->thread.gsindex;
+ case offsetof(struct user_regs_struct32, gs):
+ val = child->thread.gsindex;
break;
- case offsetof(struct user32, regs.ds):
- *val = child->thread.ds;
+ case offsetof(struct user_regs_struct32, ds):
+ val = child->thread.ds;
break;
- case offsetof(struct user32, regs.es):
- *val = child->thread.es;
+ case offsetof(struct user_regs_struct32, es):
+ val = child->thread.es;
break;
R32(cs, cs);
@@ -163,232 +131,503 @@ static int getreg32(struct task_struct *
R32(eax, rax);
R32(orig_eax, orig_rax);
R32(eip, rip);
- R32(eflags, eflags);
R32(esp, rsp);
- case offsetof(struct user32, u_debugreg[0]):
- *val = child->thread.debugreg0;
- break;
- case offsetof(struct user32, u_debugreg[1]):
- *val = child->thread.debugreg1;
- break;
- case offsetof(struct user32, u_debugreg[2]):
- *val = child->thread.debugreg2;
- break;
- case offsetof(struct user32, u_debugreg[3]):
- *val = child->thread.debugreg3;
- break;
- case offsetof(struct user32, u_debugreg[6]):
- *val = child->thread.debugreg6;
- break;
- case offsetof(struct user32, u_debugreg[7]):
- *val = child->thread.debugreg7;
+ case offsetof(struct user_regs_struct32, eflags):
+ val = stack[offsetof(struct pt_regs, eflags) / 8];
+ if (test_tsk_thread_flag(child, TIF_FORCED_TF))
+ val &= ~X86_EFLAGS_TF;
break;
default:
- if (regno > sizeof(struct user32) || (regno & 3))
- return -EIO;
-
- /* Other dummy fields in the virtual user structure are ignored */
- *val = 0;
+ BUG();
+ val = -1;
break;
}
- return 0;
+
+ return val;
}
#undef R32
-static long ptrace32_siginfo(unsigned request, u32 pid, u32 addr, u32 data)
+static int
+ia32_genregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
{
+ if (kbuf) {
+ u32 *kp = kbuf;
+ while (count > 0) {
+ *kp++ = getreg32(target, pos);
+ pos += 4;
+ count -= 4;
+ }
+ }
+ else {
+ u32 __user *up = ubuf;
+ while (count > 0) {
+ if (__put_user(getreg32(target, pos), up++))
+ return -EFAULT;
+ pos += 4;
+ count -= 4;
+ }
+ }
+
+ return 0;
+}
+
+static int
+ia32_genregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret = 0;
+
+ if (kbuf) {
+ const u32 *kp = kbuf;
+ while (!ret && count > 0) {
+ ret = putreg32(target, pos, *kp++);
+ pos += 4;
+ count -= 4;
+ }
+ }
+ else {
+ int ret = 0;
+ const u32 __user *up = ubuf;
+ while (!ret && count > 0) {
+ u32 val;
+ ret = __get_user(val, up++);
+ if (!ret)
+ ret = putreg32(target, pos, val);
+ pos += 4;
+ count -= 4;
+ }
+ }
+
+ return ret;
+}
+
+static int
+ia32_fpregs_active(struct task_struct *target,
+ const struct utrace_regset *regset)
+{
+ return tsk_used_math(target) ? regset->n : 0;
+}
+
+static int
+ia32_fpregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ struct user_i387_ia32_struct fp;
int ret;
- compat_siginfo_t *si32 = (compat_siginfo_t *)compat_ptr(data);
- siginfo_t ssi;
- siginfo_t *si = compat_alloc_user_space(sizeof(siginfo_t));
- if (request == PTRACE_SETSIGINFO) {
- memset(&ssi, 0, sizeof(siginfo_t));
- ret = copy_siginfo_from_user32(&ssi, si32);
+
+ if (tsk_used_math(target)) {
+ if (target == current)
+ unlazy_fpu(target);
+ }
+ else
+ init_fpu(target);
+
+ ret = get_fpregs32(&fp, target);
+ if (ret == 0)
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &fp, 0, -1);
+
+ return ret;
+}
+
+static int
+ia32_fpregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct user_i387_ia32_struct fp;
+ int ret;
+
+ if (tsk_used_math(target)) {
+ if (target == current)
+ unlazy_fpu(target);
+ }
+ else if (pos == 0 && count == sizeof(fp))
+ set_stopped_child_used_math(target);
+ else
+ init_fpu(target);
+
+ if (pos > 0 || count < sizeof(fp)) {
+ ret = get_fpregs32(&fp, target);
+ if (ret == 0)
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &fp, 0, -1);
if (ret)
return ret;
- if (copy_to_user(si, &ssi, sizeof(siginfo_t)))
- return -EFAULT;
+ kbuf = &fp;
}
- ret = sys_ptrace(request, pid, addr, (unsigned long)si);
- if (ret)
- return ret;
- if (request == PTRACE_GETSIGINFO) {
- if (copy_from_user(&ssi, si, sizeof(siginfo_t)))
+ else if (kbuf == NULL) {
+ if (__copy_from_user(&fp, ubuf, sizeof(fp)))
return -EFAULT;
- ret = copy_siginfo_to_user32(si32, &ssi);
+ kbuf = &fp;
}
- return ret;
+
+ return set_fpregs32(target, kbuf);
}
-asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data)
+static int
+ia32_fpxregs_active(struct task_struct *target,
+ const struct utrace_regset *regset)
{
- struct task_struct *child;
- struct pt_regs *childregs;
- void __user *datap = compat_ptr(data);
- int ret;
- __u32 val;
+ return tsk_used_math(target) ? regset->n : 0;
+}
- switch (request) {
- case PTRACE_TRACEME:
- case PTRACE_ATTACH:
- case PTRACE_KILL:
- case PTRACE_CONT:
- case PTRACE_SINGLESTEP:
- case PTRACE_DETACH:
- case PTRACE_SYSCALL:
- case PTRACE_SETOPTIONS:
- return sys_ptrace(request, pid, addr, data);
+static int
+ia32_fpxregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (tsk_used_math(target)) {
+ if (target == current)
+ unlazy_fpu(target);
+ }
+ else
+ init_fpu(target);
- default:
- return -EINVAL;
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.i387.fxsave, 0, -1);
+}
- case PTRACE_PEEKTEXT:
- case PTRACE_PEEKDATA:
- case PTRACE_POKEDATA:
- case PTRACE_POKETEXT:
- case PTRACE_POKEUSR:
- case PTRACE_PEEKUSR:
- case PTRACE_GETREGS:
- case PTRACE_SETREGS:
- case PTRACE_SETFPREGS:
- case PTRACE_GETFPREGS:
- case PTRACE_SETFPXREGS:
- case PTRACE_GETFPXREGS:
- case PTRACE_GETEVENTMSG:
- break;
+static int
+ia32_fpxregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
- case PTRACE_SETSIGINFO:
- case PTRACE_GETSIGINFO:
- return ptrace32_siginfo(request, pid, addr, data);
- }
+{
+ int ret;
- child = ptrace_get_task_struct(pid);
- if (IS_ERR(child))
- return PTR_ERR(child);
+ if (tsk_used_math(target)) {
+ if (target == current)
+ unlazy_fpu(target);
+ }
+ else if (pos == 0 && count == sizeof(struct i387_fxsave_struct))
+ set_stopped_child_used_math(target);
+ else
+ init_fpu(target);
- ret = ptrace_check_attach(child, request == PTRACE_KILL);
- if (ret < 0)
- goto out;
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.i387.fxsave, 0, -1);
- childregs = task_pt_regs(child);
+ target->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
- switch (request) {
- case PTRACE_PEEKDATA:
- case PTRACE_PEEKTEXT:
- ret = 0;
- if (access_process_vm(child, addr, &val, sizeof(u32), 0)!=sizeof(u32))
- ret = -EIO;
- else
- ret = put_user(val, (unsigned int __user *)datap);
- break;
+ return ret;
+}
- case PTRACE_POKEDATA:
- case PTRACE_POKETEXT:
- ret = 0;
- if (access_process_vm(child, addr, &data, sizeof(u32), 1)!=sizeof(u32))
- ret = -EIO;
- break;
+static int
+ia32_dbregs_active(struct task_struct *tsk, const struct utrace_regset *regset)
+{
+ if (tsk->thread.debugreg6 | tsk->thread.debugreg7)
+ return 8;
+ return 0;
+}
- case PTRACE_PEEKUSR:
- ret = getreg32(child, addr, &val);
- if (ret == 0)
- ret = put_user(val, (__u32 __user *)datap);
- break;
+static int
+ia32_dbregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ for (pos >>= 2, count >>= 2; count > 0; --count, ++pos) {
+ u32 val;
- case PTRACE_POKEUSR:
- ret = putreg32(child, addr, data);
- break;
+ /*
+ * The hardware updates the status register on a debug trap,
+ * but do_debug (traps.c) saves it for us when that happens.
+ * So whether the target is current or not, debugregN is good.
+ */
+ val = 0;
+ switch (pos) {
+ case 0: val = target->thread.debugreg0; break;
+ case 1: val = target->thread.debugreg1; break;
+ case 2: val = target->thread.debugreg2; break;
+ case 3: val = target->thread.debugreg3; break;
+ case 6: val = target->thread.debugreg6; break;
+ case 7: val = target->thread.debugreg7; break;
+ }
- case PTRACE_GETREGS: { /* Get all gp regs from the child. */
- int i;
- if (!access_ok(VERIFY_WRITE, datap, 16*4)) {
- ret = -EIO;
- break;
+ if (kbuf) {
+ *(u32 *) kbuf = val;
+ kbuf += sizeof(u32);
}
- ret = 0;
- for ( i = 0; i <= 16*4 ; i += sizeof(__u32) ) {
- getreg32(child, i, &val);
- ret |= __put_user(val,(u32 __user *)datap);
- datap += sizeof(u32);
+ else {
+ if (__put_user(val, (u32 __user *) ubuf))
+ return -EFAULT;
+ ubuf += sizeof(u32);
}
- break;
}
- case PTRACE_SETREGS: { /* Set all gp regs in the child. */
- unsigned long tmp;
- int i;
- if (!access_ok(VERIFY_READ, datap, 16*4)) {
- ret = -EIO;
- break;
+ return 0;
+}
+
+static int
+ia32_dbregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ /*
+ * We'll just hijack the native setter to do the real work for us.
+ */
+ const struct utrace_regset *dbregset = &utrace_x86_64_native.regsets[2];
+
+ int ret = 0;
+
+ for (pos >>= 2, count >>= 2; count > 0; --count, ++pos) {
+ unsigned long val;
+
+ if (kbuf) {
+ val = *(const u32 *) kbuf;
+ kbuf += sizeof(u32);
}
- ret = 0;
- for ( i = 0; i <= 16*4; i += sizeof(u32) ) {
- ret |= __get_user(tmp, (u32 __user *)datap);
- putreg32(child, i, tmp);
- datap += sizeof(u32);
+ else {
+ if (__get_user(val, (u32 __user *) ubuf))
+ return -EFAULT;
+ ubuf += sizeof(u32);
}
- break;
- }
- case PTRACE_GETFPREGS:
- ret = -EIO;
- if (!access_ok(VERIFY_READ, compat_ptr(data),
- sizeof(struct user_i387_struct)))
- break;
- save_i387_ia32(child, datap, childregs, 1);
- ret = 0;
+ ret = (*dbregset->set)(target, dbregset, pos * sizeof(long),
+ sizeof(val), &val, NULL);
+ if (ret)
break;
+ }
- case PTRACE_SETFPREGS:
- ret = -EIO;
- if (!access_ok(VERIFY_WRITE, datap,
- sizeof(struct user_i387_struct)))
- break;
- ret = 0;
- /* don't check EFAULT to be bug-to-bug compatible to i386 */
- restore_i387_ia32(child, datap, 1);
- break;
+ return ret;
+}
- case PTRACE_GETFPXREGS: {
- struct user32_fxsr_struct __user *u = datap;
- init_fpu(child);
- ret = -EIO;
- if (!access_ok(VERIFY_WRITE, u, sizeof(*u)))
- break;
- ret = -EFAULT;
- if (__copy_to_user(u, &child->thread.i387.fxsave, sizeof(*u)))
- break;
- ret = __put_user(childregs->cs, &u->fcs);
- ret |= __put_user(child->thread.ds, &u->fos);
- break;
- }
- case PTRACE_SETFPXREGS: {
- struct user32_fxsr_struct __user *u = datap;
- unlazy_fpu(child);
- ret = -EIO;
- if (!access_ok(VERIFY_READ, u, sizeof(*u)))
- break;
- /* no checking to be bug-to-bug compatible with i386 */
- __copy_from_user(&child->thread.i387.fxsave, u, sizeof(*u));
- set_stopped_child_used_math(child);
- child->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
- ret = 0;
- break;
+
+/*
+ * Perform get_thread_area on behalf of the traced child.
+ */
+static int
+ia32_tls_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ struct user_desc info, *ip;
+ const struct n_desc_struct *desc;
+ const struct n_desc_struct *tls;
+
+/*
+ * Get the current Thread-Local Storage area:
+ */
+
+#define GET_BASE(desc) ( \
+ (((desc)->a >> 16) & 0x0000ffff) | \
+ (((desc)->b << 16) & 0x00ff0000) | \
+ ( (desc)->b & 0xff000000) )
+
+#define GET_LIMIT(desc) ( \
+ ((desc)->a & 0x0ffff) | \
+ ((desc)->b & 0xf0000) )
+
+#define GET_32BIT(desc) (((desc)->b >> 22) & 1)
+#define GET_CONTENTS(desc) (((desc)->b >> 10) & 3)
+#define GET_WRITABLE(desc) (((desc)->b >> 9) & 1)
+#define GET_LIMIT_PAGES(desc) (((desc)->b >> 23) & 1)
+#define GET_PRESENT(desc) (((desc)->b >> 15) & 1)
+#define GET_USEABLE(desc) (((desc)->b >> 20) & 1)
+
+ tls = (struct n_desc_struct *) target->thread.tls_array;
+ desc = &tls[pos];
+ ip = kbuf ?: &info;
+ memset(ip, 0, sizeof *ip);
+ for (; count > 0; count -= sizeof(struct user_desc), ++desc) {
+ ip->entry_number = desc - tls + GDT_ENTRY_TLS_MIN;
+ ip->base_addr = GET_BASE(desc);
+ ip->limit = GET_LIMIT(desc);
+ ip->seg_32bit = GET_32BIT(desc);
+ ip->contents = GET_CONTENTS(desc);
+ ip->read_exec_only = !GET_WRITABLE(desc);
+ ip->limit_in_pages = GET_LIMIT_PAGES(desc);
+ ip->seg_not_present = !GET_PRESENT(desc);
+ ip->useable = GET_USEABLE(desc);
+
+ if (kbuf)
+ ++ip;
+ else {
+ if (__copy_to_user(ubuf, &info, sizeof(info)))
+ return -EFAULT;
+ ubuf += sizeof(info);
+ }
}
- case PTRACE_GETEVENTMSG:
- ret = put_user(child->ptrace_message,(unsigned int __user *)compat_ptr(data));
- break;
+ return 0;
+}
- default:
- BUG();
+/*
+ * Perform set_thread_area on behalf of the traced child.
+ */
+static int
+ia32_tls_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct user_desc info;
+ struct n_desc_struct *desc;
+ struct n_desc_struct newtls[GDT_ENTRY_TLS_ENTRIES];
+ unsigned int i;
+ int cpu;
+
+ pos /= sizeof(struct user_desc);
+ count /= sizeof(struct user_desc);
+
+ desc = &newtls[pos];
+ for (i = 0; i < count; ++i, ++desc) {
+ const struct user_desc *ip;
+ if (kbuf) {
+ ip = kbuf;
+ kbuf += sizeof(struct user_desc);
+ }
+ else {
+ ip = &info;
+ if (__copy_from_user(&info, ubuf, sizeof(info)))
+ return -EFAULT;
+ ubuf += sizeof(struct user_desc);
+ }
+
+ if (LDT_empty(ip)) {
+ desc->a = 0;
+ desc->b = 0;
+ } else {
+ desc->a = LDT_entry_a(ip);
+ desc->b = LDT_entry_b(ip);
+ }
}
- out:
- put_task_struct(child);
- return ret;
+ /*
+ * We must not get preempted while modifying the TLS.
+ */
+ cpu = get_cpu();
+ memcpy(&target->thread.tls_array[pos], newtls,
+ count * sizeof(newtls[0]));
+ if (target == current)
+ load_TLS(&target->thread, cpu);
+ put_cpu();
+
+ return 0;
+}
+
+/*
+ * Determine how many TLS slots are in use.
+ */
+static int
+ia32_tls_active(struct task_struct *target, const struct utrace_regset *regset)
+{
+ int i;
+ for (i = GDT_ENTRY_TLS_ENTRIES; i > 0; --i) {
+ struct n_desc_struct *desc = (struct n_desc_struct *)
+ &target->thread.tls_array[i - 1];
+ if ((desc->a | desc->b) != 0)
+ break;
+ }
+ return i;
}
+
+/*
+ * This should match arch/i386/kernel/ptrace.c:native_regsets.
+ * XXX ioperm? vm86?
+ */
+static const struct utrace_regset ia32_regsets[] = {
+ {
+ .n = sizeof(struct user_regs_struct32)/4,
+ .size = 4, .align = 4,
+ .get = ia32_genregs_get, .set = ia32_genregs_set
+ },
+ {
+ .n = sizeof(struct user_i387_ia32_struct) / 4,
+ .size = 4, .align = 4,
+ .active = ia32_fpregs_active,
+ .get = ia32_fpregs_get, .set = ia32_fpregs_set
+ },
+ {
+ .n = sizeof(struct user32_fxsr_struct) / 4,
+ .size = 4, .align = 4,
+ .active = ia32_fpxregs_active,
+ .get = ia32_fpxregs_get, .set = ia32_fpxregs_set
+ },
+ {
+ .n = GDT_ENTRY_TLS_ENTRIES,
+ .bias = GDT_ENTRY_TLS_MIN,
+ .size = sizeof(struct user_desc),
+ .align = sizeof(struct user_desc),
+ .active = ia32_tls_active,
+ .get = ia32_tls_get, .set = ia32_tls_set
+ },
+ {
+ .n = 8, .size = 4, .align = 4,
+ .active = ia32_dbregs_active,
+ .get = ia32_dbregs_get, .set = ia32_dbregs_set
+ },
+};
+
+const struct utrace_regset_view utrace_ia32_view = {
+ .name = "i386", .e_machine = EM_386,
+ .regsets = ia32_regsets,
+ .n = sizeof ia32_regsets / sizeof ia32_regsets[0],
+};
+EXPORT_SYMBOL_GPL(utrace_ia32_view);
+
+
+#ifdef CONFIG_PTRACE
+/*
+ * This matches the arch/i386/kernel/ptrace.c definitions.
+ */
+
+static const struct ptrace_layout_segment ia32_uarea[] = {
+ {0, sizeof(struct user_regs_struct32), 0, 0},
+ {offsetof(struct user32, u_debugreg[0]),
+ offsetof(struct user32, u_debugreg[8]), 4, 0},
+ {0, 0, -1, 0}
+};
+
+fastcall int arch_compat_ptrace(compat_long_t *req, struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ compat_ulong_t addr, compat_ulong_t data,
+ compat_long_t *val)
+{
+ switch (*req) {
+ case PTRACE_PEEKUSR:
+ return ptrace_compat_peekusr(child, engine, ia32_uarea,
+ addr, data);
+ case PTRACE_POKEUSR:
+ return ptrace_compat_pokeusr(child, engine, ia32_uarea,
+ addr, data);
+ case PTRACE_GETREGS:
+ return ptrace_whole_regset(child, engine, data, 0, 0);
+ case PTRACE_SETREGS:
+ return ptrace_whole_regset(child, engine, data, 0, 1);
+ case PTRACE_GETFPREGS:
+ return ptrace_whole_regset(child, engine, data, 1, 0);
+ case PTRACE_SETFPREGS:
+ return ptrace_whole_regset(child, engine, data, 1, 1);
+ case PTRACE_GETFPXREGS:
+ return ptrace_whole_regset(child, engine, data, 2, 0);
+ case PTRACE_SETFPXREGS:
+ return ptrace_whole_regset(child, engine, data, 2, 1);
+ case PTRACE_GET_THREAD_AREA:
+ case PTRACE_SET_THREAD_AREA:
+ return ptrace_onereg_access(child, engine,
+ &utrace_ia32_view, 3,
+ addr,
+ (void __user *)(unsigned long)data,
+ *req == PTRACE_SET_THREAD_AREA);
+ }
+ return -ENOSYS;
+}
+#endif /* CONFIG_PTRACE */
--- linux-2.6/arch/x86_64/ia32/ia32_signal.c.utrace-ptrace-compat
+++ linux-2.6/arch/x86_64/ia32/ia32_signal.c
@@ -497,11 +497,7 @@ int ia32_setup_frame(int sig, struct k_s
regs->cs = __USER32_CS;
regs->ss = __USER32_DS;
-
set_fs(USER_DS);
- regs->eflags &= ~TF_MASK;
- if (test_thread_flag(TIF_SINGLESTEP))
- ptrace_notify(SIGTRAP);
#if DEBUG_SIG
printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
@@ -593,11 +589,7 @@ int ia32_setup_rt_frame(int sig, struct
regs->cs = __USER32_CS;
regs->ss = __USER32_DS;
-
set_fs(USER_DS);
- regs->eflags &= ~TF_MASK;
- if (test_thread_flag(TIF_SINGLESTEP))
- ptrace_notify(SIGTRAP);
#if DEBUG_SIG
printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
--- linux-2.6/arch/x86_64/ia32/sys_ia32.c.utrace-ptrace-compat
+++ linux-2.6/arch/x86_64/ia32/sys_ia32.c
@@ -855,11 +855,6 @@ asmlinkage long sys32_execve(char __user
if (IS_ERR(filename))
return error;
error = compat_do_execve(filename, argv, envp, regs);
- if (error == 0) {
- task_lock(current);
- current->ptrace &= ~PT_DTRACE;
- task_unlock(current);
- }
putname(filename);
return error;
}
--- linux-2.6/arch/x86_64/ia32/ia32_aout.c.utrace-ptrace-compat
+++ linux-2.6/arch/x86_64/ia32/ia32_aout.c
@@ -421,12 +421,6 @@ beyond_if:
(regs)->cs = __USER32_CS;
(regs)->ss = __USER32_DS;
set_fs(USER_DS);
- if (unlikely(current->ptrace & PT_PTRACED)) {
- if (current->ptrace & PT_TRACE_EXEC)
- ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP);
- else
- send_sig(SIGTRAP, current, 0);
- }
return 0;
}
--- linux-2.6/arch/x86_64/ia32/fpu32.c.utrace-ptrace-compat
+++ linux-2.6/arch/x86_64/ia32/fpu32.c
@@ -9,6 +9,7 @@
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <asm/i387.h>
+#include <asm/user32.h>
static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
{
@@ -24,7 +25,8 @@ static inline unsigned short twd_i387_to
return tmp;
}
-static inline unsigned long twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
+static inline unsigned long
+twd_fxsr_to_i387(const struct i387_fxsave_struct *fxsave)
{
struct _fpxreg *st = NULL;
unsigned long tos = (fxsave->swd >> 11) & 7;
@@ -71,16 +73,11 @@ static inline unsigned long twd_fxsr_to_
}
-static inline int convert_fxsr_from_user(struct i387_fxsave_struct *fxsave,
- struct _fpstate_ia32 __user *buf)
+static inline void
+convert_fxsr_env_from_i387(struct i387_fxsave_struct *fxsave, const u32 env[7])
{
- struct _fpxreg *to;
- struct _fpreg __user *from;
- int i;
u32 v;
- int err = 0;
-
-#define G(num,val) err |= __get_user(val, num + (u32 __user *)buf)
+#define G(num,val) val = env[num]
G(0, fxsave->cwd);
G(1, fxsave->swd);
G(2, fxsave->twd);
@@ -91,9 +88,21 @@ static inline int convert_fxsr_from_user
G(5, fxsave->rdp);
/* 6: ds ignored */
#undef G
- if (err)
+}
+
+static inline int convert_fxsr_from_user(struct i387_fxsave_struct *fxsave,
+ struct _fpstate_ia32 __user *buf)
+{
+ u32 env[7];
+ struct _fpxreg *to;
+ struct _fpreg __user *from;
+ int i;
+
+ if (__copy_from_user(env, buf, sizeof(env)))
return -1;
+ convert_fxsr_env_from_i387(fxsave, env);
+
to = (struct _fpxreg *)&fxsave->st_space[0];
from = &buf->_st[0];
for (i = 0 ; i < 8 ; i++, to++, from++) {
@@ -104,16 +113,11 @@ static inline int convert_fxsr_from_user
}
-static inline int convert_fxsr_to_user(struct _fpstate_ia32 __user *buf,
- struct i387_fxsave_struct *fxsave,
- struct pt_regs *regs,
- struct task_struct *tsk)
+static inline void
+convert_fxsr_env_to_i387(struct task_struct *tsk, struct pt_regs *regs,
+ u32 env[7], const struct i387_fxsave_struct *fxsave)
{
- struct _fpreg __user *to;
- struct _fpxreg *from;
- int i;
u16 cs,ds;
- int err = 0;
if (tsk == current) {
/* should be actually ds/cs at fpu exception time,
@@ -125,7 +129,7 @@ static inline int convert_fxsr_to_user(s
cs = regs->cs;
}
-#define P(num,val) err |= __put_user(val, num + (u32 __user *)buf)
+#define P(num,val) env[num] = val
P(0, (u32)fxsave->cwd | 0xffff0000);
P(1, (u32)fxsave->swd | 0xffff0000);
P(2, twd_fxsr_to_i387(fxsave));
@@ -134,8 +138,21 @@ static inline int convert_fxsr_to_user(s
P(5, fxsave->rdp);
P(6, 0xffff0000 | ds);
#undef P
+}
+
+
+static inline int convert_fxsr_to_user(struct _fpstate_ia32 __user *buf,
+ struct i387_fxsave_struct *fxsave,
+ struct pt_regs *regs,
+ struct task_struct *tsk)
+{
+ struct _fpreg __user *to;
+ struct _fpxreg *from;
+ int i;
+ u32 env[7];
- if (err)
+ convert_fxsr_env_to_i387(tsk, regs, env, fxsave);
+ if (__copy_to_user(buf, env, sizeof(env)))
return -1;
to = &buf->_st[0];
@@ -181,3 +198,38 @@ int save_i387_ia32(struct task_struct *t
sizeof(struct i387_fxsave_struct));
return err ? -1 : 1;
}
+
+int get_fpregs32(struct user_i387_ia32_struct *buf, struct task_struct *tsk)
+{
+ struct pt_regs *regs = ((struct pt_regs *)tsk->thread.rsp0) - 1;
+ struct _fpreg *to;
+ const struct _fpxreg *from;
+ unsigned int i;
+
+ convert_fxsr_env_to_i387(tsk, regs,
+ (u32 *) buf, &tsk->thread.i387.fxsave);
+
+ to = (struct _fpreg *) buf->st_space;
+ from = (const struct _fpxreg *) &tsk->thread.i387.fxsave.st_space[0];
+ for (i = 0; i < 8; i++, to++, from++)
+ *to = *(const struct _fpreg *) from;
+
+ return 0;
+}
+
+int
+set_fpregs32(struct task_struct *tsk, const struct user_i387_ia32_struct *buf)
+{
+ struct _fpxreg *to;
+ const struct _fpreg *from;
+ unsigned int i;
+
+ convert_fxsr_env_from_i387(&tsk->thread.i387.fxsave, (u32 *) buf);
+
+ to = (struct _fpxreg *) &tsk->thread.i387.fxsave.st_space[0];
+ from = (const struct _fpreg *) buf->st_space;
+ for (i = 0; i < 8; i++, to++, from++)
+ *(struct _fpreg *) to = *from;
+
+ return 0;
+}
--- linux-2.6/arch/x86_64/ia32/ia32entry.S.utrace-ptrace-compat
+++ linux-2.6/arch/x86_64/ia32/ia32entry.S
@@ -417,7 +417,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
--- linux-2.6/arch/x86_64/mm/fault.c.utrace-ptrace-compat
+++ linux-2.6/arch/x86_64/mm/fault.c
@@ -11,7 +11,7 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/smp.h>
@@ -252,7 +252,7 @@ int unhandled_signal(struct task_struct
{
if (tsk->pid == 1)
return 1;
- if (tsk->ptrace & PT_PTRACED)
+ if (tracehook_consider_fatal_signal(tsk, sig))
return 0;
return (tsk->sighand->action[sig-1].sa.sa_handler == SIG_IGN) ||
(tsk->sighand->action[sig-1].sa.sa_handler == SIG_DFL);
--- linux-2.6/arch/frv/kernel/ptrace.c.utrace-ptrace-compat
+++ linux-2.6/arch/frv/kernel/ptrace.c
@@ -700,24 +700,11 @@ asmlinkage void do_syscall_trace(int lea
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return;
- if (!(current->ptrace & PT_PTRACED))
- return;
-
/* we need to indicate entry or exit to strace */
if (leaving)
__frame->__status |= REG__STATUS_SYSC_EXIT;
else
__frame->__status |= REG__STATUS_SYSC_ENTRY;
- ptrace_notify(SIGTRAP);
-
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
- }
+ tracehook_report_syscall(regs, leaving);
}
Index: linux-2.6.18.noarch/kernel/utrace.c
===================================================================
--- linux-2.6.18.noarch.orig/kernel/utrace.c
+++ linux-2.6.18.noarch/kernel/utrace.c
@@ -155,6 +155,16 @@ remove_engine(struct utrace_attached_eng
call_rcu(&engine->rhead, utrace_engine_free);
}
+/*
+ * This is pointed to by the utrace struct, but it's really a private
+ * structure between utrace_get_signal and utrace_inject_signal.
+ */
+struct utrace_signal
+{
+ siginfo_t *const info;
+ struct k_sigaction *return_ka;
+ int signr;
+};
/*
* Called with utrace locked, after remove_engine may have run.
@@ -172,6 +182,11 @@ check_dead_utrace(struct task_struct *ts
return utrace;
}
+ if (utrace->u.live.signal && utrace->u.live.signal->signr != 0) {
+ utrace_unlock(utrace);
+ return utrace;
+ }
+
utrace_unlock(utrace);
rcu_utrace_free(utrace);
return NULL;
@@ -1083,19 +1098,6 @@ utrace_report_syscall(struct pt_regs *re
check_quiescent(tsk, action);
}
-
-/*
- * This is pointed to by the utrace struct, but it's really a private
- * structure between utrace_get_signal and utrace_inject_signal.
- */
-struct utrace_signal
-{
- siginfo_t *const info;
- struct k_sigaction *return_ka;
- int signr;
-};
-
-
// XXX copied from signal.c
#ifdef SIGEMT
#define M_SIGEMT M(SIGEMT)
@@ -1205,7 +1207,7 @@ utrace_get_signal(struct task_struct *ts
if (tsk->utrace_flags & UTRACE_ACTION_QUIESCE) {
spin_unlock_irq(&tsk->sighand->siglock);
utrace_quiescent(tsk);
- if (utrace->u.live.signal != &signal || signal.signr == 0)
+ if (signal.signr == 0)
/*
* This return value says to reacquire the siglock
* and check again. This will check for a pending
@@ -1324,6 +1326,9 @@ utrace_get_signal(struct task_struct *ts
recalc_sigpending_tsk(tsk);
}
+ if (tsk->utrace != utrace)
+ rcu_utrace_free(utrace);
+
/*
* We express the chosen action to the signals code in terms
* of a representative signal whose default action does it.
diff --git a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c
index ebc9976..0000000 100644
--- a/arch/i386/kernel/signal.c
+++ b/arch/i386/kernel/signal.c
@@ -533,9 +533,7 @@ handle_signal(unsigned long sig, siginfo
sigaddset(¤t->blocked,sig);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- }
- if (ret) {
/*
* Clear TF when entering the signal handler, but
* notify any tracer that was single-stepping it.
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index a65dc89..0000000 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -1217,10 +1217,9 @@ no_signal:
its frame, and we can clear the TIF_RESTORE_SIGMASK flag */
if (test_thread_flag(TIF_RESTORE_SIGMASK))
clear_thread_flag(TIF_RESTORE_SIGMASK);
- }
- if (ret)
tracehook_report_handle_signal(signr, &ka, oldset, regs);
+ }
return ret;
}
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index d7b86c0..0000000 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -460,10 +460,9 @@ static int handle_signal(unsigned long s
sigaddset(¤t->blocked,sig);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- }
- if (ret)
tracehook_report_handle_signal(sig, ka, oldset, regs);
+ }
return ret;
}
diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c
index 456cdf6..0000000 100644
--- a/arch/s390/kernel/compat_signal.c
+++ b/arch/s390/kernel/compat_signal.c
@@ -580,10 +580,9 @@ handle_signal32(unsigned long sig, struc
sigaddset(¤t->blocked,sig);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- }
- if (ret == 0)
tracehook_report_handle_signal(sig, ka, oldset, regs);
+ }
return ret;
}
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c
index 7fa7492..0000000 100644
--- a/arch/s390/kernel/signal.c
+++ b/arch/s390/kernel/signal.c
@@ -404,10 +404,9 @@ handle_signal(unsigned long sig, struct
sigaddset(¤t->blocked,sig);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- }
- if (ret == 0)
tracehook_report_handle_signal(sig, ka, oldset, regs);
+ }
return ret;
}
diff --git a/arch/x86_64/kernel/signal.c b/arch/x86_64/kernel/signal.c
index 683c4f0..0000000 100644
--- a/arch/x86_64/kernel/signal.c
+++ b/arch/x86_64/kernel/signal.c
@@ -408,9 +408,7 @@ handle_signal(unsigned long sig, siginfo
sigaddset(¤t->blocked,sig);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- }
- if (ret) {
/*
* Clear TF when entering the signal handler, but
* notify any tracer that was single-stepping it.