Signed-off-by: Andrew Morton --- 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/process.c | 3 arch/i386/kernel/ptrace.c | 103 +----- arch/i386/kernel/signal.c | 37 -- arch/i386/kernel/vm86.c | 7 arch/i386/math-emu/fpu_entry.c | 6 arch/ia64/kernel/asm-offsets.c | 2 arch/ia64/kernel/fsys.S | 16 - arch/ia64/kernel/mca.c | 2 arch/mips/kernel/ptrace.c | 21 - arch/mips/kernel/sysirix.c | 2 arch/powerpc/kernel/asm-offsets.c | 2 arch/powerpc/kernel/process.c | 5 arch/powerpc/kernel/ptrace-common.h | 16 - arch/powerpc/kernel/ptrace.c | 74 ---- arch/powerpc/kernel/ptrace32.c | 13 arch/powerpc/kernel/signal_32.c | 3 arch/powerpc/kernel/signal_64.c | 3 arch/powerpc/kernel/sys_ppc32.c | 5 arch/ppc/kernel/asm-offsets.c | 2 arch/s390/kernel/compat_linux.c | 3 arch/s390/kernel/process.c | 3 arch/sparc64/kernel/binfmt_aout32.c | 2 arch/sparc64/kernel/process.c | 3 arch/sparc64/kernel/sys_sparc32.c | 3 arch/x86_64/ia32/ia32_aout.c | 6 arch/x86_64/ia32/ia32_signal.c | 7 arch/x86_64/ia32/ptrace32.c | 2 arch/x86_64/ia32/sys_ia32.c | 5 arch/x86_64/kernel/process.c | 5 arch/x86_64/kernel/ptrace.c | 57 +-- arch/x86_64/kernel/signal.c | 28 - 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 | 12 fs/proc/base.c | 17 - include/asm-i386/signal.h | 4 include/asm-i386/thread_info.h | 7 include/asm-i386/tracehook.h | 50 +++ include/asm-powerpc/tracehook.h | 74 ++++ include/asm-x86_64/thread_info.h | 3 include/asm-x86_64/tracehook.h | 49 +++ include/linux/init_task.h | 3 include/linux/ptrace.h | 18 - include/linux/sched.h | 16 - include/linux/tracehook.h | 397 ++++++++++++++++++++++++++ kernel/exit.c | 229 +++----------- kernel/fork.c | 59 --- kernel/ptrace.c | 259 ---------------- kernel/signal.c | 211 ++----------- kernel/sys.c | 2 kernel/timer.c | 6 kernel/tsacct.c | 2 security/selinux/hooks.c | 54 ++- security/selinux/include/objsec.h | 1 67 files changed, 891 insertions(+), 1175 deletions(-) diff -puN arch/alpha/kernel/asm-offsets.c~utrace-utrace-tracehook arch/alpha/kernel/asm-offsets.c --- a/arch/alpha/kernel/asm-offsets.c~utrace-utrace-tracehook +++ a/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(); diff -puN arch/alpha/kernel/entry.S~utrace-utrace-tracehook arch/alpha/kernel/entry.S --- a/arch/alpha/kernel/entry.S~utrace-utrace-tracehook +++ a/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 diff -puN arch/arm/kernel/ptrace.c~utrace-utrace-tracehook arch/arm/kernel/ptrace.c --- a/arch/arm/kernel/ptrace.c~utrace-utrace-tracehook +++ a/arch/arm/kernel/ptrace.c @@ -805,34 +805,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; } diff -puN arch/arm26/kernel/ptrace.c~utrace-utrace-tracehook arch/arm26/kernel/ptrace.c --- a/arch/arm26/kernel/ptrace.c~utrace-utrace-tracehook +++ a/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; } diff -puN arch/frv/kernel/ptrace.c~utrace-utrace-tracehook arch/frv/kernel/ptrace.c --- a/arch/frv/kernel/ptrace.c~utrace-utrace-tracehook +++ a/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); } diff -puN arch/i386/kernel/entry.S~utrace-utrace-tracehook arch/i386/kernel/entry.S --- a/arch/i386/kernel/entry.S~utrace-utrace-tracehook +++ a/arch/i386/kernel/entry.S @@ -336,7 +336,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 @@ -377,7 +377,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 @@ -518,9 +518,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 PT_ORIG_EAX(%esp), %eax cmpl $(nr_syscalls), %eax jnae syscall_call diff -puN arch/i386/kernel/process.c~utrace-utrace-tracehook arch/i386/kernel/process.c --- a/arch/i386/kernel/process.c~utrace-utrace-tracehook +++ a/arch/i386/kernel/process.c @@ -769,9 +769,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); } diff -puN arch/i386/kernel/ptrace.c~utrace-utrace-tracehook arch/i386/kernel/ptrace.c --- a/arch/i386/kernel/ptrace.c~utrace-utrace-tracehook +++ a/arch/i386/kernel/ptrace.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -215,7 +216,7 @@ static inline int is_setting_trap_flag(s 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); @@ -243,19 +244,18 @@ static void set_singlestep(struct task_s if (is_setting_trap_flag(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; } } @@ -266,9 +266,7 @@ static void clear_singlestep(struct task */ void ptrace_disable(struct task_struct *child) { - clear_singlestep(child); - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); + tracehook_disable_single_step(child); } /* @@ -480,18 +478,18 @@ long arch_ptrace(struct task_struct *chi if (!valid_signal(data)) break; if (request == PTRACE_SYSEMU) { - set_tsk_thread_flag(child, TIF_SYSCALL_EMU); + //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); + //clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); } else { - clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); + //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); + tracehook_disable_single_step(child); wake_up_process(child); ret = 0; break; @@ -507,7 +505,7 @@ long arch_ptrace(struct task_struct *chi break; child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ - clear_singlestep(child); + tracehook_disable_single_step(child); wake_up_process(child); break; @@ -517,13 +515,8 @@ long arch_ptrace(struct task_struct *chi 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); + tracehook_enable_single_step(child); child->exit_code = data; /* give it a chance to run. */ wake_up_process(child); @@ -651,78 +644,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; } diff -puN arch/i386/kernel/signal.c~utrace-utrace-tracehook arch/i386/kernel/signal.c --- a/arch/i386/kernel/signal.c~utrace-utrace-tracehook +++ a/arch/i386/kernel/signal.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -387,16 +387,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); @@ -481,16 +471,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); @@ -535,14 +515,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,15 @@ handle_signal(unsigned long sig, siginfo sigaddset(¤t->blocked,sig); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); + + /* + * 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; diff -puN arch/i386/kernel/vm86.c~utrace-utrace-tracehook arch/i386/kernel/vm86.c --- a/arch/i386/kernel/vm86.c~utrace-utrace-tracehook +++ a/arch/i386/kernel/vm86.c @@ -557,13 +557,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; diff -puN arch/i386/math-emu/fpu_entry.c~utrace-utrace-tracehook arch/i386/math-emu/fpu_entry.c --- a/arch/i386/math-emu/fpu_entry.c~utrace-utrace-tracehook +++ a/arch/i386/math-emu/fpu_entry.c @@ -25,7 +25,6 @@ +---------------------------------------------------------------------------*/ #include -#include #include #include @@ -211,9 +210,8 @@ asmlinkage void math_emulate(long arg) if ( code_limit < code_base ) code_limit = 0xffffffff; } - FPU_lookahead = 1; - if (current->ptrace & PT_PTRACED) - FPU_lookahead = 0; + /* Don't run ahead if single-stepping. */ + FPU_lookahead = (FPU_EFLAGS & X86_EFLAGS_TF) == 0; if ( !valid_prefix(&byte1, (u_char __user **)&FPU_EIP, &addr_modes.override) ) diff -puN arch/ia64/kernel/asm-offsets.c~utrace-utrace-tracehook arch/ia64/kernel/asm-offsets.c --- a/arch/ia64/kernel/asm-offsets.c~utrace-utrace-tracehook +++ a/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)); diff -puN arch/ia64/kernel/fsys.S~utrace-utrace-tracehook arch/ia64/kernel/fsys.S --- a/arch/ia64/kernel/fsys.S~utrace-utrace-tracehook +++ a/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 ;; diff -puN arch/ia64/kernel/mca.c~utrace-utrace-tracehook arch/ia64/kernel/mca.c --- a/arch/ia64/kernel/mca.c~utrace-utrace-tracehook +++ a/arch/ia64/kernel/mca.c @@ -1698,7 +1698,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); diff -puN arch/mips/kernel/ptrace.c~utrace-utrace-tracehook arch/mips/kernel/ptrace.c --- a/arch/mips/kernel/ptrace.c~utrace-utrace-tracehook +++ a/arch/mips/kernel/ptrace.c @@ -487,26 +487,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], diff -puN arch/mips/kernel/sysirix.c~utrace-utrace-tracehook arch/mips/kernel/sysirix.c --- a/arch/mips/kernel/sysirix.c~utrace-utrace-tracehook +++ a/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; } diff -puN arch/powerpc/kernel/asm-offsets.c~utrace-utrace-tracehook arch/powerpc/kernel/asm-offsets.c --- a/arch/powerpc/kernel/asm-offsets.c~utrace-utrace-tracehook +++ a/arch/powerpc/kernel/asm-offsets.c @@ -59,7 +59,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)); @@ -80,7 +79,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])); diff -puN arch/powerpc/kernel/process.c~utrace-utrace-tracehook arch/powerpc/kernel/process.c --- a/arch/powerpc/kernel/process.c~utrace-utrace-tracehook +++ a/arch/powerpc/kernel/process.c @@ -808,11 +808,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; diff -puN arch/powerpc/kernel/ptrace-common.h~utrace-utrace-tracehook arch/powerpc/kernel/ptrace-common.h --- a/arch/powerpc/kernel/ptrace-common.h~utrace-utrace-tracehook +++ a/arch/powerpc/kernel/ptrace-common.h @@ -54,22 +54,6 @@ static inline int put_reg(struct task_st 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. diff -puN arch/powerpc/kernel/ptrace.c~utrace-utrace-tracehook arch/powerpc/kernel/ptrace.c --- a/arch/powerpc/kernel/ptrace.c~utrace-utrace-tracehook +++ a/arch/powerpc/kernel/ptrace.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ #include #include #include +#include #ifdef CONFIG_PPC64 #include "ptrace-common.h" @@ -205,36 +207,6 @@ static inline int set_evrregs(struct tas return 0; } #endif /* CONFIG_SPE */ - -static inline void -set_single_step(struct task_struct *task) -{ - struct pt_regs *regs = task->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 - } -} - -static inline void -clear_single_step(struct task_struct *task) -{ - struct pt_regs *regs = task->thread.regs; - - if (regs != NULL) { -#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) - task->thread.dbcr0 = 0; - regs->msr &= ~MSR_DE; -#else - regs->msr &= ~MSR_SE; -#endif - } -} #endif /* CONFIG_PPC32 */ /* @@ -245,7 +217,7 @@ clear_single_step(struct task_struct *ta void ptrace_disable(struct task_struct *child) { /* make sure the single step bit is not set. */ - clear_single_step(child); + tracehook_disable_single_step(child); } long arch_ptrace(struct task_struct *child, long request, long addr, long data) @@ -348,7 +320,7 @@ long arch_ptrace(struct task_struct *chi 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); ret = 0; break; @@ -365,7 +337,7 @@ long arch_ptrace(struct task_struct *chi break; 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); break; } @@ -375,7 +347,7 @@ long arch_ptrace(struct task_struct *chi if (!valid_signal(data)) break; clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - set_single_step(child); + tracehook_enable_single_step(child); child->exit_code = data; /* give it a chance to run. */ wake_up_process(child); @@ -506,31 +478,12 @@ long arch_ptrace(struct task_struct *chi return ret; } -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) { secure_computing(regs->gpr[0]); - 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)) { #ifdef CONFIG_PPC64 @@ -556,8 +509,11 @@ void do_syscall_trace_leave(struct pt_re audit_syscall_exit((regs->ccr&0x10000000)?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); + } } diff -puN arch/powerpc/kernel/ptrace32.c~utrace-utrace-tracehook arch/powerpc/kernel/ptrace32.c --- a/arch/powerpc/kernel/ptrace32.c~utrace-utrace-tracehook +++ a/arch/powerpc/kernel/ptrace32.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include "ptrace-common.h" @@ -46,6 +48,9 @@ long compat_sys_ptrace(int request, int struct task_struct *child; int ret; + if (request == PTRACE_TRACEME) + return -ENOSYS; + lock_kernel(); if (request == PTRACE_TRACEME) { ret = ptrace_traceme(); @@ -290,7 +295,7 @@ long compat_sys_ptrace(int request, int 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); ret = 0; break; @@ -307,7 +312,7 @@ long compat_sys_ptrace(int request, int break; 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); break; } @@ -317,7 +322,7 @@ long compat_sys_ptrace(int request, int if (!valid_signal(data)) break; clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - set_single_step(child); + tracehook_enable_single_step(child); child->exit_code = data; /* give it a chance to run. */ wake_up_process(child); @@ -406,6 +411,7 @@ long compat_sys_ptrace(int request, int break; } +#if 0 /* XXX */ case PTRACE_GETEVENTMSG: ret = put_user(child->ptrace_message, (unsigned int __user *) data); break; @@ -423,6 +429,7 @@ long compat_sys_ptrace(int request, int ret = set_vrregs(child, (unsigned long __user *)data); break; #endif +#endif default: ret = ptrace_request(child, request, addr, data); diff -puN arch/powerpc/kernel/signal_32.c~utrace-utrace-tracehook arch/powerpc/kernel/signal_32.c --- a/arch/powerpc/kernel/signal_32.c~utrace-utrace-tracehook +++ a/arch/powerpc/kernel/signal_32.c @@ -25,6 +25,7 @@ #include #include #include +#include #ifdef CONFIG_PPC64 #include #include @@ -1226,6 +1227,8 @@ 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); + + tracehook_report_handle_signal(signr, &ka, oldset, regs); } return ret; diff -puN arch/powerpc/kernel/signal_64.c~utrace-utrace-tracehook arch/powerpc/kernel/signal_64.c --- a/arch/powerpc/kernel/signal_64.c~utrace-utrace-tracehook +++ a/arch/powerpc/kernel/signal_64.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -459,6 +460,8 @@ static int handle_signal(unsigned long s sigaddset(¤t->blocked,sig); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); + + tracehook_report_handle_signal(sig, ka, oldset, regs); } return ret; diff -puN arch/powerpc/kernel/sys_ppc32.c~utrace-utrace-tracehook arch/powerpc/kernel/sys_ppc32.c --- a/arch/powerpc/kernel/sys_ppc32.c~utrace-utrace-tracehook +++ a/arch/powerpc/kernel/sys_ppc32.c @@ -372,11 +372,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: diff -puN arch/ppc/kernel/asm-offsets.c~utrace-utrace-tracehook arch/ppc/kernel/asm-offsets.c --- a/arch/ppc/kernel/asm-offsets.c~utrace-utrace-tracehook +++ a/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])); diff -puN arch/s390/kernel/compat_linux.c~utrace-utrace-tracehook arch/s390/kernel/compat_linux.c --- a/arch/s390/kernel/compat_linux.c~utrace-utrace-tracehook +++ a/arch/s390/kernel/compat_linux.c @@ -513,9 +513,6 @@ asmlinkage long sys32_execve(void) result = rc; goto out_putname; } - task_lock(current); - current->ptrace &= ~PT_DTRACE; - task_unlock(current); current->thread.fp_regs.fpc=0; asm volatile("sfpc %0,0" : : "d" (0)); result = regs->gprs[2]; diff -puN arch/s390/kernel/process.c~utrace-utrace-tracehook arch/s390/kernel/process.c --- a/arch/s390/kernel/process.c~utrace-utrace-tracehook +++ a/arch/s390/kernel/process.c @@ -322,9 +322,6 @@ asmlinkage long sys_vfork(void) asmlinkage void execve_tail(void) { - 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)); diff -puN arch/sparc64/kernel/binfmt_aout32.c~utrace-utrace-tracehook arch/sparc64/kernel/binfmt_aout32.c --- a/arch/sparc64/kernel/binfmt_aout32.c~utrace-utrace-tracehook +++ a/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; } diff -puN arch/sparc64/kernel/process.c~utrace-utrace-tracehook arch/sparc64/kernel/process.c --- a/arch/sparc64/kernel/process.c~utrace-utrace-tracehook +++ a/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; diff -puN arch/sparc64/kernel/sys_sparc32.c~utrace-utrace-tracehook arch/sparc64/kernel/sys_sparc32.c --- a/arch/sparc64/kernel/sys_sparc32.c~utrace-utrace-tracehook +++ a/arch/sparc64/kernel/sys_sparc32.c @@ -682,9 +682,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; diff -puN arch/x86_64/ia32/ia32_aout.c~utrace-utrace-tracehook arch/x86_64/ia32/ia32_aout.c --- a/arch/x86_64/ia32/ia32_aout.c~utrace-utrace-tracehook +++ a/arch/x86_64/ia32/ia32_aout.c @@ -430,12 +430,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; } diff -puN arch/x86_64/ia32/ia32_signal.c~utrace-utrace-tracehook arch/x86_64/ia32/ia32_signal.c --- a/arch/x86_64/ia32/ia32_signal.c~utrace-utrace-tracehook +++ a/arch/x86_64/ia32/ia32_signal.c @@ -493,11 +493,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=%lx ra=%u\n", @@ -601,9 +597,6 @@ int ia32_setup_rt_frame(int sig, struct 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=%lx ra=%u\n", diff -puN arch/x86_64/ia32/ptrace32.c~utrace-utrace-tracehook arch/x86_64/ia32/ptrace32.c --- a/arch/x86_64/ia32/ptrace32.c~utrace-utrace-tracehook +++ a/arch/x86_64/ia32/ptrace32.c @@ -387,9 +387,11 @@ asmlinkage long sys32_ptrace(long reques break; } +#if 0 /* XXX */ case PTRACE_GETEVENTMSG: ret = put_user(child->ptrace_message,(unsigned int __user *)compat_ptr(data)); break; +#endif default: BUG(); diff -puN arch/x86_64/ia32/sys_ia32.c~utrace-utrace-tracehook arch/x86_64/ia32/sys_ia32.c --- a/arch/x86_64/ia32/sys_ia32.c~utrace-utrace-tracehook +++ a/arch/x86_64/ia32/sys_ia32.c @@ -800,11 +800,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; } diff -puN arch/x86_64/kernel/process.c~utrace-utrace-tracehook arch/x86_64/kernel/process.c --- a/arch/x86_64/kernel/process.c~utrace-utrace-tracehook +++ a/arch/x86_64/kernel/process.c @@ -683,11 +683,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; } diff -puN arch/x86_64/kernel/ptrace.c~utrace-utrace-tracehook arch/x86_64/kernel/ptrace.c --- a/arch/x86_64/kernel/ptrace.c~utrace-utrace-tracehook +++ a/arch/x86_64/kernel/ptrace.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -165,7 +166,7 @@ static int is_setting_trap_flag(struct t 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); @@ -193,19 +194,18 @@ static void set_singlestep(struct task_s if (is_setting_trap_flag(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; } } @@ -216,7 +216,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, @@ -445,7 +445,7 @@ long arch_ptrace(struct task_struct *chi clear_tsk_thread_flag(child, TIF_SINGLESTEP); child->exit_code = data; /* make sure the single step bit is not set. */ - clear_singlestep(child); + tracehook_disable_single_step(child); wake_up_process(child); ret = 0; break; @@ -492,7 +492,7 @@ long arch_ptrace(struct task_struct *chi clear_tsk_thread_flag(child, TIF_SINGLESTEP); child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ - clear_singlestep(child); + tracehook_disable_single_step(child); wake_up_process(child); break; @@ -501,7 +501,7 @@ long arch_ptrace(struct task_struct *chi if (!valid_signal(data)) break; clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE); - set_singlestep(child); + tracehook_enable_single_step(child); child->exit_code = data; /* give it a chance to run. */ wake_up_process(child); @@ -575,37 +575,13 @@ long arch_ptrace(struct task_struct *chi 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); -#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; - } -} - 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)) { @@ -627,8 +603,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); + } } diff -puN arch/x86_64/kernel/signal.c~utrace-utrace-tracehook arch/x86_64/kernel/signal.c --- a/arch/x86_64/kernel/signal.c~utrace-utrace-tracehook +++ a/arch/x86_64/kernel/signal.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -297,9 +297,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); @@ -351,16 +348,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)) { @@ -379,6 +372,15 @@ handle_signal(unsigned long sig, siginfo sigaddset(¤t->blocked,sig); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); + + /* + * 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; diff -puN arch/x86_64/kernel/traps.c~utrace-utrace-tracehook arch/x86_64/kernel/traps.c --- a/arch/x86_64/kernel/traps.c~utrace-utrace-tracehook +++ a/arch/x86_64/kernel/traps.c @@ -883,14 +883,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 */ diff -puN arch/x86_64/mm/fault.c~utrace-utrace-tracehook arch/x86_64/mm/fault.c --- a/arch/x86_64/mm/fault.c~utrace-utrace-tracehook +++ a/arch/x86_64/mm/fault.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -225,7 +225,7 @@ int unhandled_signal(struct task_struct { if (is_init(tsk)) 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); diff -puN drivers/connector/cn_proc.c~utrace-utrace-tracehook drivers/connector/cn_proc.c --- a/drivers/connector/cn_proc.c~utrace-utrace-tracehook +++ a/drivers/connector/cn_proc.c @@ -63,8 +63,8 @@ void proc_fork_connector(struct task_str ktime_get_ts(&ts); /* get high res monotonic timestamp */ put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns); 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; diff -puN fs/binfmt_aout.c~utrace-utrace-tracehook fs/binfmt_aout.c --- a/fs/binfmt_aout.c~utrace-utrace-tracehook +++ a/fs/binfmt_aout.c @@ -461,12 +461,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; } diff -puN fs/binfmt_elf.c~utrace-utrace-tracehook fs/binfmt_elf.c --- a/fs/binfmt_elf.c~utrace-utrace-tracehook +++ a/fs/binfmt_elf.c @@ -1024,12 +1024,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); diff -puN fs/binfmt_elf_fdpic.c~utrace-utrace-tracehook fs/binfmt_elf_fdpic.c --- a/fs/binfmt_elf_fdpic.c~utrace-utrace-tracehook +++ a/fs/binfmt_elf_fdpic.c @@ -426,13 +426,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: diff -puN fs/binfmt_flat.c~utrace-utrace-tracehook fs/binfmt_flat.c --- a/fs/binfmt_flat.c~utrace-utrace-tracehook +++ a/fs/binfmt_flat.c @@ -914,9 +914,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; } diff -puN fs/binfmt_som.c~utrace-utrace-tracehook fs/binfmt_som.c --- a/fs/binfmt_som.c~utrace-utrace-tracehook +++ a/fs/binfmt_som.c @@ -285,8 +285,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 */ diff -puN fs/exec.c~utrace-utrace-tracehook fs/exec.c --- a/fs/exec.c~utrace-utrace-tracehook +++ a/fs/exec.c @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include #include @@ -953,13 +953,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) @@ -1099,6 +1093,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); diff -puN fs/proc/array.c~utrace-utrace-tracehook fs/proc/array.c --- a/fs/proc/array.c~utrace-utrace-tracehook +++ a/fs/proc/array.c @@ -73,6 +73,7 @@ #include #include #include +#include #include #include @@ -158,11 +159,16 @@ 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; + buffer += sprintf(buffer, "State:\t%s\n" "SleepAVG:\t%lu%%\n" @@ -175,8 +181,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) ? rcu_dereference(p->real_parent)->tgid : 0, - pid_alive(p) && p->ptrace ? rcu_dereference(p->parent)->pid : 0, + pid_alive(p) ? rcu_dereference(p->parent)->tgid : 0, + tracer_pid, p->uid, p->euid, p->suid, p->fsuid, p->gid, p->egid, p->sgid, p->fsgid); @@ -383,7 +389,7 @@ static int do_task_stat(struct task_stru sid = signal_session(sig); pgid = process_group(task); - ppid = rcu_dereference(task->real_parent)->tgid; + ppid = rcu_dereference(task->parent)->tgid; unlock_task_sighand(task, &flags); } diff -puN fs/proc/base.c~utrace-utrace-tracehook fs/proc/base.c --- a/fs/proc/base.c~utrace-utrace-tracehook +++ a/fs/proc/base.c @@ -67,6 +67,7 @@ #include #include #include +#include #include #include #include @@ -189,13 +190,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; @@ -515,7 +509,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; @@ -541,7 +536,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; @@ -585,7 +581,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; diff -puN include/asm-i386/signal.h~utrace-utrace-tracehook include/asm-i386/signal.h --- a/include/asm-i386/signal.h~utrace-utrace-tracehook +++ a/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__ */ diff -puN include/asm-i386/thread_info.h~utrace-utrace-tracehook include/asm-i386/thread_info.h --- a/include/asm-i386/thread_info.h~utrace-utrace-tracehook +++ a/include/asm-i386/thread_info.h @@ -127,7 +127,6 @@ 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() */ @@ -135,6 +134,7 @@ static inline struct thread_info *curren #define TIF_DEBUG 17 /* uses debug registers */ #define TIF_IO_BITMAP 18 /* uses I/O bitmap */ #define TIF_FREEZE 19 /* is freezing for suspend */ +#define TIF_FORCED_TF 20 /* true if TF in eflags artificially */ #define _TIF_SYSCALL_TRACE (1< +#include + +/* + * 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; +} + + +#endif diff -puN /dev/null include/asm-powerpc/tracehook.h --- /dev/null +++ a/include/asm-powerpc/tracehook.h @@ -0,0 +1,74 @@ +/* + * Tracing hooks, PowerPC CPU support + * + * Copyright (C) 2006, 2007 Red Hat, Inc. All rights reserved. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * Red Hat Author: Roland McGrath. + */ + +#ifndef _ASM_TRACEHOOK_H +#define _ASM_TRACEHOOK_H 1 + +#include +#include + +/* + * 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; +} + + +#endif diff -puN include/asm-x86_64/thread_info.h~utrace-utrace-tracehook include/asm-x86_64/thread_info.h --- a/include/asm-x86_64/thread_info.h~utrace-utrace-tracehook +++ a/include/asm-x86_64/thread_info.h @@ -115,7 +115,7 @@ static inline struct thread_info *stack_ #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 */ -/* 16 free */ +#define TIF_FORCED_TF 16 /* true if TF in eflags artificially */ #define TIF_IA32 17 /* 32bit process */ #define TIF_FORK 18 /* ret_from_fork */ #define TIF_ABI_PENDING 19 @@ -133,6 +133,7 @@ static inline struct thread_info *stack_ #define _TIF_SYSCALL_AUDIT (1< +#include + +/* + * 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; +} + +#endif diff -puN include/linux/init_task.h~utrace-utrace-tracehook include/linux/init_task.h --- a/include/linux/init_task.h~utrace-utrace-tracehook +++ a/include/linux/init_task.h @@ -119,9 +119,6 @@ extern struct group_info init_groups; .time_slice = HZ, \ INIT_PREEMPT_RCU \ .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), \ diff -puN include/linux/ptrace.h~utrace-utrace-tracehook include/linux/ptrace.h --- a/include/linux/ptrace.h~utrace-utrace-tracehook +++ a/include/linux/ptrace.h @@ -67,7 +67,6 @@ #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 @@ -91,25 +90,8 @@ extern int ptrace_detach(struct task_str 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); -static inline void ptrace_link(struct task_struct *child, - struct task_struct *new_parent) -{ - if (unlikely(child->ptrace)) - __ptrace_link(child, new_parent); -} -static inline void ptrace_unlink(struct task_struct *child) -{ - if (unlikely(child->ptrace)) - __ptrace_unlink(child); -} - #ifndef force_successful_syscall_return /* diff -puN include/linux/sched.h~utrace-utrace-tracehook include/linux/sched.h --- a/include/linux/sched.h~utrace-utrace-tracehook +++ a/include/linux/sched.h @@ -829,7 +829,6 @@ struct task_struct { struct thread_info *thread_info; atomic_t usage; unsigned int flags; /* per process flags, defined below */ - unsigned int ptrace; int lock_depth; /* BKL lock depth */ @@ -866,12 +865,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; @@ -891,15 +884,13 @@ struct task_struct { unsigned long stack_canary; #endif /* - * 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 */ @@ -1035,8 +1026,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 @@ -1363,6 +1352,7 @@ extern int kill_pgrp(struct pid *pid, in extern int kill_pid(struct pid *pid, int sig, int priv); extern int kill_proc_info(int, struct siginfo *, pid_t); 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); diff -puN /dev/null include/linux/tracehook.h --- /dev/null +++ a/include/linux/tracehook.h @@ -0,0 +1,397 @@ +/* + * Tracing hooks + * + * Copyright (C) 2006, 2007 Red Hat, Inc. All rights reserved. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * Red Hat Author: Roland McGrath. + * + * 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 +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. + * If these are available, asm/tracehook.h does #define HAVE_ARCH_BLOCK_STEP. + * 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); + * + */ + + +/*** + *** + *** 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. + *** + ***/ + + +/* + * 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) +{ +} + +/* + * 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) +{ +} + +/* + * 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) +{ + 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) +{ + 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; + *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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + return 0; +// if (p->ptrace & PT_PTRACED) { +// if (p->ptrace & PT_PTRACE_CAP) +// unsafe |= LSM_UNSAFE_PTRACE_CAP; +// else +// unsafe |= LSM_UNSAFE_PTRACE; +// } +} + +/* + * 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) +{ + 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; + 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) +{ +} + +/* + * exec completed + */ +static inline void tracehook_report_exec(struct linux_binprm *bprm, + struct pt_regs *regs) +{ +} + +/* + * 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) +{ +} + +/* + * 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) +{ +} + +/* + * 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) +{ +} + +/* + * 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) +{ +} + +/* + * Called for system call entry or exit. + */ +static inline void tracehook_report_syscall(struct pt_regs *regs, int is_exit) +{ +} + +/* + * 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) +{ +} + + +#endif /* */ diff -puN kernel/exit.c~utrace-utrace-tracehook kernel/exit.c --- a/kernel/exit.c~utrace-utrace-tracehook +++ a/kernel/exit.c @@ -22,8 +22,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -143,10 +143,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); /* @@ -218,10 +218,10 @@ static int will_become_orphaned_pgrp(str do_each_pid_task(pgrp, PIDTYPE_PGID, p) { if (p == ignored_task || p->exit_state - || is_init(p->real_parent)) + || is_init(p->parent)) continue; - if (task_pgrp(p->real_parent) != pgrp && - task_session(p->real_parent) == task_session(p)) { + if (task_pgrp(p->parent) != pgrp && + task_session(p->parent) == task_session(p)) { ret = 0; break; } @@ -270,11 +270,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); - current->real_parent = child_reaper(current); add_parent(current); /* Set the exit signal to SIGCHLD so we signal init on exit */ @@ -590,45 +588,23 @@ 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) { if (p->pdeath_signal) /* We already hold the tasklist_lock here. */ 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); - - 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); - } - } + list_move_tail(&p->sibling, &p->parent->children); /* If this is a threaded reparent there is no need to * notify anyone anything has happened. */ - if (p->real_parent->group_leader == father->group_leader) + if (p->parent->group_leader == father->group_leader) return; /* We don't want people slaying init. */ @@ -638,7 +614,8 @@ reparent_thread(struct task_struct *p, s /* If we'd notified the old parent about this child's death, * also notify the new parent. */ - if (!traced && p->exit_state == EXIT_ZOMBIE && + if (!tracehook_inhibit_wait_zombie(p) && + p->exit_state == EXIT_ZOMBIE && p->exit_signal != -1 && thread_group_empty(p)) do_notify_parent(p, p->exit_signal); @@ -668,7 +645,7 @@ reparent_thread(struct task_struct *p, s * space. */ 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; @@ -681,48 +658,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); } } @@ -734,8 +673,9 @@ static void exit_notify(struct task_stru { int state; struct task_struct *t; - struct list_head ptrace_dead, *_p, *_n; struct pid *pgrp; + int noreap; + void *cookie; if (signal_pending(tsk) && !(tsk->signal->flags & SIGNAL_GROUP_EXIT) && !thread_group_empty(tsk)) { @@ -771,10 +711,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 @@ -786,7 +724,7 @@ static void exit_notify(struct task_stru * is about to become orphaned. */ - t = tsk->real_parent; + t = tsk->parent; pgrp = task_pgrp(tsk); if ((task_pgrp(t) != pgrp) && @@ -819,32 +757,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) @@ -872,10 +796,7 @@ fastcall NORET_TYPE void do_exit(long co } - 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 @@ -1045,10 +966,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; @@ -1119,7 +1039,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); @@ -1143,15 +1063,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; @@ -1233,28 +1147,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; } @@ -1273,7 +1167,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. @@ -1294,14 +1188,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); } @@ -1357,9 +1250,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) @@ -1427,22 +1318,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) { @@ -1474,26 +1349,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), @@ -1518,6 +1384,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); @@ -1534,6 +1404,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); @@ -1543,14 +1415,7 @@ check_continued: } } 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; - } + // XXX set flag if we have ptracees } if (options & __WNOTHREAD) break; @@ -1575,7 +1440,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 diff -puN kernel/fork.c~utrace-utrace-tracehook kernel/fork.c --- a/kernel/fork.c~utrace-utrace-tracehook +++ a/kernel/fork.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include #include @@ -921,8 +921,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; } @@ -1179,8 +1178,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); @@ -1207,10 +1204,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); @@ -1252,8 +1248,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; @@ -1343,22 +1338,6 @@ struct task_struct * __cpuinit 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. * @@ -1373,18 +1352,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); /* @@ -1399,30 +1372,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); diff -puN kernel/ptrace.c~utrace-utrace-tracehook kernel/ptrace.c --- a/kernel/ptrace.c~utrace-utrace-tracehook +++ a/kernel/ptrace.c @@ -22,100 +22,13 @@ #include #include -/* - * 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); - - if (!ret && !kill) { - wait_task_inactive(child); - } - - /* All systems go.. */ - return ret; + return -ENOSYS; } static int may_attach(struct task_struct *task) @@ -165,47 +78,12 @@ int ptrace_attach(struct task_struct *ta 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); + retval = -ENOSYS; bad: write_unlock_irq(&tasklist_lock); @@ -214,16 +92,6 @@ 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)) @@ -232,13 +100,10 @@ int ptrace_detach(struct task_struct *ch /* 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); + /* .. re-parent .. */ + child->exit_code = data; - return 0; + return -ENOSYS; } int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len) @@ -291,103 +156,10 @@ int ptrace_writedata(struct task_struct return copied; } -static int ptrace_setoptions(struct task_struct *child, long data) -{ - child->ptrace &= ~PT_TRACE_MASK; - - if (data & PTRACE_O_TRACESYSGOOD) - child->ptrace |= PT_TRACESYSGOOD; - - if (data & PTRACE_O_TRACEFORK) - child->ptrace |= PT_TRACE_FORK; - - if (data & PTRACE_O_TRACEVFORK) - child->ptrace |= PT_TRACE_VFORK; - - if (data & PTRACE_O_TRACECLONE) - child->ptrace |= PT_TRACE_CLONE; - - if (data & PTRACE_O_TRACEEXEC) - child->ptrace |= PT_TRACE_EXEC; - - if (data & PTRACE_O_TRACEVFORKDONE) - child->ptrace |= PT_TRACE_VFORK_DONE; - - if (data & PTRACE_O_TRACEEXIT) - child->ptrace |= PT_TRACE_EXIT; - - return (data & ~PTRACE_O_MASK) ? -EINVAL : 0; -} - -static int ptrace_getsiginfo(struct task_struct *child, siginfo_t __user * data) -{ - siginfo_t lastinfo; - int error = -ESRCH; - - 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; - } - spin_unlock_irq(&child->sighand->siglock); - } - read_unlock(&tasklist_lock); - if (!error) - return copy_siginfo_to_user(data, &lastinfo); - return error; -} - -static int ptrace_setsiginfo(struct task_struct *child, siginfo_t __user * data) -{ - siginfo_t newinfo; - int error = -ESRCH; - - if (copy_from_user(&newinfo, data, sizeof (siginfo_t))) - return -EFAULT; - - 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; - } - spin_unlock_irq(&child->sighand->siglock); - } - read_unlock(&tasklist_lock); - return error; -} - int ptrace_request(struct task_struct *child, long request, long addr, long data) { - int ret = -EIO; - - switch (request) { -#ifdef PTRACE_OLDSETOPTIONS - case PTRACE_OLDSETOPTIONS: -#endif - case PTRACE_SETOPTIONS: - ret = ptrace_setoptions(child, data); - break; - case PTRACE_GETEVENTMSG: - ret = put_user(child->ptrace_message, (unsigned long __user *) data); - break; - case PTRACE_GETSIGINFO: - ret = ptrace_getsiginfo(child, (siginfo_t __user *) data); - break; - case PTRACE_SETSIGINFO: - ret = ptrace_setsiginfo(child, (siginfo_t __user *) data); - break; - default: - break; - } - - return ret; + return -ENOSYS; } /** @@ -400,20 +172,11 @@ int ptrace_traceme(void) { int ret = -EPERM; - /* - * Are we already being traced? - */ - task_lock(current); - if (!(current->ptrace & PT_PTRACED)) { - ret = security_ptrace(current->parent, current); - /* - * Set the ptrace bit in the process ptrace flags. - */ - if (!ret) - current->ptrace |= PT_PTRACED; - } - task_unlock(current); - return ret; + ret = security_ptrace(current->parent, current); + if (ret) + return -EPERM; + + return -ENOSYS; } /** diff -puN kernel/signal.c~utrace-utrace-tracehook kernel/signal.c --- a/kernel/signal.c~utrace-utrace-tracehook +++ a/kernel/signal.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -164,12 +164,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. @@ -179,8 +173,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); } /* @@ -220,7 +218,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); @@ -622,8 +621,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. @@ -929,7 +926,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. */ @@ -1485,8 +1482,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; @@ -1511,7 +1507,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))) { /* @@ -1539,20 +1535,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; @@ -1577,6 +1566,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 && @@ -1589,110 +1587,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) { @@ -1701,7 +1595,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); @@ -1828,44 +1722,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) { @@ -1914,7 +1788,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; } @@ -1941,13 +1815,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); @@ -1959,7 +1833,6 @@ EXPORT_SYMBOL_GPL(dequeue_signal); EXPORT_SYMBOL(flush_signals); EXPORT_SYMBOL(force_sig); EXPORT_SYMBOL(kill_proc); -EXPORT_SYMBOL(ptrace_notify); EXPORT_SYMBOL(send_sig); EXPORT_SYMBOL(send_sig_info); EXPORT_SYMBOL(sigprocmask); diff -puN kernel/sys.c~utrace-utrace-tracehook kernel/sys.c --- a/kernel/sys.c~utrace-utrace-tracehook +++ a/kernel/sys.c @@ -1454,7 +1454,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 (task_session(p) != task_session(group_leader)) goto out; diff -puN kernel/timer.c~utrace-utrace-tracehook kernel/timer.c --- a/kernel/timer.c~utrace-utrace-tracehook +++ a/kernel/timer.c @@ -1329,9 +1329,9 @@ 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 + * value of ->parent under rcu_read_lock(), see * release_task()->call_rcu(delayed_put_task_struct). */ asmlinkage long sys_getppid(void) @@ -1339,7 +1339,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; diff -puN kernel/tsacct.c~utrace-utrace-tracehook kernel/tsacct.c --- a/kernel/tsacct.c~utrace-utrace-tracehook +++ a/kernel/tsacct.c @@ -58,7 +58,7 @@ void bacct_add_tsk(struct taskstats *sta stats->ac_pid = tsk->pid; rcu_read_lock(); stats->ac_ppid = pid_alive(tsk) ? - rcu_dereference(tsk->real_parent)->tgid : 0; + rcu_dereference(tsk->parent)->tgid : 0; rcu_read_unlock(); stats->ac_utime = cputime_to_msecs(tsk->utime) * USEC_PER_MSEC; stats->ac_stime = cputime_to_msecs(tsk->stime) * USEC_PER_MSEC; diff -puN security/selinux/hooks.c~utrace-utrace-tracehook security/selinux/hooks.c --- a/security/selinux/hooks.c~utrace-utrace-tracehook +++ a/security/selinux/hooks.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -163,7 +163,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; @@ -1370,19 +1370,13 @@ static inline u32 file_to_av(struct file 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, @@ -1852,12 +1846,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; @@ -2751,11 +2757,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; } @@ -4545,6 +4546,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; @@ -4633,18 +4635,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); } diff -puN security/selinux/include/objsec.h~utrace-utrace-tracehook security/selinux/include/objsec.h --- a/security/selinux/include/objsec.h~utrace-utrace-tracehook +++ a/security/selinux/include/objsec.h @@ -35,7 +35,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 { _