[PATCH 1d] utrace: tracehook for UML This is the tracehook part of the UML utrace work, enough to get UML building with the utrace prep patches applied. Checks of task->ptrace & PT_DTRACE were replaced with test_thread_flag(TIF_SINGLESTEP, or removed, in the case of execve. Most of arch/um/kernel/ptrace.c is gone, to be reinstated in future utrace work. Similarly, calls to syscall_trace and ptrace notifications in the signal delivery code are gone. Signed-off-by: Jeff Dike Signed-off-by: Roland McGrath --- include/asm-um/ptrace-i386.h | 2 include/asm-um/thread_info.h | 3 include/asm-um/ptrace-x86_64.h | 2 include/asm-um/tracehook.h | 64 ++++++++ include/asm-um/ptrace-generic.h | 3 arch/um/kernel/skas/syscall.c | 4 - arch/um/kernel/exec.c | 1 arch/um/kernel/process.c | 6 - arch/um/kernel/signal.c | 5 + arch/um/kernel/ptrace.c | 312 ++++----------------------------------- arch/um/sys-i386/signal.c | 4 - 11 files changed, 108 insertions(+), 298 deletions(-) create include/asm-um/tracehook.h --- linux-2.6/include/asm-um/ptrace-i386.h +++ linux-2.6/include/asm-um/ptrace-i386.h @@ -6,6 +6,8 @@ #ifndef __UM_PTRACE_I386_H #define __UM_PTRACE_I386_H +#define ARCH_HAS_SINGLE_STEP (1) + #define HOST_AUDIT_ARCH AUDIT_ARCH_I386 #include "linux/compiler.h" --- linux-2.6/include/asm-um/thread_info.h +++ linux-2.6/include/asm-um/thread_info.h @@ -69,6 +69,8 @@ static inline struct thread_info *curren #define TIF_MEMDIE 5 #define TIF_SYSCALL_AUDIT 6 #define TIF_RESTORE_SIGMASK 7 +#define TIF_SINGLESTEP 8 /* restore singlestep on return to user + * mode */ #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) @@ -77,5 +79,6 @@ static inline struct thread_info *curren #define _TIF_MEMDIE (1 << TIF_MEMDIE) #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) #define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK) +#define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP) #endif --- linux-2.6/include/asm-um/ptrace-x86_64.h +++ linux-2.6/include/asm-um/ptrace-x86_64.h @@ -14,6 +14,8 @@ #define __FRAME_OFFSETS /* Needed to get the R* macros */ #include "asm/ptrace-generic.h" +#define ARCH_HAS_SINGLE_STEP (1) + #define HOST_AUDIT_ARCH AUDIT_ARCH_X86_64 /* Also defined in sysdep/ptrace.h, so may already be defined. */ --- linux-2.6/include/asm-um/tracehook.h +++ linux-2.6/include/asm-um/tracehook.h @@ -0,0 +1,64 @@ +/* + * Tracing hooks, i386 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. + * + * Munged for UML - jdike@{addtoit,linux.intel}.com + */ + +#ifndef _ASM_TRACEHOOK_H +#define _ASM_TRACEHOOK_H 1 + +#include +#include +#include + +/* + * See linux/tracehook.h for the descriptions of what these need to do. + */ + +static inline void tracehook_enable_single_step(struct task_struct *tsk) +{ + set_tsk_thread_flag(tsk, TIF_SINGLESTEP); +} + +static inline void tracehook_disable_single_step(struct task_struct *tsk) +{ + clear_tsk_thread_flag(tsk, TIF_SINGLESTEP); +} + +static inline int tracehook_single_step_enabled(struct task_struct *tsk) +{ + return test_tsk_thread_flag(tsk, TIF_SINGLESTEP); +} + +static inline void tracehook_enable_syscall_trace(struct task_struct *tsk) +{ + set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE); +} + +static inline void tracehook_disable_syscall_trace(struct task_struct *tsk) +{ + clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE); +} + +static inline void tracehook_abort_syscall(struct pt_regs *regs) +{ + PT_REGS_SYSCALL_NR(regs) = -1; +} + +extern const struct utrace_regset_view utrace_um_native; +static inline const struct utrace_regset_view * +utrace_native_view(struct task_struct *tsk) +{ + return &utrace_um_native; +} + + +#endif --- linux-2.6/include/asm-um/ptrace-generic.h +++ linux-2.6/include/asm-um/ptrace-generic.h @@ -44,9 +44,6 @@ extern int set_fpxregs(unsigned long buf extern void show_regs(struct pt_regs *regs); -extern void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs, - int error_code); - extern int arch_copy_tls(struct task_struct *new); extern void clear_flushed_tls(struct task_struct *task); --- linux-2.6/arch/um/kernel/skas/syscall.c +++ linux-2.6/arch/um/kernel/skas/syscall.c @@ -19,8 +19,6 @@ void handle_syscall(union uml_pt_regs *r long result; int syscall; - syscall_trace(r, 0); - current->thread.nsyscalls++; nsyscalls++; @@ -38,6 +36,4 @@ void handle_syscall(union uml_pt_regs *r else result = EXECUTE_SYSCALL(syscall, regs); REGS_SET_SYSCALL_RETURN(r->skas.regs, result); - - syscall_trace(r, 1); } --- linux-2.6/arch/um/kernel/exec.c +++ linux-2.6/arch/um/kernel/exec.c @@ -51,7 +51,6 @@ static long execve1(char *file, char __u error = do_execve(file, argv, env, ¤t->thread.regs); if (error == 0){ task_lock(current); - current->ptrace &= ~PT_DTRACE; #ifdef SUBARCH_EXECVE1 SUBARCH_EXECVE1(¤t->thread.regs.regs); #endif --- linux-2.6/arch/um/kernel/process.c +++ linux-2.6/arch/um/kernel/process.c @@ -458,11 +458,11 @@ int singlestepping(void * t) { struct task_struct *task = t ? t : current; - if ( ! (task->ptrace & PT_DTRACE) ) - return(0); + if (!test_thread_flag(TIF_SINGLESTEP)) + return 0; if (task->thread.singlestep_syscall) - return(1); + return 1; return 2; } --- linux-2.6/arch/um/kernel/signal.c +++ linux-2.6/arch/um/kernel/signal.c @@ -14,6 +14,7 @@ #include "linux/tty.h" #include "linux/binfmts.h" #include "linux/ptrace.h" +#include "linux/tracehook.h" #include "asm/signal.h" #include "asm/uaccess.h" #include "asm/unistd.h" @@ -93,6 +94,8 @@ static int handle_signal(struct pt_regs sigaddset(¤t->blocked, signr); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); + + tracehook_report_handle_signal(signr, ka, oldset, regs); } return err; @@ -148,7 +151,7 @@ static int kern_do_signal(struct pt_regs * on the host. The tracing thread will check this flag and * PTRACE_SYSCALL if necessary. */ - if(current->ptrace & PT_DTRACE) + if(test_thread_flag(TIF_SYSCALL_TRACE)) current->thread.singlestep_syscall = is_syscall(PT_REGS_IP(¤t->thread.regs)); --- linux-2.6/arch/um/kernel/ptrace.c +++ linux-2.6/arch/um/kernel/ptrace.c @@ -3,261 +3,26 @@ * Licensed under the GPL */ -#include "linux/sched.h" -#include "linux/mm.h" -#include "linux/errno.h" -#include "linux/smp_lock.h" -#include "linux/security.h" -#include "linux/ptrace.h" -#include "linux/audit.h" -#ifdef CONFIG_PROC_MM -#include "linux/proc_mm.h" -#endif -#include "asm/ptrace.h" -#include "asm/uaccess.h" -#include "kern_util.h" -#include "skas_ptrace.h" -#include "sysdep/ptrace.h" -#include "os.h" - -static inline void set_singlestepping(struct task_struct *child, int on) -{ - if (on) - child->ptrace |= PT_DTRACE; - else - child->ptrace &= ~PT_DTRACE; - child->thread.singlestep_syscall = 0; - -#ifdef SUBARCH_SET_SINGLESTEPPING - SUBARCH_SET_SINGLESTEPPING(child, on); -#endif -} +#include +#include +#include +#include +#include /* * Called by kernel/ptrace.c when detaching.. */ void ptrace_disable(struct task_struct *child) { - set_singlestepping(child,0); } -extern int peek_user(struct task_struct * child, long addr, long data); -extern int poke_user(struct task_struct * child, long addr, long data); - long arch_ptrace(struct task_struct *child, long request, long addr, long data) { - int i, ret; - unsigned long __user *p = (void __user *)(unsigned long)data; - - switch (request) { - /* when I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - ret = -EIO; - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp, p); - break; - } - - /* read the word at location addr in the USER area. */ - case PTRACE_PEEKUSR: - ret = peek_user(child, addr, data); - break; - - /* when I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: - ret = -EIO; - if (access_process_vm(child, addr, &data, sizeof(data), - 1) != sizeof(data)) - break; - ret = 0; - break; - - case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ - ret = poke_user(child, addr, data); - break; - - case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: { /* restart after signal. */ - ret = -EIO; - if (!valid_signal(data)) - break; - - set_singlestepping(child, 0); - if (request == PTRACE_SYSCALL) { - set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - } - else { - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - } - child->exit_code = data; - wake_up_process(child); - ret = 0; - break; - } - -/* - * make the child exit. Best I can do is send it a sigkill. - * perhaps it should be put in the status that it wants to - * exit. - */ - case PTRACE_KILL: { - ret = 0; - if (child->exit_state == EXIT_ZOMBIE) /* already dead */ - break; - - set_singlestepping(child, 0); - child->exit_code = SIGKILL; - wake_up_process(child); - break; - } - - case PTRACE_SINGLESTEP: { /* set the trap flag. */ - ret = -EIO; - if (!valid_signal(data)) - break; - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - set_singlestepping(child, 1); - child->exit_code = data; - /* give it a chance to run. */ - wake_up_process(child); - ret = 0; - break; - } - - case PTRACE_DETACH: - /* detach a process that was attached. */ - ret = ptrace_detach(child, data); - break; - -#ifdef PTRACE_GETREGS - case PTRACE_GETREGS: { /* Get all gp regs from the child. */ - if (!access_ok(VERIFY_WRITE, p, MAX_REG_OFFSET)) { - ret = -EIO; - break; - } - for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { - __put_user(getreg(child, i), p); - p++; - } - ret = 0; - break; - } -#endif -#ifdef PTRACE_SETREGS - case PTRACE_SETREGS: { /* Set all gp regs in the child. */ - unsigned long tmp = 0; - if (!access_ok(VERIFY_READ, p, MAX_REG_OFFSET)) { - ret = -EIO; - break; - } - for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { - __get_user(tmp, p); - putreg(child, i, tmp); - p++; - } - ret = 0; - break; - } -#endif -#ifdef PTRACE_GETFPREGS - case PTRACE_GETFPREGS: /* Get the child FPU state. */ - ret = get_fpregs(data, child); - break; -#endif -#ifdef PTRACE_SETFPREGS - case PTRACE_SETFPREGS: /* Set the child FPU state. */ - ret = set_fpregs(data, child); - break; -#endif -#ifdef PTRACE_GETFPXREGS - case PTRACE_GETFPXREGS: /* Get the child FPU state. */ - ret = get_fpxregs(data, child); - break; -#endif -#ifdef PTRACE_SETFPXREGS - case PTRACE_SETFPXREGS: /* Set the child FPU state. */ - ret = set_fpxregs(data, child); - break; -#endif - case PTRACE_GET_THREAD_AREA: - ret = ptrace_get_thread_area(child, addr, - (struct user_desc __user *) data); - break; - - case PTRACE_SET_THREAD_AREA: - ret = ptrace_set_thread_area(child, addr, - (struct user_desc __user *) data); - break; - - case PTRACE_FAULTINFO: { - /* Take the info from thread->arch->faultinfo, - * but transfer max. sizeof(struct ptrace_faultinfo). - * On i386, ptrace_faultinfo is smaller! - */ - ret = copy_to_user(p, &child->thread.arch.faultinfo, - sizeof(struct ptrace_faultinfo)); - if(ret) - break; - break; - } - -#ifdef PTRACE_LDT - case PTRACE_LDT: { - struct ptrace_ldt ldt; - - if(copy_from_user(&ldt, p, sizeof(ldt))){ - ret = -EIO; - break; - } - - /* This one is confusing, so just punt and return -EIO for - * now - */ - ret = -EIO; - break; - } -#endif -#ifdef CONFIG_PROC_MM - case PTRACE_SWITCH_MM: { - struct mm_struct *old = child->mm; - struct mm_struct *new = proc_mm_get_mm(data); - - if(IS_ERR(new)){ - ret = PTR_ERR(new); - break; - } - - atomic_inc(&new->mm_users); - child->mm = new; - child->active_mm = new; - mmput(old); - ret = 0; - break; - } -#endif -#ifdef PTRACE_ARCH_PRCTL - case PTRACE_ARCH_PRCTL: - /* XXX Calls ptrace on the host - needs some SMP thinking */ - ret = arch_prctl_skas(child, data, (void *) addr); - break; -#endif - default: - ret = ptrace_request(child, request, addr, data); - break; - } - - return ret; + return -ENOSYS; } -void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs, - int error_code) +static void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, + int error_code) { struct siginfo info; @@ -266,56 +31,39 @@ void send_sigtrap(struct task_struct *ts info.si_code = TRAP_BRKPT; /* User-mode eip? */ - info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL; + info.si_addr = UPT_IS_USER(®s->regs) ? + (void __user *) UPT_IP(®s->regs) : NULL; /* Send us the fakey SIGTRAP */ force_sig_info(SIGTRAP, &info, tsk); } -/* XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and - * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check +/* notification of system call entry/exit + * - triggered by current->work.syscall_trace */ -void syscall_trace(union uml_pt_regs *regs, int entryexit) +void do_syscall_trace(struct pt_regs *regs, int entryexit) { - int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit; - int tracesysgood; + /* do the secure computing check first */ + if (!entryexit) + secure_computing(PT_REGS_SYSCALL_NR(regs)); - if (unlikely(current->audit_context)) { - if (!entryexit) - audit_syscall_entry(HOST_AUDIT_ARCH, + if (unlikely(current->audit_context) && entryexit) + audit_syscall_exit(AUDITSC_RESULT(UPT_SYSCALL_RET(regs)), + UPT_SYSCALL_RET(regs)); + + if (test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall(regs, entryexit); + + if (test_thread_flag(TIF_SINGLESTEP) && entryexit) { + send_sigtrap(current, regs, 0); /* XXX */ + tracehook_report_syscall_step(regs); + } + + if (unlikely(current->audit_context) && !entryexit) + audit_syscall_entry(HOST_AUDIT_ARCH, UPT_SYSCALL_NR(regs), UPT_SYSCALL_ARG1(regs), UPT_SYSCALL_ARG2(regs), UPT_SYSCALL_ARG3(regs), UPT_SYSCALL_ARG4(regs)); - else audit_syscall_exit(AUDITSC_RESULT(UPT_SYSCALL_RET(regs)), - UPT_SYSCALL_RET(regs)); - } - - /* Fake a debug trap */ - if (is_singlestep) - send_sigtrap(current, regs, 0); - - if (!test_thread_flag(TIF_SYSCALL_TRACE)) - return; - - if (!(current->ptrace & PT_PTRACED)) - return; - - /* the 0x80 provides a way for the tracing parent to distinguish - between a syscall stop and SIGTRAP delivery */ - tracesysgood = (current->ptrace & PT_TRACESYSGOOD); - ptrace_notify(SIGTRAP | (tracesysgood ? 0x80 : 0)); - - if (entryexit) /* force do_signal() --> is_syscall() */ - set_thread_flag(TIF_SIGPENDING); - - /* 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; - } } --- linux-2.6/arch/um/sys-i386/signal.c +++ linux-2.6/arch/um/sys-i386/signal.c @@ -267,8 +267,6 @@ int setup_signal_stack_sc(unsigned long PT_REGS_EDX(regs) = (unsigned long) 0; PT_REGS_ECX(regs) = (unsigned long) 0; - if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED)) - ptrace_notify(SIGTRAP); return 0; err: @@ -324,8 +322,6 @@ int setup_signal_stack_si(unsigned long PT_REGS_EDX(regs) = (unsigned long) &frame->info; PT_REGS_ECX(regs) = (unsigned long) &frame->uc; - if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED)) - ptrace_notify(SIGTRAP); return 0; err: