X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fum%2Fkernel%2Fptrace.c;h=9a77fb3c269d667d122c628fec3dd90d8fc4222a;hb=refs%2Fheads%2Fvserver;hp=0005a9b8489639813c2f6db98ff809a6d2ae1690;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c index 0005a9b84..9a77fb3c2 100644 --- a/arch/um/kernel/ptrace.c +++ b/arch/um/kernel/ptrace.c @@ -9,65 +9,44 @@ #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 "ptrace_user.h" +#include "skas_ptrace.h" +#include "sysdep/ptrace.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 +} /* * Called by kernel/ptrace.c when detaching.. */ void ptrace_disable(struct task_struct *child) { + set_singlestepping(child,0); } -int sys_ptrace(long request, long pid, long addr, long data) +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) { - struct task_struct *child; int i, ret; - - lock_kernel(); - ret = -EPERM; - if (request == PTRACE_TRACEME) { - /* are we already being traced? */ - if (current->ptrace & PT_PTRACED) - goto out; - - ret = security_ptrace(current->parent, current); - if (ret) - goto out; - - /* set the ptrace bit in the process flags. */ - current->ptrace |= PT_PTRACED; - ret = 0; - goto out; - } - ret = -ESRCH; - read_lock(&tasklist_lock); - child = find_task_by_pid(pid); - if (child) - get_task_struct(child); - read_unlock(&tasklist_lock); - if (!child) - goto out; - if (!vx_check(vx_task_xid(child), VX_WATCH|VX_IDENT)) - goto out_tsk; - - ret = -EPERM; - if (pid == 1) /* you may not mess with init */ - goto out_tsk; - - if (request == PTRACE_ATTACH) { - ret = ptrace_attach(child); - goto out_tsk; - } - - ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (ret < 0) - goto out_tsk; + unsigned long __user *p = (void __user *)(unsigned long)data; switch (request) { /* when I and D space are separate, these will need to be fixed. */ @@ -80,31 +59,14 @@ int sys_ptrace(long request, long pid, long addr, long data) copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); if (copied != sizeof(tmp)) break; - ret = put_user(tmp,(unsigned long *) data); + ret = put_user(tmp, p); break; } /* read the word at location addr in the USER area. */ - case PTRACE_PEEKUSR: { - unsigned long tmp; - - ret = -EIO; - if ((addr & 3) || addr < 0) - break; - - tmp = 0; /* Default return condition */ - if(addr < FRAME_SIZE_OFFSET){ - tmp = getreg(child, addr); - } - else if((addr >= offsetof(struct user, u_debugreg[0])) && - (addr <= offsetof(struct user, u_debugreg[7]))){ - addr -= offsetof(struct user, u_debugreg[0]); - addr = addr >> 2; - tmp = child->thread.arch.debugregs[addr]; - } - ret = put_user(tmp, (unsigned long *) data); - break; - } + 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. */ @@ -117,30 +79,16 @@ int sys_ptrace(long request, long pid, long addr, long data) break; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ - ret = -EIO; - if ((addr & 3) || addr < 0) - break; - - if (addr < FRAME_SIZE_OFFSET) { - ret = putreg(child, addr, data); - break; - } - else if((addr >= offsetof(struct user, u_debugreg[0])) && - (addr <= offsetof(struct user, u_debugreg[7]))){ - addr -= offsetof(struct user, u_debugreg[0]); - addr = addr >> 2; - if((addr == 4) || (addr == 5)) break; - child->thread.arch.debugregs[addr] = data; - ret = 0; - } - - break; + 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 ((unsigned long) data > _NSIG) + if (!valid_signal(data)) break; + + set_singlestepping(child, 0); if (request == PTRACE_SYSCALL) { set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); } @@ -160,8 +108,10 @@ int sys_ptrace(long request, long pid, long addr, long data) */ case PTRACE_KILL: { ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; + + set_singlestepping(child, 0); child->exit_code = SIGKILL; wake_up_process(child); break; @@ -169,10 +119,10 @@ int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_SINGLESTEP: { /* set the trap flag. */ ret = -EIO; - if ((unsigned long) data > _NSIG) + if (!valid_signal(data)) break; clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - child->ptrace |= PT_DTRACE; + set_singlestepping(child, 1); child->exit_code = data; /* give it a chance to run. */ wake_up_process(child); @@ -187,14 +137,13 @@ int sys_ptrace(long request, long pid, long addr, long data) #ifdef PTRACE_GETREGS case PTRACE_GETREGS: { /* Get all gp regs from the child. */ - if (!access_ok(VERIFY_WRITE, (unsigned long *)data, - FRAME_SIZE_OFFSET)) { + if (!access_ok(VERIFY_WRITE, p, MAX_REG_OFFSET)) { ret = -EIO; break; } - for ( i = 0; i < FRAME_SIZE_OFFSET; i += sizeof(long) ) { - __put_user(getreg(child, i), (unsigned long *) data); - data += sizeof(long); + for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { + __put_user(getreg(child, i), p); + p++; } ret = 0; break; @@ -203,15 +152,14 @@ int sys_ptrace(long request, long pid, long addr, long data) #ifdef PTRACE_SETREGS case PTRACE_SETREGS: { /* Set all gp regs in the child. */ unsigned long tmp = 0; - if (!access_ok(VERIFY_READ, (unsigned *)data, - FRAME_SIZE_OFFSET)) { + if (!access_ok(VERIFY_READ, p, MAX_REG_OFFSET)) { ret = -EIO; break; } - for ( i = 0; i < FRAME_SIZE_OFFSET; i += sizeof(long) ) { - __get_user(tmp, (unsigned long *) data); + for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { + __get_user(tmp, p); putreg(child, i, tmp); - data += sizeof(long); + p++; } ret = 0; break; @@ -237,29 +185,33 @@ int sys_ptrace(long request, long pid, long addr, long data) ret = set_fpxregs(data, child); break; #endif - case PTRACE_FAULTINFO: { - struct ptrace_faultinfo fault; + 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; - fault = ((struct ptrace_faultinfo) - { .is_write = child->thread.err, - .addr = child->thread.cr2 }); - ret = copy_to_user((unsigned long *) data, &fault, - sizeof(fault)); + 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; } - case PTRACE_SIGPENDING: - ret = copy_to_user((unsigned long *) data, - &child->pending.signal, - sizeof(child->pending.signal)); - break; +#ifdef PTRACE_LDT case PTRACE_LDT: { struct ptrace_ldt ldt; - if(copy_from_user(&ldt, (unsigned long *) data, - sizeof(ldt))){ + if(copy_from_user(&ldt, p, sizeof(ldt))){ ret = -EIO; break; } @@ -270,6 +222,7 @@ int sys_ptrace(long request, long pid, long addr, long data) ret = -EIO; break; } +#endif #ifdef CONFIG_PROC_MM case PTRACE_SWITCH_MM: { struct mm_struct *old = child->mm; @@ -292,36 +245,65 @@ int sys_ptrace(long request, long pid, long addr, long data) ret = ptrace_request(child, request, addr, data); break; } - out_tsk: - put_task_struct(child); - out: - unlock_kernel(); + return ret; } +void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs, + int error_code) +{ + struct siginfo info; + + memset(&info, 0, sizeof(info)); + info.si_signo = SIGTRAP; + info.si_code = TRAP_BRKPT; + + /* User-mode eip? */ + info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(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 + */ void syscall_trace(union uml_pt_regs *regs, int entryexit) { + int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit; + int tracesysgood; + if (unlikely(current->audit_context)) { if (!entryexit) - audit_syscall_entry(current, regs->orig_eax, - regs->ebx, regs->ecx, - regs->edx, regs->esi); - else - audit_syscall_exit(current, regs->eax); + 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 */ - ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0)); + 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 + /* 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 */ @@ -330,14 +312,3 @@ void syscall_trace(union uml_pt_regs *regs, int entryexit) current->exit_code = 0; } } - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */