[PATCH 1] utrace: tracehook (die, ptrace, die) This patch rips out the old ptrace implementation. It leaves behind stubs so ptrace just fails with ENOSYS. This is not really a useful kernel tree to build, but it's a clean removal of the old code before adding the new. Some dead code is left behind especially in architecture ptrace.c files; this is left in to be reused under new interfaces by later patches, reducing the total disruption to the code. The "real_parent" member in task_struct is renamed "parent", and the old "parent" member is gone. That is to say, nothing interferes in the normal parent/child links any more. The fact that ptrace could cause these links to change was the source of numerous race conditions and similar bugs uncovered and worked around (often contortedly) in the core task management code under the old ptrace implementation. In each place in core code that referred to the old ptrace stuff, we now have the tracehook interface. These declarations are in , and use "tracehook_*" function names. This centralizes all the hooks that a thread tracing facility needs into core code, and the file documents the calling environment (locking conditions, etc.) and meaning of each hook. The definitions here are all stubs doing nothing, so there are no user debugging features in the kernel at all. They provide the placeholders for where a tracing interface can tie into the kernel. The architecture support for single-step (and block-step) from the old ptrace support is moved into tracehook_* function interfaces. Nothing yet calls these, but this provides the clean interface that user debugging support can use for the architecture-specific single-step control code. Signed-off-by: Roland McGrath --- fs/binfmt_flat.c | 3 fs/binfmt_som.c | 2 fs/proc/base.c | 17 + fs/proc/array.c | 10 + fs/binfmt_elf_fdpic.c | 7 - fs/exec.c | 11 - fs/binfmt_elf.c | 10 - fs/binfmt_aout.c | 6 - security/selinux/hooks.c | 54 +++-- security/selinux/include/objsec.h | 1 arch/s390/kernel/compat_linux.c | 3 arch/s390/kernel/process.c | 3 arch/frv/kernel/ptrace.c | 15 - arch/arm/kernel/ptrace.c | 17 - arch/mips/kernel/sysirix.c | 2 arch/mips/kernel/ptrace.c | 23 -- arch/powerpc/kernel/sys_ppc32.c | 5 arch/powerpc/kernel/ptrace32.c | 7 + arch/powerpc/kernel/ptrace.c | 49 +--- arch/powerpc/kernel/signal.c | 3 arch/powerpc/kernel/asm-offsets.c | 2 arch/powerpc/kernel/process.c | 5 arch/x86/math-emu/fpu_entry.c | 6 - arch/x86/ia32/sys_ia32.c | 5 arch/x86/ia32/ptrace32.c | 2 arch/x86/ia32/ia32_aout.c | 6 - arch/x86/ia32/ia32_signal.c | 7 - arch/x86/ia32/ia32entry.S | 4 arch/x86/kernel/vm86_32.c | 7 - arch/x86/kernel/process_64.c | 5 arch/x86/kernel/process_32.c | 3 arch/x86/kernel/entry_32.S | 7 - arch/x86/kernel/signal_64.c | 28 +- arch/x86/kernel/entry_64.S | 8 - arch/x86/kernel/ptrace_32.c | 103 ++------- arch/x86/kernel/traps_64.c | 8 - arch/x86/kernel/ptrace_64.c | 57 ++--- arch/x86/kernel/signal_32.c | 37 +-- arch/x86/mm/fault_64.c | 2 arch/alpha/kernel/asm-offsets.c | 2 arch/alpha/kernel/entry.S | 4 arch/sparc64/kernel/binfmt_aout32.c | 2 arch/sparc64/kernel/process.c | 3 arch/sparc64/kernel/sys_sparc32.c | 3 arch/ppc/kernel/asm-offsets.c | 2 arch/ia64/kernel/mca.c | 2 arch/ia64/kernel/asm-offsets.c | 2 arch/ia64/kernel/fsys.S | 16 + kernel/exit.c | 238 +++++--------------- kernel/power/process.c | 11 + kernel/sched.c | 2 kernel/ptrace.c | 307 +------------------------- kernel/timer.c | 6 - kernel/signal.c | 210 ++++-------------- kernel/acct.c | 2 kernel/tsacct.c | 2 kernel/sys.c | 2 kernel/fork.c | 67 ++---- include/linux/ptrace.h | 18 -- include/linux/sched.h | 16 - include/linux/init_task.h | 3 include/linux/tracehook.h | 414 +++++++++++++++++++++++++++++++++++ include/asm-powerpc/tracehook.h | 49 ++++ include/asm-x86/thread_info_32.h | 7 - include/asm-x86/thread_info_64.h | 3 include/asm-x86/tracehook.h | 63 +++++ include/asm-x86/signal.h | 4 drivers/connector/cn_proc.c | 4 mm/nommu.c | 4 69 files changed, 869 insertions(+), 1149 deletions(-) create include/linux/tracehook.h create include/asm-powerpc/tracehook.h create include/asm-x86/tracehook.h --- linux-2.6/fs/binfmt_flat.c +++ linux-2.6/fs/binfmt_flat.c @@ -920,9 +920,6 @@ static int load_flat_binary(struct linux start_thread(regs, start_addr, current->mm->start_stack); - if (current->ptrace & PT_PTRACED) - send_sig(SIGTRAP, current, 0); - return 0; } --- linux-2.6/fs/binfmt_som.c +++ linux-2.6/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 */ --- linux-2.6/fs/proc/base.c +++ linux-2.6/fs/proc/base.c @@ -68,6 +68,7 @@ #include #include #include +#include #include #include #include @@ -195,13 +196,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)) - struct mm_struct *mm_for_maps(struct task_struct *task) { struct mm_struct *mm = get_task_mm(task); @@ -609,7 +603,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; @@ -635,7 +630,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; @@ -679,7 +675,8 @@ static ssize_t mem_write(struct file * f if (!task) goto out_no_task; - if (!MAY_PTRACE(task) || !ptrace_may_attach(task)) + if (!tracehook_allow_access_process_vm(task) + || !ptrace_may_attach(task)) goto out; copied = -ENOMEM; --- linux-2.6/fs/proc/array.c +++ linux-2.6/fs/proc/array.c @@ -75,6 +75,7 @@ #include #include #include +#include #include #include #include @@ -158,6 +159,7 @@ static inline const char *get_task_state static inline char *task_state(struct task_struct *p, char *buffer) { + struct task_struct *tracer; struct group_info *group_info; int g; struct fdtable *fdt = NULL; @@ -167,9 +169,9 @@ static inline char *task_state(struct ta ns = current->nsproxy->pid_ns; rcu_read_lock(); ppid = pid_alive(p) ? - task_tgid_nr_ns(rcu_dereference(p->real_parent), ns) : 0; - tpid = pid_alive(p) && p->ptrace ? - task_pid_nr_ns(rcu_dereference(p->parent), ns) : 0; + task_tgid_nr_ns(rcu_dereference(p->parent), ns) : 0; + tracer = tracehook_tracer_task(p); + tpid = tracer == NULL ? 0 : tracer->pid; buffer += sprintf(buffer, "State:\t%s\n" "Tgid:\t%d\n" @@ -464,7 +466,7 @@ static int do_task_stat(struct task_stru } sid = task_session_nr_ns(task, ns); - ppid = task_tgid_nr_ns(task->real_parent, ns); + ppid = task_tgid_nr_ns(task->parent, ns); pgid = task_pgrp_nr_ns(task, ns); unlock_task_sighand(task, &flags); --- linux-2.6/fs/binfmt_elf_fdpic.c +++ linux-2.6/fs/binfmt_elf_fdpic.c @@ -427,13 +427,6 @@ static int load_elf_fdpic_binary(struct entryaddr = interp_params.entry_addr ?: exec_params.entry_addr; start_thread(regs, entryaddr, current->mm->start_stack); - if (unlikely(current->ptrace & PT_PTRACED)) { - if (current->ptrace & PT_TRACE_EXEC) - ptrace_notify((PTRACE_EVENT_EXEC << 8) | SIGTRAP); - else - send_sig(SIGTRAP, current, 0); - } - retval = 0; error: --- linux-2.6/fs/exec.c +++ linux-2.6/fs/exec.c @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include #include #include @@ -1104,13 +1104,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) @@ -1254,6 +1248,7 @@ int search_binary_handler(struct linux_b bprm->file = NULL; current->did_exec = 1; proc_exec_connector(current); + tracehook_report_exec(bprm, regs); return retval; } read_lock(&binfmt_lock); --- linux-2.6/fs/binfmt_elf.c +++ linux-2.6/fs/binfmt_elf.c @@ -1047,12 +1047,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); @@ -1384,7 +1378,7 @@ static void fill_prstatus(struct elf_prs prstatus->pr_sigpend = p->pending.signal.sig[0]; prstatus->pr_sighold = p->blocked.sig[0]; prstatus->pr_pid = task_pid_vnr(p); - prstatus->pr_ppid = task_pid_vnr(p->real_parent); + prstatus->pr_ppid = task_pid_vnr(p->parent); prstatus->pr_pgrp = task_pgrp_vnr(p); prstatus->pr_sid = task_session_vnr(p); if (thread_group_leader(p)) { @@ -1430,7 +1424,7 @@ static int fill_psinfo(struct elf_prpsin psinfo->pr_psargs[len] = 0; psinfo->pr_pid = task_pid_vnr(p); - psinfo->pr_ppid = task_pid_vnr(p->real_parent); + psinfo->pr_ppid = task_pid_vnr(p->parent); psinfo->pr_pgrp = task_pgrp_vnr(p); psinfo->pr_sid = task_session_vnr(p); --- linux-2.6/fs/binfmt_aout.c +++ linux-2.6/fs/binfmt_aout.c @@ -447,12 +447,6 @@ beyond_if: regs->gp = ex.a_gpvalue; #endif start_thread(regs, ex.a_entry, current->mm->start_stack); - if (unlikely(current->ptrace & PT_PTRACED)) { - if (current->ptrace & PT_TRACE_EXEC) - ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP); - else - send_sig(SIGTRAP, current, 0); - } return 0; } --- linux-2.6/security/selinux/hooks.c +++ linux-2.6/security/selinux/hooks.c @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include #include @@ -164,7 +164,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; @@ -1373,19 +1373,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, @@ -1855,12 +1849,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; @@ -2809,11 +2815,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; } @@ -4595,6 +4596,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; @@ -4683,18 +4685,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, 0, &avd); if (!error) tsec->sid = sid; task_unlock(p); - avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS, + avc_audit(ptsid, sid, SECCLASS_PROCESS, PROCESS__PTRACE, &avd, error, NULL); if (error) return error; } else { + rcu_read_unlock(); tsec->sid = sid; task_unlock(p); } --- linux-2.6/security/selinux/include/objsec.h +++ linux-2.6/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 { --- linux-2.6/arch/s390/kernel/compat_linux.c +++ linux-2.6/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]; --- linux-2.6/arch/s390/kernel/process.c +++ linux-2.6/arch/s390/kernel/process.c @@ -340,9 +340,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)); --- linux-2.6/arch/frv/kernel/ptrace.c +++ linux-2.6/arch/frv/kernel/ptrace.c @@ -689,24 +689,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); } --- linux-2.6/arch/arm/kernel/ptrace.c +++ linux-2.6/arch/arm/kernel/ptrace.c @@ -791,8 +791,6 @@ asmlinkage int syscall_trace(int why, st if (!test_thread_flag(TIF_SYSCALL_TRACE)) return scno; - if (!(current->ptrace & PT_PTRACED)) - return scno; /* * Save IP. IP is used to denote syscall entry/exit: @@ -803,20 +801,7 @@ asmlinkage int syscall_trace(int why, st current_thread_info()->syscall = 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; + tracehook_report_syscall(regs, why); return current_thread_info()->syscall; } --- linux-2.6/arch/mips/kernel/sysirix.c +++ linux-2.6/arch/mips/kernel/sysirix.c @@ -582,7 +582,7 @@ out: asmlinkage int irix_getpid(struct pt_regs *regs) { - regs->regs[3] = current->real_parent->pid; + regs->regs[3] = current->parent->pid; return current->pid; } --- linux-2.6/arch/mips/kernel/ptrace.c +++ linux-2.6/arch/mips/kernel/ptrace.c @@ -474,28 +474,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)) + tracehook_report_syscall(regs, entryexit); - if (!test_thread_flag(TIF_SYSCALL_TRACE)) - goto out; - - /* 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[0], regs->regs[4], regs->regs[5], --- linux-2.6/arch/powerpc/kernel/sys_ppc32.c +++ linux-2.6/arch/powerpc/kernel/sys_ppc32.c @@ -368,11 +368,6 @@ long compat_sys_execve(unsigned long a0, error = compat_do_execve(filename, compat_ptr(a1), compat_ptr(a2), regs); - if (error == 0) { - task_lock(current); - current->ptrace &= ~PT_DTRACE; - task_unlock(current); - } putname(filename); out: --- linux-2.6/arch/powerpc/kernel/ptrace32.c +++ linux-2.6/arch/powerpc/kernel/ptrace32.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include /* * does not yet catch signals sent when the child dies. @@ -90,6 +92,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(); @@ -337,9 +342,11 @@ 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; +#endif case PTRACE_GETREGS: { /* Get all pt_regs from the child. */ int ui; --- linux-2.6/arch/powerpc/kernel/ptrace.c +++ linux-2.6/arch/powerpc/kernel/ptrace.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include /* * does not yet catch signals sent when the child dies. @@ -255,8 +257,7 @@ static int set_evrregs(struct task_struc } #endif /* CONFIG_SPE */ - -static void set_single_step(struct task_struct *task) +void tracehook_enable_single_step(struct task_struct *task) { struct pt_regs *regs = task->thread.regs; @@ -271,7 +272,7 @@ static void set_single_step(struct task_ set_tsk_thread_flag(task, TIF_SINGLESTEP); } -static void clear_single_step(struct task_struct *task) +void tracehook_disable_single_step(struct task_struct *task) { struct pt_regs *regs = task->thread.regs; @@ -313,7 +314,7 @@ static int ptrace_set_debugreg(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); } /* @@ -456,7 +457,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; @@ -473,7 +474,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; } @@ -483,7 +484,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); @@ -604,31 +605,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 @@ -654,8 +636,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); + } } --- linux-2.6/arch/powerpc/kernel/signal.c +++ linux-2.6/arch/powerpc/kernel/signal.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -173,6 +174,8 @@ int do_signal(sigset_t *oldset, struct p */ if (test_thread_flag(TIF_RESTORE_SIGMASK)) clear_thread_flag(TIF_RESTORE_SIGMASK); + + tracehook_report_handle_signal(signr, &ka, oldset, regs); } return ret; --- linux-2.6/arch/powerpc/kernel/asm-offsets.c +++ linux-2.6/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, stack)); - DEFINE(PTRACE, offsetof(struct task_struct, ptrace)); #endif /* CONFIG_PPC64 */ DEFINE(KSP, offsetof(struct thread_struct, ksp)); @@ -79,7 +78,6 @@ int main(void) DEFINE(PGDIR, offsetof(struct thread_struct, pgdir)); #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) DEFINE(THREAD_DBCR0, offsetof(struct thread_struct, dbcr0)); - DEFINE(PT_PTRACED, PT_PTRACED); #endif #ifdef CONFIG_SPE DEFINE(THREAD_EVR0, offsetof(struct thread_struct, evr[0])); --- linux-2.6/arch/powerpc/kernel/process.c +++ linux-2.6/arch/powerpc/kernel/process.c @@ -862,11 +862,6 @@ int sys_execve(unsigned long a0, unsigne flush_spe_to_thread(current); error = do_execve(filename, (char __user * __user *) a1, (char __user * __user *) a2, regs); - if (error == 0) { - task_lock(current); - current->ptrace &= ~PT_DTRACE; - task_unlock(current); - } putname(filename); out: return error; --- linux-2.6/arch/x86/math-emu/fpu_entry.c +++ linux-2.6/arch/x86/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) ) --- linux-2.6/arch/x86/ia32/sys_ia32.c +++ linux-2.6/arch/x86/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; } --- linux-2.6/arch/x86/ia32/ptrace32.c +++ linux-2.6/arch/x86/ia32/ptrace32.c @@ -389,9 +389,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(); --- linux-2.6/arch/x86/ia32/ia32_aout.c +++ linux-2.6/arch/x86/ia32/ia32_aout.c @@ -423,12 +423,6 @@ beyond_if: regs->r8 = regs->r9 = regs->r10 = regs->r11 = regs->r12 = regs->r13 = regs->r14 = regs->r15 = 0; set_fs(USER_DS); - if (unlikely(current->ptrace & PT_PTRACED)) { - if (current->ptrace & PT_TRACE_EXEC) - ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP); - else - send_sig(SIGTRAP, current, 0); - } return 0; } --- linux-2.6/arch/x86/ia32/ia32_signal.c +++ linux-2.6/arch/x86/ia32/ia32_signal.c @@ -492,11 +492,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", @@ -600,9 +596,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", --- linux-2.6/arch/x86/ia32/ia32entry.S +++ linux-2.6/arch/x86/ia32/ia32entry.S @@ -326,7 +326,7 @@ ENTRY(ia32_syscall) jnz ia32_tracesys ia32_do_syscall: cmpl $(IA32_NR_syscalls-1),%eax - ja ia32_badsys + ja int_ret_from_sys_call /* ia32_tracesys has set RAX(%rsp) */ IA32_ARG_FIXUP call *ia32_sys_call_table(,%rax,8) # xxx: rip relative ia32_sysret: @@ -336,7 +336,7 @@ ia32_sysret: ia32_tracesys: SAVE_REST CLEAR_RREGS - movq $-ENOSYS,RAX(%rsp) /* really needed? */ + movq $-ENOSYS,RAX(%rsp) /* ptrace can change this for a bad syscall */ movq %rsp,%rdi /* &pt_regs -> arg1 */ call syscall_trace_enter LOAD_ARGS32 ARGOFFSET /* reload args from stack in case ptrace changed it */ --- linux-2.6/arch/x86/kernel/vm86_32.c +++ linux-2.6/arch/x86/kernel/vm86_32.c @@ -554,13 +554,6 @@ int handle_vm86_trap(struct kernel_vm86_ } if (trapno !=1) return 1; /* we let this handle by the calling routine */ - if (current->ptrace & PT_PTRACED) { - unsigned long flags; - spin_lock_irqsave(¤t->sighand->siglock, flags); - sigdelset(¤t->blocked, SIGTRAP); - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - } send_sig(SIGTRAP, current, 1); current->thread.trap_no = trapno; current->thread.error_code = error_code; --- linux-2.6/arch/x86/kernel/process_64.c +++ linux-2.6/arch/x86/kernel/process_64.c @@ -712,11 +712,6 @@ long sys_execve(char __user *name, char if (IS_ERR(filename)) return error; error = do_execve(filename, argv, envp, ®s); - if (error == 0) { - task_lock(current); - current->ptrace &= ~PT_DTRACE; - task_unlock(current); - } putname(filename); return error; } --- linux-2.6/arch/x86/kernel/process_32.c +++ linux-2.6/arch/x86/kernel/process_32.c @@ -824,9 +824,6 @@ asmlinkage int sys_execve(struct pt_regs (char __user * __user *) regs.edx, ®s); if (error == 0) { - task_lock(current); - current->ptrace &= ~PT_DTRACE; - task_unlock(current); /* Make sure we don't return using sysenter.. */ set_thread_flag(TIF_IRET); } --- linux-2.6/arch/x86/kernel/entry_32.S +++ linux-2.6/arch/x86/kernel/entry_32.S @@ -333,7 +333,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 @@ -371,7 +371,7 @@ ENTRY(system_call) GET_THREAD_INFO(%ebp) # 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 @@ -516,9 +516,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 --- linux-2.6/arch/x86/kernel/signal_64.c +++ linux-2.6/arch/x86/kernel/signal_64.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -295,9 +295,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=%lx ra=%p\n", current->comm, current->pid, frame, regs->rip, frame->pretcode); @@ -349,16 +346,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)) { @@ -377,6 +370,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; --- linux-2.6/arch/x86/kernel/entry_64.S +++ linux-2.6/arch/x86/kernel/entry_64.S @@ -306,19 +306,17 @@ badsys: /* Do syscall tracing */ tracesys: SAVE_REST - movq $-ENOSYS,RAX(%rsp) + movq $-ENOSYS,RAX(%rsp) /* ptrace can change this for a bad syscall */ FIXUP_TOP_OF_STACK %rdi movq %rsp,%rdi call syscall_trace_enter LOAD_ARGS ARGOFFSET /* reload args from stack in case ptrace changed it */ RESTORE_REST cmpq $__NR_syscall_max,%rax - movq $-ENOSYS,%rcx - cmova %rcx,%rax - ja 1f + ja int_ret_from_sys_call /* -ENOSYS already in RAX(%rsp) */ movq %r10,%rcx /* fixup for C */ call *sys_call_table(,%rax,8) -1: movq %rax,RAX-ARGOFFSET(%rsp) + movq %rax,RAX-ARGOFFSET(%rsp) /* Use IRET because user could have changed frame */ /* --- linux-2.6/arch/x86/kernel/ptrace_32.c +++ linux-2.6/arch/x86/kernel/ptrace_32.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ #include #include #include +#include /* * does not yet catch signals sent when the child dies. @@ -221,7 +223,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); @@ -249,19 +251,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; } } @@ -272,8 +273,7 @@ static void clear_singlestep(struct task */ void ptrace_disable(struct task_struct *child) { - clear_singlestep(child); - clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); + tracehook_disable_single_step(child); } /* @@ -474,18 +474,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; @@ -501,7 +501,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; @@ -511,13 +511,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); @@ -640,78 +635,24 @@ void send_sigtrap(struct task_struct *ts * - triggered by current->work.syscall_trace */ __attribute__((regparm(3))) -int do_syscall_trace(struct pt_regs *regs, int entryexit) +void do_syscall_trace(struct pt_regs *regs, int entryexit) { - int is_sysemu = test_thread_flag(TIF_SYSCALL_EMU); - /* - * With TIF_SYSCALL_EMU set we want to ignore TIF_SINGLESTEP for syscall - * interception - */ - int is_singlestep = !is_sysemu && test_thread_flag(TIF_SINGLESTEP); - int ret = 0; - /* do the secure computing check first */ if (!entryexit) secure_computing(regs->orig_eax); - if (unlikely(current->audit_context)) { - if (entryexit) - audit_syscall_exit(AUDITSC_RESULT(regs->eax), - regs->eax); - /* Debug traps, when using PTRACE_SINGLESTEP, must be sent only - * on the syscall exit path. Normally, when TIF_SYSCALL_AUDIT is - * not used, entry.S will call us only on syscall exit, not - * entry; so when TIF_SYSCALL_AUDIT is used we must avoid - * calling send_sigtrap() on syscall entry. - * - * Note that when PTRACE_SYSEMU_SINGLESTEP is used, - * is_singlestep is false, despite his name, so we will still do - * the correct thing. - */ - else if (is_singlestep) - goto out; - } - - if (!(current->ptrace & PT_PTRACED)) - goto out; + if (unlikely(current->audit_context) && entryexit) + audit_syscall_exit(AUDITSC_RESULT(regs->eax), regs->eax); - /* If a process stops on the 1st tracepoint with SYSCALL_TRACE - * and then is resumed with SYSEMU_SINGLESTEP, it will come in - * here. We have to check this and return */ - if (is_sysemu && entryexit) - return 0; - - /* Fake a debug trap */ - if (is_singlestep) - send_sigtrap(current, regs, 0); - - if (!test_thread_flag(TIF_SYSCALL_TRACE) && !is_sysemu) - goto out; - - /* the 0x80 provides a way for the tracing parent to distinguish - between a syscall stop and SIGTRAP delivery */ - /* Note that the debugger could change the result of test_thread_flag!*/ - ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80:0)); + if (test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall(regs, entryexit); - /* - * this isn't the same as continuing with a signal, but it will do - * for normal use. strace only continues with a signal if the - * stopping signal is not SIGTRAP. -brl - */ - if (current->exit_code) { - send_sig(current->exit_code, current, 1); - current->exit_code = 0; + if (test_thread_flag(TIF_SINGLESTEP) && entryexit) { + send_sigtrap(current, regs, 0); /* XXX */ + tracehook_report_syscall_step(regs); } - ret = is_sysemu; -out: + if (unlikely(current->audit_context) && !entryexit) audit_syscall_entry(AUDIT_ARCH_I386, regs->orig_eax, regs->ebx, regs->ecx, regs->edx, regs->esi); - if (ret == 0) - return 0; - - regs->orig_eax = -1; /* force skip of syscall restarting */ - if (unlikely(current->audit_context)) - audit_syscall_exit(AUDITSC_RESULT(regs->eax), regs->eax); - return 1; } --- linux-2.6/arch/x86/kernel/traps_64.c +++ linux-2.6/arch/x86/kernel/traps_64.c @@ -886,14 +886,6 @@ asmlinkage void __kprobes do_debug(struc */ if (!user_mode(regs)) goto clear_TF_reenable; - /* - * Was the TF flag set by a debugger? If so, clear it now, - * so that register information is correct. - */ - if (tsk->ptrace & PT_DTRACE) { - regs->eflags &= ~TF_MASK; - tsk->ptrace &= ~PT_DTRACE; - } } /* Ok, finally something we can handle */ --- linux-2.6/arch/x86/kernel/ptrace_64.c +++ linux-2.6/arch/x86/kernel/ptrace_64.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -172,7 +173,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); @@ -200,19 +201,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; } } @@ -223,7 +223,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, @@ -437,7 +437,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; @@ -484,7 +484,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; @@ -493,7 +493,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); @@ -562,37 +562,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)) { @@ -614,8 +590,11 @@ asmlinkage void syscall_trace_leave(stru if (unlikely(current->audit_context)) audit_syscall_exit(AUDITSC_RESULT(regs->rax), regs->rax); - if ((test_thread_flag(TIF_SYSCALL_TRACE) - || test_thread_flag(TIF_SINGLESTEP)) - && (current->ptrace & PT_PTRACED)) - syscall_trace(regs); + if (test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall(regs, 1); + + if (test_thread_flag(TIF_SINGLESTEP)) { + force_sig(SIGTRAP, current); /* XXX */ + tracehook_report_syscall_step(regs); + } } --- linux-2.6/arch/x86/kernel/signal_32.c +++ linux-2.6/arch/x86/kernel/signal_32.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -390,16 +390,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); @@ -483,16 +473,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); @@ -537,14 +517,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) @@ -559,6 +537,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; --- linux-2.6/arch/x86/mm/fault_64.c +++ linux-2.6/arch/x86/mm/fault_64.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include --- linux-2.6/arch/alpha/kernel/asm-offsets.c +++ linux-2.6/arch/alpha/kernel/asm-offsets.c @@ -27,7 +27,7 @@ void foo(void) DEFINE(TASK_EUID, offsetof(struct task_struct, euid)); DEFINE(TASK_GID, offsetof(struct task_struct, gid)); DEFINE(TASK_EGID, offsetof(struct task_struct, egid)); - DEFINE(TASK_REAL_PARENT, offsetof(struct task_struct, real_parent)); + DEFINE(TASK_PARENT, offsetof(struct task_struct, parent)); DEFINE(TASK_GROUP_LEADER, offsetof(struct task_struct, group_leader)); DEFINE(TASK_TGID, offsetof(struct task_struct, tgid)); BLANK(); --- linux-2.6/arch/alpha/kernel/entry.S +++ linux-2.6/arch/alpha/kernel/entry.S @@ -878,14 +878,14 @@ sys_getxpid: /* See linux/kernel/timer.c sys_getppid for discussion about this loop. */ ldq $3, TASK_GROUP_LEADER($2) - ldq $4, TASK_REAL_PARENT($3) + ldq $4, TASK_PARENT($3) ldl $0, TASK_TGID($2) 1: ldl $1, TASK_TGID($4) #ifdef CONFIG_SMP mov $4, $5 mb ldq $3, TASK_GROUP_LEADER($2) - ldq $4, TASK_REAL_PARENT($3) + ldq $4, TASK_PARENT($3) cmpeq $4, $5, $5 beq $5, 1b #endif --- linux-2.6/arch/sparc64/kernel/binfmt_aout32.c +++ linux-2.6/arch/sparc64/kernel/binfmt_aout32.c @@ -336,8 +336,6 @@ beyond_if: tsb_context_switch(current->mm); start_thread32(regs, ex.a_entry, current->mm->start_stack); - if (current->ptrace & PT_PTRACED) - send_sig(SIGTRAP, current, 0); return 0; } --- linux-2.6/arch/sparc64/kernel/process.c +++ linux-2.6/arch/sparc64/kernel/process.c @@ -831,9 +831,6 @@ asmlinkage int sparc_execve(struct pt_re current_thread_info()->xfsr[0] = 0; current_thread_info()->fpsaved[0] = 0; regs->tstate &= ~TSTATE_PEF; - task_lock(current); - current->ptrace &= ~PT_DTRACE; - task_unlock(current); } out: return error; --- linux-2.6/arch/sparc64/kernel/sys_sparc32.c +++ linux-2.6/arch/sparc64/kernel/sys_sparc32.c @@ -679,9 +679,6 @@ asmlinkage long sparc32_execve(struct pt current_thread_info()->xfsr[0] = 0; current_thread_info()->fpsaved[0] = 0; regs->tstate &= ~TSTATE_PEF; - task_lock(current); - current->ptrace &= ~PT_DTRACE; - task_unlock(current); } out: return error; --- linux-2.6/arch/ppc/kernel/asm-offsets.c +++ linux-2.6/arch/ppc/kernel/asm-offsets.c @@ -37,7 +37,6 @@ main(void) DEFINE(THREAD, offsetof(struct task_struct, thread)); DEFINE(THREAD_INFO, offsetof(struct task_struct, stack)); 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(PT_REGS, offsetof(struct thread_struct, regs)); @@ -46,7 +45,6 @@ main(void) DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr)); #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) DEFINE(THREAD_DBCR0, offsetof(struct thread_struct, dbcr0)); - DEFINE(PT_PTRACED, PT_PTRACED); #endif #ifdef CONFIG_ALTIVEC DEFINE(THREAD_VR0, offsetof(struct thread_struct, vr[0])); --- linux-2.6/arch/ia64/kernel/mca.c +++ linux-2.6/arch/ia64/kernel/mca.c @@ -1745,7 +1745,7 @@ format_mca_init_stack(void *mca_data, un p->state = TASK_UNINTERRUPTIBLE; cpu_set(cpu, p->cpus_allowed); INIT_LIST_HEAD(&p->tasks); - p->parent = p->real_parent = p->group_leader = p; + p->parent = p->group_leader = p; INIT_LIST_HEAD(&p->children); INIT_LIST_HEAD(&p->sibling); strncpy(p->comm, type, sizeof(p->comm)-1); --- linux-2.6/arch/ia64/kernel/asm-offsets.c +++ linux-2.6/arch/ia64/kernel/asm-offsets.c @@ -47,7 +47,7 @@ void foo(void) DEFINE(IA64_TASK_GROUP_LEADER_OFFSET, offsetof (struct task_struct, group_leader)); DEFINE(IA64_TASK_PENDING_OFFSET,offsetof (struct task_struct, pending)); DEFINE(IA64_TASK_PID_OFFSET, offsetof (struct task_struct, pid)); - DEFINE(IA64_TASK_REAL_PARENT_OFFSET, offsetof (struct task_struct, real_parent)); + DEFINE(IA64_TASK_PARENT_OFFSET, offsetof (struct task_struct, parent)); DEFINE(IA64_TASK_SIGHAND_OFFSET,offsetof (struct task_struct, sighand)); DEFINE(IA64_TASK_SIGNAL_OFFSET,offsetof (struct task_struct, signal)); DEFINE(IA64_TASK_TGID_OFFSET, offsetof (struct task_struct, tgid)); --- linux-2.6/arch/ia64/kernel/fsys.S +++ linux-2.6/arch/ia64/kernel/fsys.S @@ -85,29 +85,29 @@ ENTRY(fsys_getppid) ;; ld4 r9=[r9] - add r17=IA64_TASK_REAL_PARENT_OFFSET,r17 // r17 = ¤t->group_leader->real_parent + add r17=IA64_TASK_PARENT_OFFSET,r17 // r17 = ¤t->group_leader->parent ;; and r9=TIF_ALLWORK_MASK,r9 -1: ld8 r18=[r17] // r18 = current->group_leader->real_parent +1: ld8 r18=[r17] // r18 = current->group_leader->parent ;; cmp.ne p8,p0=0,r9 - add r8=IA64_TASK_TGID_OFFSET,r18 // r8 = ¤t->group_leader->real_parent->tgid + add r8=IA64_TASK_TGID_OFFSET,r18 // r8 = ¤t->group_leader->parent->tgid ;; /* * The .acq is needed to ensure that the read of tgid has returned its data before - * we re-check "real_parent". + * we re-check "parent". */ - ld4.acq r8=[r8] // r8 = current->group_leader->real_parent->tgid + ld4.acq r8=[r8] // r8 = current->group_leader->parent->tgid #ifdef CONFIG_SMP /* - * Re-read current->group_leader->real_parent. + * Re-read current->group_leader->parent. */ - ld8 r19=[r17] // r19 = current->group_leader->real_parent + ld8 r19=[r17] // r19 = current->group_leader->parent (p8) br.spnt.many fsys_fallback_syscall ;; - cmp.ne p6,p0=r18,r19 // did real_parent change? + cmp.ne p6,p0=r18,r19 // did parent change? mov r19=0 // i must not leak kernel bits... (p6) br.cond.spnt.few 1b // yes -> redo the read of tgid and the check ;; --- linux-2.6/kernel/exit.c +++ linux-2.6/kernel/exit.c @@ -21,8 +21,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -146,12 +146,13 @@ void release_task(struct task_struct * p { struct task_struct *leader; int zap_leader; + int inhibited_leader; repeat: + tracehook_release_task(p); atomic_dec(&p->user->processes); + BUG_ON(tracehook_check_released(p)); proc_flush_task(p); write_lock_irq(&tasklist_lock); - ptrace_unlink(p); - BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children)); __exit_signal(p); /* @@ -160,10 +161,14 @@ repeat: * group leader's parent process. (if it wants notification.) */ zap_leader = 0; + inhibited_leader = 0; leader = p->group_leader; if (leader != p && thread_group_empty(leader) && leader->exit_state == EXIT_ZOMBIE) { BUG_ON(leader->exit_signal == -1); - do_notify_parent(leader, leader->exit_signal); + if (tracehook_inhibit_wait_zombie(leader)) + inhibited_leader = 1; + else + do_notify_parent(leader, leader->exit_signal); /* * If we were the last child thread and the leader has * exited already, and the leader's parent ignores SIGCHLD, @@ -173,6 +178,14 @@ repeat: * that case. */ zap_leader = (leader->exit_signal == -1); + if (zap_leader) { + /* + * Preserve the invariant that release_task() + * can only be called on a task in EXIT_DEAD. + */ + zap_leader = xchg(&leader->exit_state, EXIT_DEAD); + BUG_ON(zap_leader != EXIT_ZOMBIE); + } } write_unlock_irq(&tasklist_lock); @@ -182,6 +195,13 @@ repeat: p = leader; if (unlikely(zap_leader)) goto repeat; + + /* + * If tracing usurps normal reaping of the leader, tracing needs + * to be notified it would normally be reapable now. + */ + if (unlikely(inhibited_leader)) + tracehook_report_delayed_group_leader(leader); } /* @@ -221,10 +241,10 @@ static int will_become_orphaned_pgrp(str do_each_pid_task(pgrp, PIDTYPE_PGID, p) { if (p == ignored_task || p->exit_state - || is_global_init(p->real_parent)) + || is_global_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; } @@ -273,10 +293,9 @@ static void reparent_to_kthreadd(void) { write_lock_irq(&tasklist_lock); - ptrace_unlink(current); /* Reparent to init */ remove_parent(current); - current->real_parent = current->parent = kthreadd_task; + current->parent = kthreadd_task; add_parent(current); /* Set the exit signal to SIGCHLD so we signal init on exit */ @@ -593,41 +612,19 @@ static void exit_mm(struct task_struct * } 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. */ @@ -637,7 +634,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); @@ -669,9 +667,6 @@ reparent_thread(struct task_struct *p, s static void forget_original_parent(struct task_struct *father) { struct task_struct *p, *n, *reaper = father; - struct list_head ptrace_dead; - - INIT_LIST_HEAD(&ptrace_dead); write_lock_irq(&tasklist_lock); @@ -683,58 +678,13 @@ static void forget_original_parent(struc } } while (reaper->flags & PF_EXITING); - /* - * 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_entry_safe(p, n, &father->children, sibling) { - int ptrace; - - 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 */ - p->real_parent = 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, &ptrace_dead); - } - - list_for_each_entry_safe(p, n, &father->ptrace_children, ptrace_list) { - p->real_parent = reaper; - reparent_thread(p, father, 1); + p->parent = reaper; + reparent_thread(p, father); } write_unlock_irq(&tasklist_lock); BUG_ON(!list_empty(&father->children)); - BUG_ON(!list_empty(&father->ptrace_children)); - - list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_list) { - list_del_init(&p->ptrace_list); - release_task(p); - } - } /* @@ -746,6 +696,8 @@ static void exit_notify(struct task_stru int state; struct task_struct *t; struct pid *pgrp; + int noreap; + void *cookie; if (signal_pending(tsk) && !(tsk->signal->flags & SIGNAL_GROUP_EXIT) && !thread_group_empty(tsk)) { @@ -786,7 +738,7 @@ static void exit_notify(struct task_stru * and we were the only connection outside, so our pgrp * is about to become orphaned. */ - t = tsk->real_parent; + t = tsk->parent; pgrp = task_pgrp(tsk); if ((task_pgrp(t) != pgrp) && @@ -817,20 +769,12 @@ 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)) + if (tsk->exit_signal == -1 && !noreap) state = EXIT_DEAD; tsk->exit_state = state; @@ -841,6 +785,8 @@ static void exit_notify(struct task_stru write_unlock_irq(&tasklist_lock); + tracehook_report_death(tsk, state, cookie); + /* If the process is dead, release it - nobody will wait for it */ if (state == EXIT_DEAD) release_task(tsk); @@ -921,10 +867,7 @@ fastcall NORET_TYPE void do_exit(long co if (unlikely(!tsk->pid)) panic("Attempted to kill the idle task!"); - 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 @@ -1127,10 +1070,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; @@ -1191,7 +1133,7 @@ static int wait_task_zombie(struct task_ int __user *stat_addr, struct rusage __user *ru) { unsigned long state; - int retval, status, traced; + int retval, status; struct pid_namespace *ns; ns = current->nsproxy->pid_ns; @@ -1204,7 +1146,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); @@ -1229,10 +1171,7 @@ static int wait_task_zombie(struct task_ return 0; } - /* traced means p->ptrace, but not vice versa */ - traced = (p->real_parent != p->parent); - - if (likely(!traced)) { + if (likely(p->signal)) { struct signal_struct *psig; struct signal_struct *sig; @@ -1322,24 +1261,6 @@ static int wait_task_zombie(struct task_ if (!retval) retval = task_pid_nr_ns(p, ns); - if (traced) { - write_lock_irq(&tasklist_lock); - /* We dropped tasklist, ptracer could die and untrace */ - ptrace_unlink(p); - /* - * 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->exit_state = EXIT_ZOMBIE; - p = NULL; - } - } - write_unlock_irq(&tasklist_lock); - } if (p != NULL) release_task(p); @@ -1361,8 +1282,7 @@ static int wait_task_stopped(struct task if (!p->exit_code) return 0; - if (delayed_group_leader && !(p->ptrace & PT_PTRACED) && - p->signal->group_stop_count > 0) + if (delayed_group_leader && p->signal->group_stop_count > 0) /* * A group stop is in progress and this is the group leader. * We won't report until all threads have stopped. @@ -1382,13 +1302,12 @@ static int wait_task_stopped(struct task if (unlikely(noreap)) { 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->exit_state)) goto bail_ref; return wait_noreap_copyout(p, pid, uid, - why, exit_code, + CLD_STOPPED, exit_code, infop, ru); } @@ -1444,9 +1363,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) @@ -1513,22 +1430,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) { @@ -1565,26 +1466,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), @@ -1609,6 +1501,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); @@ -1625,6 +1521,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); @@ -1634,13 +1532,7 @@ check_continued: } } if (!flag) { - list_for_each_entry(p, &tsk->ptrace_children, - ptrace_list) { - if (!eligible_child(pid, options, p)) - continue; - flag = 1; - break; - } + // XXX set flag if we have ptracees } if (options & __WNOTHREAD) break; @@ -1667,7 +1559,7 @@ end: remove_wait_queue(¤t->signal->wait_chldexit,&wait); if (infop) { if (retval > 0) - retval = 0; + retval = 0; else { /* * For a WNOHANG return, clear out all the fields --- linux-2.6/kernel/power/process.c +++ linux-2.6/kernel/power/process.c @@ -13,6 +13,7 @@ #include #include #include +#include /* * Timeout for stopping processes @@ -182,9 +183,13 @@ static int try_to_freeze_tasks(int freez if (frozen(p) || !freezeable(p)) continue; - if (p->state == TASK_TRACED && frozen(p->parent)) { - cancel_freezing(p); - continue; + if (p->state == TASK_TRACED) { + struct task_struct *tracer; + tracer = tracehook_tracer_task(p); + if (tracer && frozen(tracer)) { + cancel_freezing(p); + continue; + } } if (!freeze_task(p, freeze_user_space)) --- linux-2.6/kernel/sched.c +++ linux-2.6/kernel/sched.c @@ -4918,7 +4918,7 @@ static void show_task(struct task_struct } #endif printk(KERN_CONT "%5lu %5d %6d\n", free, - task_pid_nr(p), task_pid_nr(p->real_parent)); + task_pid_nr(p), task_pid_nr(p->parent)); if (state != TASK_RUNNING) show_stack(p, NULL); --- linux-2.6/kernel/ptrace.c +++ linux-2.6/kernel/ptrace.c @@ -18,106 +18,20 @@ #include #include #include +#include #include #include #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; } int __ptrace_may_attach(struct task_struct *task) @@ -162,7 +76,6 @@ int ptrace_may_attach(struct task_struct int ptrace_attach(struct task_struct *task) { int retval; - unsigned long flags; audit_ptrace(task); @@ -170,63 +83,18 @@ int ptrace_attach(struct task_struct *ta if (task->pid <= 1) goto out; if (same_thread_group(task, current)) - 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); - if (!write_trylock_irqsave(&tasklist_lock, flags)) { - 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 = __ptrace_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_irqrestore(&tasklist_lock, flags); - task_unlock(task); out: return retval; } -static inline void __ptrace_detach(struct task_struct *child, unsigned int data) -{ - child->exit_code = data; - /* .. re-parent .. */ - __ptrace_unlink(child); - /* .. and wake it up. */ - if (child->exit_state != EXIT_ZOMBIE) - wake_up_process(child); -} - int ptrace_detach(struct task_struct *child, unsigned int data) { if (!valid_signal(data)) @@ -236,13 +104,10 @@ int ptrace_detach(struct task_struct *ch ptrace_disable(child); clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - 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) @@ -295,106 +160,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; - case PTRACE_DETACH: /* detach a process that was attached. */ - ret = ptrace_detach(child, data); - break; - default: - break; - } - - return ret; + return -ENOSYS; } /** @@ -407,20 +176,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; } /** @@ -461,48 +221,7 @@ struct task_struct *ptrace_get_task_stru #ifndef __ARCH_SYS_PTRACE asmlinkage long sys_ptrace(long request, long pid, long addr, long data) { - struct task_struct *child; - long ret; - - /* - * This lock_kernel fixes a subtle race with suid exec - */ - lock_kernel(); - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); - goto out; - } - - child = ptrace_get_task_struct(pid); - if (IS_ERR(child)) { - ret = PTR_ERR(child); - goto out; - } - - if (request == PTRACE_ATTACH) { - ret = ptrace_attach(child); - /* - * Some architectures need to do book-keeping after - * a ptrace attach. - */ - if (!ret) - arch_ptrace_attach(child); - goto out_put_task_struct; - } - - ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (ret < 0) - goto out_put_task_struct; - - ret = arch_ptrace(child, request, addr, data); - if (ret < 0) - goto out_put_task_struct; - - out_put_task_struct: - put_task_struct(child); - out: - unlock_kernel(); - return ret; + return -ENOSYS; } #endif /* __ARCH_SYS_PTRACE */ --- linux-2.6/kernel/timer.c +++ linux-2.6/kernel/timer.c @@ -968,9 +968,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) @@ -978,7 +978,7 @@ asmlinkage long sys_getppid(void) int pid; rcu_read_lock(); - pid = task_tgid_nr_ns(current->real_parent, current->nsproxy->pid_ns); + pid = task_tgid_nr_ns(current->parent, current->nsproxy->pid_ns); rcu_read_unlock(); return pid; --- linux-2.6/kernel/signal.c +++ linux-2.6/kernel/signal.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -45,12 +45,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. @@ -60,8 +54,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); } /* @@ -100,7 +98,8 @@ static int recalc_sigpending_tsk(struct { if (t->signal->group_stop_count > 0 || 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); return 1; } @@ -258,7 +257,7 @@ int unhandled_signal(struct task_struct { if (is_global_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); @@ -546,8 +545,6 @@ static int check_kill_permission(int sig return security_task_kill(t, info, sig, 0); } -/* forward decl */ -static void do_notify_parent_cldstop(struct task_struct *tsk, int why); /* * Handle magic process-wide effects of stop/continue signals. @@ -890,7 +887,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. */ @@ -1443,8 +1440,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; @@ -1484,7 +1480,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))) { /* @@ -1512,20 +1508,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; /* @@ -1556,6 +1545,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 && @@ -1568,107 +1566,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; - - /* - * 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. - * This sets TIF_SIGPENDING, but never clears it. - */ - recalc_sigpending_tsk(current); -} - -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 = task_pid_vnr(current); - 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) { @@ -1677,7 +1574,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); @@ -1804,44 +1701,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 = task_pid_vnr(current->parent); - 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) { @@ -1888,7 +1765,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; } @@ -1917,13 +1794,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); @@ -1935,7 +1812,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); --- linux-2.6/kernel/acct.c +++ linux-2.6/kernel/acct.c @@ -482,7 +482,7 @@ static void do_acct_process(struct file #endif #if ACCT_VERSION==3 ac.ac_pid = current->tgid; - ac.ac_ppid = current->real_parent->tgid; + ac.ac_ppid = current->parent->tgid; #endif spin_lock_irq(¤t->sighand->siglock); --- linux-2.6/kernel/tsacct.c +++ linux-2.6/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; --- linux-2.6/kernel/sys.c +++ linux-2.6/kernel/sys.c @@ -942,7 +942,7 @@ asmlinkage long sys_setpgid(pid_t pid, p if (!thread_group_leader(p)) goto out; - if (p->real_parent->tgid == group_leader->tgid) { + if (p->parent->tgid == group_leader->tgid) { err = -EPERM; if (task_session(p) != task_session(group_leader)) goto out; --- linux-2.6/kernel/fork.c +++ linux-2.6/kernel/fork.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include #include @@ -940,8 +940,7 @@ static void copy_flags(unsigned long clo new_flags &= ~PF_SUPERPRIV; new_flags |= PF_FORKNOEXEC; - if (!(clone_flags & CLONE_PTRACE)) - p->ptrace = 0; + new_flags |= PF_STARTING; p->flags = new_flags; clear_freeze_flag(p); } @@ -1212,8 +1211,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); /* Now that the task is set up, run cgroup callbacks if * necessary. We need to run them before the task is visible @@ -1243,10 +1240,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); @@ -1288,8 +1284,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)) { if (clone_flags & CLONE_NEWPID) @@ -1378,22 +1373,6 @@ struct task_struct * __cpuinit fork_idle return task; } -static 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. * @@ -1408,15 +1387,8 @@ long do_fork(unsigned long clone_flags, int __user *child_tidptr) { struct task_struct *p; - int trace = 0; long 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, child_tidptr, NULL); /* @@ -1424,6 +1396,10 @@ long do_fork(unsigned long clone_flags, * might get invalid after that point, if the thread exits quickly. */ if (!IS_ERR(p)) { + /* + * When called from kernel_thread, don't do user tracing stuff. + */ + int is_user = likely(user_mode(regs)); struct completion vfork; /* @@ -1442,32 +1418,31 @@ long do_fork(unsigned long clone_flags, init_completion(&vfork); } - if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) { + if (likely(is_user)) + 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); - } + if (likely(is_user)) + tracehook_report_clone_complete(clone_flags, nr, p); if (clone_flags & CLONE_VFORK) { freezer_do_not_count(); wait_for_completion(&vfork); freezer_count(); - if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) { - current->ptrace_message = nr; - ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP); - } + if (likely(is_user)) + tracehook_report_vfork_done(p, nr); } } else { nr = PTR_ERR(p); --- linux-2.6/include/linux/ptrace.h +++ linux-2.6/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,26 +90,9 @@ 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); 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); -} - int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data); int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data); --- linux-2.6/include/linux/sched.h +++ linux-2.6/include/linux/sched.h @@ -919,7 +919,6 @@ struct task_struct { void *stack; atomic_t usage; unsigned int flags; /* per process flags, defined below */ - unsigned int ptrace; int lock_depth; /* BKL lock depth */ @@ -963,12 +962,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; @@ -988,15 +981,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 */ @@ -1129,8 +1120,6 @@ struct task_struct { struct io_context *io_context; - unsigned long ptrace_message; - siginfo_t *last_siginfo; /* For ptrace use. */ #ifdef CONFIG_TASK_XACCT /* i/o counters(bytes read/written, #syscalls */ u64 rchar, wchar, syscr, syscw; @@ -1608,6 +1597,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); --- linux-2.6/include/linux/init_task.h +++ linux-2.6/include/linux/init_task.h @@ -136,9 +136,6 @@ extern struct group_info init_groups; .ioprio = 0, \ .time_slice = HZ, \ .tasks = LIST_HEAD_INIT(tsk.tasks), \ - .ptrace_children= LIST_HEAD_INIT(tsk.ptrace_children), \ - .ptrace_list = LIST_HEAD_INIT(tsk.ptrace_list), \ - .real_parent = &tsk, \ .parent = &tsk, \ .children = LIST_HEAD_INIT(tsk.children), \ .sibling = LIST_HEAD_INIT(tsk.sibling), \ --- linux-2.6/include/linux/tracehook.h +++ linux-2.6/include/linux/tracehook.h @@ -0,0 +1,414 @@ +/* + * 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 +#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); + * + * When stopped in tracehook_report_syscall() for syscall entry or exit, + * return the address of the word the in struct pt_regs that holds the + * syscall number, and the word that holds the return value. These can be + * changed at entry to change the syscall that will be attempted, and + * at exit to change the results that will be seen by the thread. + * + * long *tracehook_syscall_callno(struct pt_regs *regs); + * long *tracehook_syscall_retval(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. + */ +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 __user *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; +} + + +/* + * 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; +} + +/* + * Return nonzero if the current task is expected to want breakpoint + * insertion in its memory at some point. A zero return is no guarantee + * it won't be done, but this is a hint that it's known to be likely. + * May be called with tsk->mm->mmap_sem held for writing. + */ +static inline int tracehook_expect_breakpoints(struct task_struct *tsk) +{ + 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) +{ +} + +/* + * This is called when tracehook_inhibit_wait_zombie(p) returned true + * and a previously delayed group_leader is now eligible for reaping. + * It's called from release_task, with no locks held, and p is not current. + */ +static inline void tracehook_report_delayed_group_leader(struct task_struct *p) +{ +} + +/* + * 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 /* */ --- linux-2.6/include/asm-powerpc/tracehook.h +++ linux-2.6/include/asm-powerpc/tracehook.h @@ -0,0 +1,49 @@ +/* + * 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) + +void tracehook_enable_single_step(struct task_struct *task); +void tracehook_disable_single_step(struct task_struct *task); + +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->gpr[0] = -1L; +} + + +#endif --- linux-2.6/include/asm-x86/thread_info_32.h +++ linux-2.6/include/asm-x86/thread_info_32.h @@ -128,7 +128,6 @@ static inline struct thread_info *curren #define TIF_NEED_RESCHED 2 /* rescheduling necessary */ #define TIF_SINGLESTEP 3 /* restore singlestep on return to user mode */ #define TIF_IRET 4 /* return with iret */ -#define TIF_SYSCALL_EMU 5 /* syscall emulation active */ #define TIF_SYSCALL_AUDIT 6 /* syscall auditing active */ #define TIF_SECCOMP 7 /* secure computing */ #define TIF_RESTORE_SIGMASK 8 /* restore signal mask in do_signal() */ @@ -137,13 +136,13 @@ static inline struct thread_info *curren #define TIF_IO_BITMAP 18 /* uses I/O bitmap */ #define TIF_FREEZE 19 /* is freezing for suspend */ #define TIF_NOTSC 20 /* TSC is not accessible in userland */ +#define TIF_FORCED_TF 21 /* 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/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); +} + +#ifdef CONFIG_X86_64 + +#define tracehook_syscall_callno(regs) (&(regs)->orig_rax) +#define tracehook_syscall_retval(regs) (&(regs)->rax) +static inline void tracehook_abort_syscall(struct pt_regs *regs) +{ + regs->orig_rax = -1L; +} +#else + +#define tracehook_syscall_callno(regs) (&(regs)->orig_eax) +#define tracehook_syscall_retval(regs) (&(regs)->eax) +static inline void tracehook_abort_syscall(struct pt_regs *regs) +{ + regs->orig_eax = -1; +} + +#endif + +#endif --- linux-2.6/include/asm-x86/signal.h +++ linux-2.6/include/asm-x86/signal.h @@ -247,10 +247,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) #else /* __i386__ */ --- linux-2.6/drivers/connector/cn_proc.c +++ linux-2.6/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; --- linux-2.6/mm/nommu.c +++ linux-2.6/mm/nommu.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -706,7 +706,7 @@ static unsigned long determine_vm_flags( * it's being traced - otherwise breakpoints set in it may interfere * with another untraced process */ - if ((flags & MAP_PRIVATE) && (current->ptrace & PT_PTRACED)) + if ((flags & MAP_PRIVATE) && tracehook_expect_breakpoints(current)) vm_flags &= ~VM_MAYSHARE; return vm_flags;