X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fx86_64%2Fkernel%2Fptrace.c;h=280bc88f2c9b973a12abed8c89b428a032ed4b54;hb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;hp=a97bee6f29fa1cae2fbb53e17501509036b19e68;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c index a97bee6f2..280bc88f2 100644 --- a/arch/x86_64/kernel/ptrace.c +++ b/arch/x86_64/kernel/ptrace.c @@ -13,10 +13,14 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include #include #include @@ -28,15 +32,19 @@ #include #include #include +#include /* * does not yet catch signals sent when the child dies. * in exit.c or in signal.c. */ -/* determines which flags the user has access to. */ -/* 1 = access 0 = no access */ -#define FLAG_MASK 0x44dd5UL +/* + * Determines which flags the user has access to [1 = access, 0 = no access]. + * Prohibits changing ID(21), VIP(20), VIF(19), VM(17), IOPL(12-13), IF(9). + * Also masks reserved bits (63-22, 15, 5, 3, 1). + */ +#define FLAG_MASK 0x54dd5UL /* set's the trap flag. */ #define TRAP_FLAG 0x100UL @@ -79,6 +87,129 @@ static inline long put_stack_long(struct task_struct *task, int offset, return 0; } +#define LDT_SEGMENT 4 + +unsigned long convert_rip_to_linear(struct task_struct *child, struct pt_regs *regs) +{ + unsigned long addr, seg; + + addr = regs->rip; + seg = regs->cs & 0xffff; + + /* + * We'll assume that the code segments in the GDT + * are all zero-based. That is largely true: the + * TLS segments are used for data, and the PNPBIOS + * and APM bios ones we just ignore here. + */ + if (seg & LDT_SEGMENT) { + u32 *desc; + unsigned long base; + + down(&child->mm->context.sem); + desc = child->mm->context.ldt + (seg & ~7); + base = (desc[0] >> 16) | ((desc[1] & 0xff) << 16) | (desc[1] & 0xff000000); + + /* 16-bit code segment? */ + if (!((desc[1] >> 22) & 1)) + addr &= 0xffff; + addr += base; + up(&child->mm->context.sem); + } + return addr; +} + +static int is_at_popf(struct task_struct *child, struct pt_regs *regs) +{ + int i, copied; + unsigned char opcode[16]; + unsigned long addr = convert_rip_to_linear(child, regs); + + copied = access_process_vm(child, addr, opcode, sizeof(opcode), 0); + for (i = 0; i < copied; i++) { + switch (opcode[i]) { + /* popf */ + case 0x9d: + return 1; + + /* CHECKME: 64 65 */ + + /* opcode and address size prefixes */ + case 0x66: case 0x67: + continue; + /* irrelevant prefixes (segment overrides and repeats) */ + case 0x26: case 0x2e: + case 0x36: case 0x3e: + case 0x64: case 0x65: + case 0xf0: case 0xf2: case 0xf3: + continue; + + /* REX prefixes */ + case 0x40 ... 0x4f: + continue; + + /* CHECKME: f0, f2, f3 */ + + /* + * pushf: NOTE! We should probably not let + * the user see the TF bit being set. But + * it's more pain than it's worth to avoid + * it, and a debugger could emulate this + * all in user space if it _really_ cares. + */ + case 0x9c: + default: + return 0; + } + } + return 0; +} + +void tracehook_enable_single_step(struct task_struct *child) +{ + struct pt_regs *regs = task_pt_regs(child); + + /* + * Always set TIF_SINGLESTEP - this guarantees that + * we single-step system calls etc.. This will also + * cause us to set TF when returning to user mode. + */ + set_tsk_thread_flag(child, TIF_SINGLESTEP); + + /* + * If TF was already set, don't do anything else + */ + if (regs->eflags & TRAP_FLAG) + return; + + /* Set TF on the kernel stack.. */ + regs->eflags |= TRAP_FLAG; + + /* + * ..but if TF is changed by the instruction we will trace, + * don't mark it as being "us" that set it, so that we + * won't clear it by hand later. + * + * AK: this is not enough, LAHF and IRET can change TF in user space too. + */ + if (is_at_popf(child, regs)) + return; + + set_tsk_thread_flag(child, TIF_FORCED_TF); +} + +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 (test_and_clear_tsk_thread_flag(child, TIF_FORCED_TF)) { + struct pt_regs *regs = task_pt_regs(child); + regs->eflags &= ~TRAP_FLAG; + } +} + /* * Called by kernel/ptrace.c when detaching.. * @@ -86,10 +217,7 @@ static inline long put_stack_long(struct task_struct *task, int offset, */ void ptrace_disable(struct task_struct *child) { - long tmp; - - tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; - put_stack_long(child, EFL_OFFSET, tmp); + tracehook_disable_single_step(child); } static int putreg(struct task_struct *child, @@ -128,13 +256,13 @@ static int putreg(struct task_struct *child, value &= 0xffff; return 0; case offsetof(struct user_regs_struct,fs_base): - if (!((value >> 48) == 0 || (value >> 48) == 0xffff)) - return -EIO; + if (value >= TASK_SIZE_OF(child)) + return -EIO; child->thread.fs = value; return 0; case offsetof(struct user_regs_struct,gs_base): - if (!((value >> 48) == 0 || (value >> 48) == 0xffff)) - return -EIO; + if (value >= TASK_SIZE_OF(child)) + return -EIO; child->thread.gs = value; return 0; case offsetof(struct user_regs_struct, eflags): @@ -142,6 +270,7 @@ static int putreg(struct task_struct *child, tmp = get_stack_long(child, EFL_OFFSET); tmp &= ~FLAG_MASK; value |= tmp; + clear_tsk_thread_flag(child, TIF_FORCED_TF); break; case offsetof(struct user_regs_struct,cs): if ((value & 3) != 3) @@ -174,360 +303,457 @@ static unsigned long getreg(struct task_struct *child, unsigned long regno) val = get_stack_long(child, regno); if (test_tsk_thread_flag(child, TIF_IA32)) val &= 0xffffffff; + if (regno == (offsetof(struct user_regs_struct, eflags) + - sizeof(struct pt_regs)) + && test_tsk_thread_flag(child, TIF_FORCED_TF)) + val &= ~X86_EFLAGS_TF; return val; } } -asmlinkage long sys_ptrace(long request, long pid, unsigned long addr, long data) +static int +genregs_get(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) { - struct task_struct *child; - long i, ret; - unsigned ui; - - /* This lock_kernel fixes a subtle race with suid exec */ - 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; + if (kbuf) { + unsigned long *kp = kbuf; + while (count > 0) { + *kp++ = getreg(target, pos); + pos += sizeof(long); + count -= sizeof(long); + } } - 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; - - 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; + else { + unsigned long __user *up = ubuf; + while (count > 0) { + if (__put_user(getreg(target, pos), up++)) + return -EFAULT; + pos += sizeof(long); + count -= sizeof(long); + } } - ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (ret < 0) - goto out_tsk; - - switch (request) { - /* when I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp,(unsigned long *) data); - break; + + return 0; +} + +static int +genregs_set(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret = 0; + + if (kbuf) { + const unsigned long *kp = kbuf; + while (!ret && count > 0) { + ret = putreg(target, pos, *kp++); + pos += sizeof(long); + count -= sizeof(long); + } + } + else { + int ret = 0; + const unsigned long __user *up = ubuf; + while (!ret && count > 0) { + unsigned long val; + ret = __get_user(val, up++); + if (!ret) + ret = putreg(target, pos, val); + pos += sizeof(long); + count -= sizeof(long); + } } - /* read the word at location addr in the USER area. */ - case PTRACE_PEEKUSR: { - unsigned long tmp; + return ret; +} - ret = -EIO; - if ((addr & 7) || - addr > sizeof(struct user) - 7) - break; - switch (addr) { - case 0 ... sizeof(struct user_regs_struct): - tmp = getreg(child, addr); - break; - case offsetof(struct user, u_debugreg[0]): - tmp = child->thread.debugreg0; - break; - case offsetof(struct user, u_debugreg[1]): - tmp = child->thread.debugreg1; - break; - case offsetof(struct user, u_debugreg[2]): - tmp = child->thread.debugreg2; - break; - case offsetof(struct user, u_debugreg[3]): - tmp = child->thread.debugreg3; - break; - case offsetof(struct user, u_debugreg[6]): - tmp = child->thread.debugreg6; - break; - case offsetof(struct user, u_debugreg[7]): - tmp = child->thread.debugreg7; - break; - default: - tmp = 0; - break; +static int +dbregs_active(struct task_struct *tsk, const struct utrace_regset *regset) +{ + if (tsk->thread.debugreg6 | tsk->thread.debugreg7) + return 8; + return 0; +} + +static int +dbregs_get(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + for (pos >>= 3, count >>= 3; count > 0; --count, ++pos) { + unsigned long val; + + /* + * The hardware updates the status register on a debug trap, + * but do_debug (traps.c) saves it for us when that happens. + * So whether the target is current or not, debugregN is good. + */ + val = 0; + switch (pos) { + case 0: val = target->thread.debugreg0; break; + case 1: val = target->thread.debugreg1; break; + case 2: val = target->thread.debugreg2; break; + case 3: val = target->thread.debugreg3; break; + case 6: val = target->thread.debugreg6; break; + case 7: val = target->thread.debugreg7; break; + } + + if (kbuf) { + *(unsigned long *) kbuf = val; + kbuf += sizeof(unsigned long); + } + else { + if (__put_user(val, (unsigned long __user *) ubuf)) + return -EFAULT; + ubuf += sizeof(unsigned long); } - ret = put_user(tmp,(unsigned long *) data); - break; } - /* when I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) - break; - ret = -EIO; - break; + return 0; +} - case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ - ret = -EIO; - if ((addr & 7) || - addr > sizeof(struct user) - 7) - break; +static int +dbregs_set(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long maxaddr = TASK_SIZE_OF(target); + maxaddr -= test_tsk_thread_flag(target, TIF_IA32) ? 3 : 7; + + for (pos >>= 3, count >>= 3; count > 0; --count, ++pos) { + unsigned long val; + unsigned int i; - switch (addr) { - case 0 ... sizeof(struct user_regs_struct): - ret = putreg(child, addr, data); + if (kbuf) { + val = *(const unsigned long *) kbuf; + kbuf += sizeof(unsigned long); + } + else { + if (__get_user(val, (unsigned long __user *) ubuf)) + return -EFAULT; + ubuf += sizeof(unsigned long); + } + + switch (pos) { +#define SET_DBREG(n) \ + target->thread.debugreg##n = val; \ + if (target == current) \ + set_debugreg(target->thread.debugreg##n, n) + + case 0: + if (val >= maxaddr) + return -EIO; + SET_DBREG(0); break; - /* Disallows to set a breakpoint into the vsyscall */ - case offsetof(struct user, u_debugreg[0]): - if (data >= TASK_SIZE-7) break; - child->thread.debugreg0 = data; - ret = 0; + case 1: + if (val >= maxaddr) + return -EIO; + SET_DBREG(1); break; - case offsetof(struct user, u_debugreg[1]): - if (data >= TASK_SIZE-7) break; - child->thread.debugreg1 = data; - ret = 0; + case 2: + if (val >= maxaddr) + return -EIO; + SET_DBREG(2); break; - case offsetof(struct user, u_debugreg[2]): - if (data >= TASK_SIZE-7) break; - child->thread.debugreg2 = data; - ret = 0; + case 3: + if (val >= maxaddr) + return -EIO; + SET_DBREG(3); break; - case offsetof(struct user, u_debugreg[3]): - if (data >= TASK_SIZE-7) break; - child->thread.debugreg3 = data; - ret = 0; + case 4: + case 5: + if (val != 0) + return -EIO; break; - case offsetof(struct user, u_debugreg[6]): - if (data >> 32) - break; - child->thread.debugreg6 = data; - ret = 0; + case 6: + if (val >> 32) + return -EIO; + SET_DBREG(6); break; - case offsetof(struct user, u_debugreg[7]): - data &= ~DR_CONTROL_RESERVED; - for(i=0; i<4; i++) - if ((0x5454 >> ((data >> (16 + 4*i)) & 0xf)) & 1) - break; - if (i == 4) { - child->thread.debugreg7 = data; - ret = 0; - } - break; - } - break; - case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: { /* restart after signal. */ - long tmp; - - ret = -EIO; - if ((unsigned long) data > _NSIG) + case 7: + /* + * See arch/i386/kernel/ptrace.c for an explanation + * of this awkward check. + */ + val &= ~DR_CONTROL_RESERVED; + for (i = 0; i < 4; i++) + if ((0x5554 >> ((val >> (16 + 4*i)) & 0xf)) + & 1) + return -EIO; + SET_DBREG(7); break; - if (request == PTRACE_SYSCALL) - set_tsk_thread_flag(child,TIF_SYSCALL_TRACE); - else - clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE); - child->exit_code = data; - /* make sure the single step bit is not set. */ - tmp = get_stack_long(child, EFL_OFFSET); - tmp &= ~TRAP_FLAG; - put_stack_long(child, EFL_OFFSET,tmp); - wake_up_process(child); - ret = 0; - break; +#undef SET_DBREG + } } -#ifdef CONFIG_IA32_EMULATION - /* This makes only sense with 32bit programs. Allow a - 64bit debugger to fully examine them too. Better - don't use it against 64bit processes, use - PTRACE_ARCH_PRCTL instead. */ - case PTRACE_SET_THREAD_AREA: { - int old; - get_user(old, &((struct user_desc *)data)->entry_number); - put_user(addr, &((struct user_desc *)data)->entry_number); - ret = do_set_thread_area(&child->thread, - (struct user_desc *)data); - put_user(old, &((struct user_desc *)data)->entry_number); - break; - case PTRACE_GET_THREAD_AREA: - get_user(old, &((struct user_desc *)data)->entry_number); - put_user(addr, &((struct user_desc *)data)->entry_number); - ret = do_get_thread_area(&child->thread, - (struct user_desc *)data); - put_user(old, &((struct user_desc *)data)->entry_number); - break; - } -#endif - /* normal 64bit interface to access TLS data. - Works just like arch_prctl, except that the arguments - are reversed. */ - case PTRACE_ARCH_PRCTL: - ret = do_arch_prctl(child, data, addr); - break; + return 0; +} -/* - * 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: { - long tmp; - ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ - break; - child->exit_code = SIGKILL; - /* make sure the single step bit is not set. */ - tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; - put_stack_long(child, EFL_OFFSET, tmp); - wake_up_process(child); - break; +static int +fpregs_active(struct task_struct *target, const struct utrace_regset *regset) +{ + return tsk_used_math(target) ? regset->n : 0; +} + +static int +fpregs_get(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + if (tsk_used_math(target)) { + if (target == current) + unlazy_fpu(target); } + else + init_fpu(target); - case PTRACE_SINGLESTEP: { /* set the trap flag. */ - long tmp; + return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.i387.fxsave, 0, -1); +} - ret = -EIO; - if ((unsigned long) data > _NSIG) - break; - clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE); - if ((child->ptrace & PT_DTRACE) == 0) { - /* Spurious delayed TF traps may occur */ - child->ptrace |= PT_DTRACE; - } - tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG; - put_stack_long(child, EFL_OFFSET, tmp); - child->exit_code = data; - /* give it a chance to run. */ - wake_up_process(child); - ret = 0; - break; +static int +fpregs_set(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret; + + if (tsk_used_math(target)) { + if (target == current) + unlazy_fpu(target); } + else if (pos == 0 && count == sizeof(struct user_i387_struct)) + set_stopped_child_used_math(target); + else + init_fpu(target); - case PTRACE_DETACH: - /* detach a process that was attached. */ - ret = ptrace_detach(child, data); - break; + ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.i387.fxsave, 0, -1); - case PTRACE_GETREGS: { /* Get all gp regs from the child. */ - if (!access_ok(VERIFY_WRITE, (unsigned *)data, FRAME_SIZE)) { - ret = -EIO; - break; - } - for (ui = 0; ui < sizeof(struct user_regs_struct); ui += sizeof(long)) { - __put_user(getreg(child, ui),(unsigned long *) data); - data += sizeof(long); - } - ret = 0; - break; - } + target->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask; - case PTRACE_SETREGS: { /* Set all gp regs in the child. */ - unsigned long tmp; - if (!access_ok(VERIFY_READ, (unsigned *)data, FRAME_SIZE)) { - ret = -EIO; - break; - } - for (ui = 0; ui < sizeof(struct user_regs_struct); ui += sizeof(long)) { - __get_user(tmp, (unsigned long *) data); - putreg(child, ui, tmp); - data += sizeof(long); + return ret; +} + +static int +fsgs_active(struct task_struct *tsk, const struct utrace_regset *regset) +{ + if (tsk->thread.gsindex == GS_TLS_SEL || tsk->thread.gs) + return 2; + if (tsk->thread.fsindex == FS_TLS_SEL || tsk->thread.fs) + return 1; + return 0; +} + +static inline u32 read_32bit_tls(struct task_struct *t, int tls) +{ + struct desc_struct *desc = (void *)t->thread.tls_array; + desc += tls; + return desc->base0 | + (((u32)desc->base1) << 16) | + (((u32)desc->base2) << 24); +} + +static int +fsgs_get(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const unsigned long *kaddr = kbuf; + const unsigned long __user *uaddr = ubuf; + unsigned long addr; + + /* + * XXX why the MSR reads here? + * Can anything change the MSRs without changing thread.fs first? + */ + if (pos == 0) { /* FS */ + if (kaddr) + addr = *kaddr++; + else if (__get_user(addr, uaddr++)) + return -EFAULT; + if (target->thread.fsindex == FS_TLS_SEL) + addr = read_32bit_tls(target, FS_TLS); + else if (target == current) { + rdmsrl(MSR_FS_BASE, addr); } - ret = 0; - break; + else + addr = target->thread.fs; } - case PTRACE_GETFPREGS: { /* Get the child extended FPU state. */ - if (!access_ok(VERIFY_WRITE, (unsigned *)data, - sizeof(struct user_i387_struct))) { - ret = -EIO; - break; + if (count > sizeof(unsigned long)) { /* GS */ + if (kaddr) + addr = *kaddr; + else if (__get_user(addr, uaddr)) + return -EFAULT; + if (target->thread.fsindex == GS_TLS_SEL) + addr = read_32bit_tls(target, GS_TLS); + else if (target == current) { + rdmsrl(MSR_GS_BASE, addr); } - ret = get_fpregs((struct user_i387_struct *)data, child); - break; + else + addr = target->thread.fs; } - case PTRACE_SETFPREGS: { /* Set the child extended FPU state. */ - if (!access_ok(VERIFY_READ, (unsigned *)data, - sizeof(struct user_i387_struct))) { - ret = -EIO; - break; - } - child->used_math = 1; - ret = set_fpregs(child, (struct user_i387_struct *)data); - break; + return 0; +} + +static int +fsgs_set(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + const unsigned long *kaddr = kbuf; + const unsigned long __user *uaddr = ubuf; + unsigned long addr; + int ret = 0; + + if (pos == 0) { /* FS */ + if (kaddr) + addr = *kaddr++; + else if (__get_user(addr, uaddr++)) + return -EFAULT; + ret = do_arch_prctl(target, ARCH_SET_FS, addr); } - default: - ret = ptrace_request(child, request, addr, data); - break; + if (!ret && count > sizeof(unsigned long)) { /* GS */ + if (kaddr) + addr = *kaddr; + else if (__get_user(addr, uaddr)) + return -EFAULT; + ret = do_arch_prctl(target, ARCH_SET_GS, addr); } -out_tsk: - put_task_struct(child); -out: - unlock_kernel(); + return ret; } -static void syscall_trace(struct pt_regs *regs) -{ -#if 0 - printk("trace %s rip %lx rsp %lx rax %d origrax %d caller %lx tiflags %x ptrace %x\n", - current->comm, - regs->rip, regs->rsp, regs->rax, regs->orig_rax, __builtin_return_address(0), - current_thread_info()->flags, current->ptrace); +/* + * These are our native regset flavors. + * XXX ioperm? vm86? + */ +static const struct utrace_regset native_regsets[] = { + { + .n = sizeof(struct user_regs_struct)/8, .size = 8, .align = 8, + .get = genregs_get, .set = genregs_set + }, + { + .n = sizeof(struct user_i387_struct) / sizeof(long), + .size = sizeof(long), .align = sizeof(long), + .active = fpregs_active, + .get = fpregs_get, .set = fpregs_set + }, + { + .n = 2, .size = sizeof(long), .align = sizeof(long), + .active = fsgs_active, + .get = fsgs_get, .set = fsgs_set + }, + { + .n = 8, .size = sizeof(long), .align = sizeof(long), + .active = dbregs_active, + .get = dbregs_get, .set = dbregs_set + }, +}; + +const struct utrace_regset_view utrace_x86_64_native = { + .name = "x86-64", .e_machine = EM_X86_64, + .regsets = native_regsets, + .n = sizeof native_regsets / sizeof native_regsets[0], +}; +EXPORT_SYMBOL_GPL(utrace_x86_64_native); + + +#ifdef CONFIG_PTRACE +static const struct ptrace_layout_segment x86_64_uarea[] = { + {0, sizeof(struct user_regs_struct), 0, 0}, + {offsetof(struct user, u_debugreg[0]), + offsetof(struct user, u_debugreg[4]), 3, 0}, + {offsetof(struct user, u_debugreg[6]), + offsetof(struct user, u_debugreg[8]), 3, 6 * sizeof(long)}, + {0, 0, -1, 0} +}; + +fastcall int arch_ptrace(long *req, struct task_struct *child, + struct utrace_attached_engine *engine, + unsigned long addr, unsigned long data, long *val) +{ + switch (*req) { + case PTRACE_PEEKUSR: + return ptrace_peekusr(child, engine, x86_64_uarea, addr, data); + case PTRACE_POKEUSR: + return ptrace_pokeusr(child, engine, x86_64_uarea, addr, data); + case PTRACE_GETREGS: + return ptrace_whole_regset(child, engine, data, 0, 0); + case PTRACE_SETREGS: + return ptrace_whole_regset(child, engine, data, 0, 1); + case PTRACE_GETFPREGS: + return ptrace_whole_regset(child, engine, data, 1, 0); + case PTRACE_SETFPREGS: + return ptrace_whole_regset(child, engine, data, 1, 1); +#ifdef CONFIG_IA32_EMULATION + case PTRACE_GET_THREAD_AREA: + case PTRACE_SET_THREAD_AREA: + return ptrace_onereg_access(child, engine, + &utrace_ia32_view, 3, + addr, (void __user *)data, + *req == PTRACE_SET_THREAD_AREA); #endif - - ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0)); - /* - * this isn't the same as continuing with a signal, but it will do - * for normal use. strace only continues with a signal if the - * stopping signal is not SIGTRAP. -brl - */ - if (current->exit_code) { - send_sig(current->exit_code, current, 1); - current->exit_code = 0; + /* normal 64bit interface to access TLS data. + Works just like arch_prctl, except that the arguments + are reversed. */ + case PTRACE_ARCH_PRCTL: + return do_arch_prctl(child, data, addr); } + return -ENOSYS; } +#endif /* CONFIG_PTRACE */ + asmlinkage void syscall_trace_enter(struct pt_regs *regs) { - if (unlikely(current->audit_context)) - audit_syscall_entry(current, regs->orig_rax, - regs->rdi, regs->rsi, - regs->rdx, regs->r10); - - if (test_thread_flag(TIF_SYSCALL_TRACE) - && (current->ptrace & PT_PTRACED)) - syscall_trace(regs); + /* do the secure computing check first */ + secure_computing(regs->orig_rax); + + if (test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall(regs, 0); + + if (unlikely(current->audit_context)) { + if (test_thread_flag(TIF_IA32)) { + audit_syscall_entry(AUDIT_ARCH_I386, + regs->orig_rax, + regs->rbx, regs->rcx, + regs->rdx, regs->rsi); + } else { + audit_syscall_entry(AUDIT_ARCH_X86_64, + regs->orig_rax, + regs->rdi, regs->rsi, + regs->rdx, regs->r10); + } + } } asmlinkage void syscall_trace_leave(struct pt_regs *regs) { if (unlikely(current->audit_context)) - audit_syscall_exit(current, regs->rax); + audit_syscall_exit(AUDITSC_RESULT(regs->rax), regs->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, 1); + + if (test_thread_flag(TIF_SINGLESTEP)) { + force_sig(SIGTRAP, current); /* XXX */ + tracehook_report_syscall_step(regs); + } }