📄 ptrace_64.c
字号:
/* By Ross Biro 1/23/92 *//* * Pentium III FXSR, SSE support * Gareth Hughes <gareth@valinux.com>, May 2000 * * x86-64 port 2000-2002 Andi Kleen */#include <linux/kernel.h>#include <linux/sched.h>#include <linux/mm.h>#include <linux/smp.h>#include <linux/errno.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 <asm/uaccess.h>#include <asm/pgtable.h>#include <asm/system.h>#include <asm/processor.h>#include <asm/i387.h>#include <asm/debugreg.h>#include <asm/ldt.h>#include <asm/desc.h>#include <asm/proto.h>#include <asm/ia32.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]. * 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/* * eflags and offset of eflags on child stack.. */#define EFLAGS offsetof(struct pt_regs, eflags)#define EFL_OFFSET ((int)(EFLAGS-sizeof(struct pt_regs)))/* * this routine will get a word off of the processes privileged stack. * the offset is how far from the base addr as stored in the TSS. * this routine assumes that all the privileged stacks are in our * data space. */ static inline unsigned long get_stack_long(struct task_struct *task, int offset){ unsigned char *stack; stack = (unsigned char *)task->thread.rsp0; stack += offset; return (*((unsigned long *)stack));}/* * this routine will put a word on the processes privileged stack. * the offset is how far from the base addr as stored in the TSS. * this routine assumes that all the privileged stacks are in our * data space. */static inline long put_stack_long(struct task_struct *task, int offset, unsigned long data){ unsigned char * stack; stack = (unsigned char *) task->thread.rsp0; stack += offset; *(unsigned long *) stack = data; return 0;}#define LDT_SEGMENT 4unsigned 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; seg &= ~7UL; mutex_lock(&child->mm->context.lock); if (unlikely((seg >> 3) >= child->mm->context.size)) addr = -1L; /* bogus selector, access would fault */ else { desc = child->mm->context.ldt + seg; base = ((desc[0] >> 16) | ((desc[1] & 0xff) << 16) | (desc[1] & 0xff000000)); /* 16-bit code segment? */ if (!((desc[1] >> 22) & 1)) addr &= 0xffff; addr += base; } mutex_unlock(&child->mm->context.lock); } return addr;}static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs){ int i, copied; unsigned char opcode[15]; 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 and iret */ case 0x9d: case 0xcf: 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 0xf2: case 0xf3: continue; case 0x40 ... 0x4f: if (regs->cs != __USER_CS) /* 32-bit mode: register increment */ return 0; /* 64-bit mode: REX prefix */ continue; /* CHECKME: 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;}static void set_singlestep(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. */ if (is_setting_trap_flag(child, regs)) return; child->ptrace |= PT_DTRACE;}static void clear_singlestep(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) { struct pt_regs *regs = task_pt_regs(child); regs->eflags &= ~TRAP_FLAG; child->ptrace &= ~PT_DTRACE; }}/* * Called by kernel/ptrace.c when detaching.. * * Make sure the single step bit is not set. */void ptrace_disable(struct task_struct *child){ clear_singlestep(child);}static int putreg(struct task_struct *child, unsigned long regno, unsigned long value){ unsigned long tmp; switch (regno) { case offsetof(struct user_regs_struct,fs): if (value && (value & 3) != 3) return -EIO; child->thread.fsindex = value & 0xffff; return 0; case offsetof(struct user_regs_struct,gs): if (value && (value & 3) != 3) return -EIO; child->thread.gsindex = value & 0xffff; return 0; case offsetof(struct user_regs_struct,ds): if (value && (value & 3) != 3) return -EIO; child->thread.ds = value & 0xffff; return 0; case offsetof(struct user_regs_struct,es): if (value && (value & 3) != 3) return -EIO; child->thread.es = value & 0xffff; return 0; case offsetof(struct user_regs_struct,ss): if ((value & 3) != 3) return -EIO; value &= 0xffff; return 0; case offsetof(struct user_regs_struct,fs_base): if (value >= TASK_SIZE_OF(child)) return -EIO; child->thread.fs = value; return 0; case offsetof(struct user_regs_struct,gs_base): if (value >= TASK_SIZE_OF(child)) return -EIO; child->thread.gs = value; return 0; case offsetof(struct user_regs_struct, eflags): value &= FLAG_MASK; tmp = get_stack_long(child, EFL_OFFSET); tmp &= ~FLAG_MASK; value |= tmp; break; case offsetof(struct user_regs_struct,cs): if ((value & 3) != 3) return -EIO; value &= 0xffff; break; } put_stack_long(child, regno - sizeof(struct pt_regs), value); return 0;}static unsigned long getreg(struct task_struct *child, unsigned long regno){ unsigned long val; switch (regno) { case offsetof(struct user_regs_struct, fs): return child->thread.fsindex; case offsetof(struct user_regs_struct, gs): return child->thread.gsindex; case offsetof(struct user_regs_struct, ds): return child->thread.ds; case offsetof(struct user_regs_struct, es): return child->thread.es; case offsetof(struct user_regs_struct, fs_base): return child->thread.fs; case offsetof(struct user_regs_struct, gs_base): return child->thread.gs; default: regno = regno - sizeof(struct pt_regs); val = get_stack_long(child, regno); if (test_tsk_thread_flag(child, TIF_IA32)) val &= 0xffffffff; return val; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -