#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
+#include <linux/tracehook.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/audit.h>
+#include <linux/seccomp.h>
+#include <linux/signal.h>
+#include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/desc.h>
#include <asm/proto.h>
#include <asm/ia32.h>
+#include <asm/prctl.h>
/*
* 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
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..
*
*/
void ptrace_disable(struct task_struct *child)
{
- long tmp;
-
- clear_tsk_thread_flag(child, TIF_SINGLESTEP);
- 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,
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):
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)
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;
- 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;
+ 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 __user *) 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 __user *) 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;
- switch (addr) {
- case 0 ... sizeof(struct user_regs_struct):
- ret = putreg(child, addr, data);
+ for (pos >>= 3, count >>= 3; count > 0; --count, ++pos) {
+ unsigned long val;
+ unsigned int i;
+
+ 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);
- clear_tsk_thread_flag(child, TIF_SINGLESTEP);
- 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: {
- struct user_desc __user *p;
- int old;
- p = (struct user_desc __user *)data;
- get_user(old, &p->entry_number);
- put_user(addr, &p->entry_number);
- ret = do_set_thread_area(&child->thread, p);
- put_user(old, &p->entry_number);
- break;
- case PTRACE_GET_THREAD_AREA:
- p = (struct user_desc __user *)data;
- get_user(old, &p->entry_number);
- put_user(addr, &p->entry_number);
- ret = do_get_thread_area(&child->thread, p);
- put_user(old, &p->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;
- clear_tsk_thread_flag(child, TIF_SINGLESTEP);
- 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);
- set_tsk_thread_flag(child, TIF_SINGLESTEP);
- 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 __user *)data,
- sizeof(struct user_regs_struct))) {
- ret = -EIO;
- break;
- }
- ret = 0;
- for (ui = 0; ui < sizeof(struct user_regs_struct); ui += sizeof(long)) {
- ret |= __put_user(getreg(child, ui),(unsigned long __user *) data);
- data += sizeof(long);
- }
- 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 __user *)data,
- sizeof(struct user_regs_struct))) {
- ret = -EIO;
- break;
- }
- ret = 0;
- for (ui = 0; ui < sizeof(struct user_regs_struct); ui += sizeof(long)) {
- ret |= __get_user(tmp, (unsigned long __user *) 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);
}
- break;
+ else
+ addr = target->thread.fs;
}
- case PTRACE_GETFPREGS: { /* Get the child extended FPU state. */
- if (!access_ok(VERIFY_WRITE, (unsigned __user *)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 __user *)data, child);
- break;
+ else
+ addr = target->thread.fs;
}
- case PTRACE_SETFPREGS: { /* Set the child extended FPU state. */
- if (!access_ok(VERIFY_READ, (unsigned __user *)data,
- sizeof(struct user_i387_struct))) {
- ret = -EIO;
- break;
- }
- child->used_math = 1;
- ret = set_fpregs(child, (struct user_i387_struct __user *)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)
- || 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);
+ }
}