uprobes/x86: Xol should send SIGTRAP if X86_EFLAGS_TF was set
authorOleg Nesterov <oleg@redhat.com>
Mon, 3 Sep 2012 14:05:10 +0000 (16:05 +0200)
committerOleg Nesterov <oleg@redhat.com>
Sat, 15 Sep 2012 15:37:31 +0000 (17:37 +0200)
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>
arch/x86/include/asm/uprobes.h
arch/x86/kernel/uprobes.c

index cee58624cb30aa06f556c0c9fd381e4b0a271cb4..d561ff5a3d4d686fe1d1978d4e7af4c0479d5ede 100644 (file)
@@ -46,8 +46,7 @@ struct arch_uprobe_task {
 #ifdef CONFIG_X86_64
        unsigned long                   saved_scratch_register;
 #endif
-#define UPROBE_CLEAR_TF                        (1 << 0)
-       unsigned int                    restore_flags;
+       unsigned int                    saved_tf;
 };
 
 extern int  arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr);
index 3b4aae68efe052f5804f1ede1a831ce759e6e01c..7e993d1f1992e9b22bbf2a0883b9a9526d544103 100644 (file)
@@ -653,7 +653,7 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
  * Skip these instructions as per the currently known x86 ISA.
  * 0x66* { 0x90 | 0x0f 0x1f | 0x0f 0x19 | 0x87 0xc0 }
  */
-bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
+static bool __skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
        int i;
 
@@ -681,16 +681,21 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
        return false;
 }
 
+bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       bool ret = __skip_sstep(auprobe, regs);
+       if (ret && (regs->flags & X86_EFLAGS_TF))
+               send_sig(SIGTRAP, current, 0);
+       return ret;
+}
+
 void arch_uprobe_enable_step(struct arch_uprobe *auprobe)
 {
        struct task_struct *task = current;
        struct arch_uprobe_task *autask = &task->utask->autask;
        struct pt_regs *regs = task_pt_regs(task);
 
-       autask->restore_flags = 0;
-       if (!(regs->flags & X86_EFLAGS_TF) &&
-           !(auprobe->fixups & UPROBE_FIX_SETF))
-               autask->restore_flags |= UPROBE_CLEAR_TF;
+       autask->saved_tf = !!(regs->flags & X86_EFLAGS_TF);
 
        regs->flags |= X86_EFLAGS_TF;
        if (test_tsk_thread_flag(task, TIF_BLOCKSTEP))
@@ -707,6 +712,8 @@ void arch_uprobe_disable_step(struct arch_uprobe *auprobe)
         * SIGTRAP if we do not clear TF. We need to examine the opcode to
         * make it right.
         */
-       if (autask->restore_flags & UPROBE_CLEAR_TF)
+       if (autask->saved_tf)
+               send_sig(SIGTRAP, task, 0);
+       else if (!(auprobe->fixups & UPROBE_FIX_SETF))
                regs->flags &= ~X86_EFLAGS_TF;
 }