diff options
-rw-r--r-- | arch/x86/include/asm/uprobes.h | 2 | ||||
-rw-r--r-- | arch/x86/kernel/uprobes.c | 33 |
2 files changed, 35 insertions, 0 deletions
diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index f3971bbcd1de..cee58624cb30 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h | |||
@@ -46,6 +46,8 @@ struct arch_uprobe_task { | |||
46 | #ifdef CONFIG_X86_64 | 46 | #ifdef CONFIG_X86_64 |
47 | unsigned long saved_scratch_register; | 47 | unsigned long saved_scratch_register; |
48 | #endif | 48 | #endif |
49 | #define UPROBE_CLEAR_TF (1 << 0) | ||
50 | unsigned int restore_flags; | ||
49 | }; | 51 | }; |
50 | 52 | ||
51 | extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr); | 53 | extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr); |
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 36fd42091fa7..309a0e02b124 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: |
@@ -673,3 +680,29 @@ 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 | void arch_uprobe_enable_step(struct arch_uprobe *auprobe) | ||
685 | { | ||
686 | struct uprobe_task *utask = current->utask; | ||
687 | struct arch_uprobe_task *autask = &utask->autask; | ||
688 | |||
689 | autask->restore_flags = 0; | ||
690 | if (!test_tsk_thread_flag(current, TIF_SINGLESTEP) && | ||
691 | !(auprobe->fixups & UPROBE_FIX_SETF)) | ||
692 | autask->restore_flags |= UPROBE_CLEAR_TF; | ||
693 | /* | ||
694 | * The state of TIF_BLOCKSTEP is not saved. With the TF flag set we | ||
695 | * would to examine the opcode and the flags to make it right. Without | ||
696 | * TF block stepping makes no sense. | ||
697 | */ | ||
698 | user_enable_single_step(current); | ||
699 | } | ||
700 | |||
701 | void arch_uprobe_disable_step(struct arch_uprobe *auprobe) | ||
702 | { | ||
703 | struct uprobe_task *utask = current->utask; | ||
704 | struct arch_uprobe_task *autask = &utask->autask; | ||
705 | |||
706 | if (autask->restore_flags & UPROBE_CLEAR_TF) | ||
707 | user_disable_single_step(current); | ||
708 | } | ||