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))