diff options
Diffstat (limited to 'arch/x86/kernel/uprobes.c')
| -rw-r--r-- | arch/x86/kernel/uprobes.c | 52 |
1 files changed, 51 insertions, 1 deletions
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 36fd42091fa7..9538f00827a9 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c | |||
| @@ -41,6 +41,9 @@ | |||
| 41 | /* Adjust the return address of a call insn */ | 41 | /* Adjust the return address of a call insn */ |
| 42 | #define UPROBE_FIX_CALL 0x2 | 42 | #define UPROBE_FIX_CALL 0x2 |
| 43 | 43 | ||
| 44 | /* Instruction will modify TF, don't change it */ | ||
| 45 | #define UPROBE_FIX_SETF 0x4 | ||
| 46 | |||
| 44 | #define UPROBE_FIX_RIP_AX 0x8000 | 47 | #define UPROBE_FIX_RIP_AX 0x8000 |
| 45 | #define UPROBE_FIX_RIP_CX 0x4000 | 48 | #define UPROBE_FIX_RIP_CX 0x4000 |
| 46 | 49 | ||
| @@ -239,6 +242,10 @@ static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn) | |||
| 239 | insn_get_opcode(insn); /* should be a nop */ | 242 | insn_get_opcode(insn); /* should be a nop */ |
| 240 | 243 | ||
| 241 | switch (OPCODE1(insn)) { | 244 | switch (OPCODE1(insn)) { |
| 245 | case 0x9d: | ||
| 246 | /* popf */ | ||
| 247 | auprobe->fixups |= UPROBE_FIX_SETF; | ||
| 248 | break; | ||
| 242 | case 0xc3: /* ret/lret */ | 249 | case 0xc3: /* ret/lret */ |
| 243 | case 0xcb: | 250 | case 0xcb: |
| 244 | case 0xc2: | 251 | case 0xc2: |
| @@ -646,7 +653,7 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
| 646 | * Skip these instructions as per the currently known x86 ISA. | 653 | * Skip these instructions as per the currently known x86 ISA. |
| 647 | * 0x66* { 0x90 | 0x0f 0x1f | 0x0f 0x19 | 0x87 0xc0 } | 654 | * 0x66* { 0x90 | 0x0f 0x1f | 0x0f 0x19 | 0x87 0xc0 } |
| 648 | */ | 655 | */ |
| 649 | bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | 656 | static bool __skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) |
| 650 | { | 657 | { |
| 651 | int i; | 658 | int i; |
| 652 | 659 | ||
| @@ -673,3 +680,46 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
| 673 | } | 680 | } |
| 674 | return false; | 681 | return false; |
| 675 | } | 682 | } |
| 683 | |||
| 684 | bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
| 685 | { | ||
| 686 | bool ret = __skip_sstep(auprobe, regs); | ||
| 687 | if (ret && (regs->flags & X86_EFLAGS_TF)) | ||
| 688 | send_sig(SIGTRAP, current, 0); | ||
| 689 | return ret; | ||
| 690 | } | ||
| 691 | |||
| 692 | void arch_uprobe_enable_step(struct arch_uprobe *auprobe) | ||
| 693 | { | ||
| 694 | struct task_struct *task = current; | ||
| 695 | struct arch_uprobe_task *autask = &task->utask->autask; | ||
| 696 | struct pt_regs *regs = task_pt_regs(task); | ||
| 697 | |||
| 698 | autask->saved_tf = !!(regs->flags & X86_EFLAGS_TF); | ||
| 699 | |||
| 700 | regs->flags |= X86_EFLAGS_TF; | ||
| 701 | if (test_tsk_thread_flag(task, TIF_BLOCKSTEP)) | ||
| 702 | set_task_blockstep(task, false); | ||
| 703 | } | ||
| 704 | |||
| 705 | void arch_uprobe_disable_step(struct arch_uprobe *auprobe) | ||
| 706 | { | ||
| 707 | struct task_struct *task = current; | ||
| 708 | struct arch_uprobe_task *autask = &task->utask->autask; | ||
| 709 | bool trapped = (task->utask->state == UTASK_SSTEP_TRAPPED); | ||
| 710 | struct pt_regs *regs = task_pt_regs(task); | ||
| 711 | /* | ||
| 712 | * The state of TIF_BLOCKSTEP was not saved so we can get an extra | ||
| 713 | * SIGTRAP if we do not clear TF. We need to examine the opcode to | ||
| 714 | * make it right. | ||
| 715 | */ | ||
| 716 | if (unlikely(trapped)) { | ||
| 717 | if (!autask->saved_tf) | ||
| 718 | regs->flags &= ~X86_EFLAGS_TF; | ||
| 719 | } else { | ||
| 720 | if (autask->saved_tf) | ||
| 721 | send_sig(SIGTRAP, task, 0); | ||
| 722 | else if (!(auprobe->fixups & UPROBE_FIX_SETF)) | ||
| 723 | regs->flags &= ~X86_EFLAGS_TF; | ||
| 724 | } | ||
| 725 | } | ||
