Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

uprobes/x86: Xol should send SIGTRAP if X86_EFLAGS_TF was set

arch_uprobe_disable_step() correctly preserves X86_EFLAGS_TF and
returns to user-mode. But this means the application gets SIGTRAP
only after the next insn.

This means that UPROBE_CLEAR_TF logic is not really right. _enable
should only record the state of X86_EFLAGS_TF, and _disable should
check it separately from UPROBE_FIX_SETF.

Remove arch_uprobe_task->restore_flags, add ->saved_tf instead, and
change enable/disable accordingly. This assumes that the probed insn
was not trapped, see the next patch.

arch_uprobe_skip_sstep() logic has the same problem, change it to
check X86_EFLAGS_TF and send SIGTRAP as well. We will cleanup this
all after we fold enable/disable_step into pre/post_hol hooks.

Note: send_sig(SIGTRAP) is not actually right, we need send_sigtrap().
But this needs more changes, handle_swbp() does the same and this is
equally wrong.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>

+14 -8
+1 -2
arch/x86/include/asm/uprobes.h
··· 46 46 #ifdef CONFIG_X86_64 47 47 unsigned long saved_scratch_register; 48 48 #endif 49 - #define UPROBE_CLEAR_TF (1 << 0) 50 - unsigned int restore_flags; 49 + unsigned int saved_tf; 51 50 }; 52 51 53 52 extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr);
+13 -6
arch/x86/kernel/uprobes.c
··· 653 653 * Skip these instructions as per the currently known x86 ISA. 654 654 * 0x66* { 0x90 | 0x0f 0x1f | 0x0f 0x19 | 0x87 0xc0 } 655 655 */ 656 - 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) 657 657 { 658 658 int i; 659 659 ··· 681 681 return false; 682 682 } 683 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 + 684 692 void arch_uprobe_enable_step(struct arch_uprobe *auprobe) 685 693 { 686 694 struct task_struct *task = current; 687 695 struct arch_uprobe_task *autask = &task->utask->autask; 688 696 struct pt_regs *regs = task_pt_regs(task); 689 697 690 - autask->restore_flags = 0; 691 - if (!(regs->flags & X86_EFLAGS_TF) && 692 - !(auprobe->fixups & UPROBE_FIX_SETF)) 693 - autask->restore_flags |= UPROBE_CLEAR_TF; 698 + autask->saved_tf = !!(regs->flags & X86_EFLAGS_TF); 694 699 695 700 regs->flags |= X86_EFLAGS_TF; 696 701 if (test_tsk_thread_flag(task, TIF_BLOCKSTEP)) ··· 712 707 * SIGTRAP if we do not clear TF. We need to examine the opcode to 713 708 * make it right. 714 709 */ 715 - if (autask->restore_flags & UPROBE_CLEAR_TF) 710 + if (autask->saved_tf) 711 + send_sig(SIGTRAP, task, 0); 712 + else if (!(auprobe->fixups & UPROBE_FIX_SETF)) 716 713 regs->flags &= ~X86_EFLAGS_TF; 717 714 }