Do singlestep post processing only when we are sure to have singlestepped.

When there are multiple engines, we cannot rely on signal_action to be
passed unchanged to report_signal callback of subsequent engines.
Relying on signal_action and info->si_signo can lead to uprobes
singlestepping when we shouldnt. (An interrupt after a breakpoint hit
can very well look like a step signal.)

This patch adds checks to ensure that user app has singlestepped before
doing post processing. 
The checks are. 
1. If orig_ka is NULL, then its mostly a UTRACE_SIGNAL_REPORT.
2. Verify from instruction_pointer if we have indeed singlestepped.


Signed-off-by: Srikar Dronamraju <sri...@linux.vnet.ibm.com>
---

 kernel/uprobes_core.c |   65 ++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 54 insertions(+), 11 deletions(-)

diff --git a/kernel/uprobes_core.c b/kernel/uprobes_core.c
index 7cea30b..d784d4a 100644
--- a/kernel/uprobes_core.c
+++ b/kernel/uprobes_core.c
@@ -1456,6 +1456,38 @@ static void uprobe_inject_delayed_signals(struct 
list_head *delayed_signals)
 }
 
 /*
+ * Verify from Instruction Pointer if singlestep has indeed occurred.
+ * If Singlestep has occurred, then do post singlestep fix-ups.
+ */
+static bool validate_and_post_sstep(struct uprobe_task *utask,
+                               struct pt_regs *regs,
+                               struct uprobe_probept *ppt)
+{
+       unsigned long vaddr = instruction_pointer(regs);
+
+       if (ppt->ubp.strategy & UBP_HNT_INLINE) {
+               /*
+                * If we have singlestepped, Instruction pointer cannot
+                * be same as virtual address of probepoint.
+                */
+               if (vaddr == ppt->ubp.vaddr)
+                       return false;
+               uprobe_post_ssin(utask, ppt, regs);
+#ifdef CONFIG_UBP_XOL
+       } else {
+               /*
+                * If we have executed out of line, Instruction pointer
+                * cannot be same as virtual address of XOL slot.
+                */
+               if (vaddr == ppt->ubp.xol_vaddr)
+                       return false;
+               uprobe_post_ssout(utask, ppt, regs);
+#endif
+       }
+       return true;
+}
+
+/*
  * Helper routine for uprobe_report_signal().
  * We get called here with:
  *     state = UPTASK_RUNNING => we are here due to a breakpoint hit
@@ -1487,7 +1519,8 @@ static void uprobe_inject_delayed_signals(struct 
list_head *delayed_signals)
 static u32 uprobe_handle_signal(u32 action,
                                struct uprobe_task *utask,
                                struct pt_regs *regs,
-                               siginfo_t *info)
+                               siginfo_t *info,
+                               const struct k_sigaction *orig_ka)
 {
        struct uprobe_probept *ppt;
        struct uprobe_process *uproc;
@@ -1507,7 +1540,13 @@ static u32 uprobe_handle_signal(u32 action,
                resume_action = UTRACE_SINGLESTEP;
        else
                resume_action = UTRACE_RESUME;
-       if (unlikely(signal_action == UTRACE_SIGNAL_REPORT)) {
+       /*
+        * This might be UTRACE_SIGNAL_REPORT request but some other
+        * engine's callback might have changed the signal action to
+        * something other than UTRACE_SIGNAL_REPORT. Use orig_ka to figure
+        * out such cases.
+        */
+       if (unlikely(signal_action == UTRACE_SIGNAL_REPORT) || !orig_ka) {
                /* This thread was quiesced using UTRACE_INTERRUPT. */
                bool done_quiescing;
                if (utask->active_probe)
@@ -1625,17 +1664,20 @@ static u32 uprobe_handle_signal(u32 action,
                        goto no_interest;
 
                down_read(&uproc->rwsem);
-               /* No further need to re-assert UTRACE_SINGLESTEP. */
-               clear_utrace_quiesce(utask, false);
                ppt = utask->active_probe;
                BUG_ON(!ppt);
 
-#ifdef CONFIG_UBP_XOL
-               if (!(ppt->ubp.strategy & UBP_HNT_INLINE))
-                       uprobe_post_ssout(utask, ppt, regs);
-               else
-#endif
-                       uprobe_post_ssin(utask, ppt, regs);
+               /*
+                * Havent singlestepped yet? then re-assert
+                * UTRACE_SINGLESTEP.
+                */
+               if (!validate_and_post_sstep(utask, regs, ppt)) {
+                       up_read(&uproc->rwsem);
+                       goto no_interest;
+               }
+
+               /* No further need to re-assert UTRACE_SINGLESTEP. */
+               clear_utrace_quiesce(utask, false);
 
 bkpt_done:
                /* Note: Can come here after running uretprobe handlers */
@@ -1703,7 +1745,8 @@ static u32 uprobe_report_signal(u32 action,
 
        /* Keep uproc intact until just before we return. */
        uprobe_get_process(uproc);
-       report_action = uprobe_handle_signal(action, utask, regs, info);
+       report_action = uprobe_handle_signal(action, utask, regs, info,
+                                                               orig_ka);
        doomed = utask->doomed;
 
        if (uprobe_put_process(uproc, true))

Reply via email to