|
@@ -266,11 +266,20 @@ void __kprobes kprobe_handler(struct pt_regs *regs)
|
|
|
#endif
|
|
|
|
|
|
if (p) {
|
|
|
- if (cur) {
|
|
|
+ if (!p->ainsn.insn_check_cc(regs->ARM_cpsr)) {
|
|
|
+ /*
|
|
|
+ * Probe hit but conditional execution check failed,
|
|
|
+ * so just skip the instruction and continue as if
|
|
|
+ * nothing had happened.
|
|
|
+ * In this case, we can skip recursing check too.
|
|
|
+ */
|
|
|
+ singlestep_skip(p, regs);
|
|
|
+ } else if (cur) {
|
|
|
/* Kprobe is pending, so we're recursing. */
|
|
|
switch (kcb->kprobe_status) {
|
|
|
case KPROBE_HIT_ACTIVE:
|
|
|
case KPROBE_HIT_SSDONE:
|
|
|
+ case KPROBE_HIT_SS:
|
|
|
/* A pre- or post-handler probe got us here. */
|
|
|
kprobes_inc_nmissed_count(p);
|
|
|
save_previous_kprobe(kcb);
|
|
@@ -279,11 +288,16 @@ void __kprobes kprobe_handler(struct pt_regs *regs)
|
|
|
singlestep(p, regs, kcb);
|
|
|
restore_previous_kprobe(kcb);
|
|
|
break;
|
|
|
+ case KPROBE_REENTER:
|
|
|
+ /* A nested probe was hit in FIQ, it is a BUG */
|
|
|
+ pr_warn("Unrecoverable kprobe detected at %p.\n",
|
|
|
+ p->addr);
|
|
|
+ /* fall through */
|
|
|
default:
|
|
|
/* impossible cases */
|
|
|
BUG();
|
|
|
}
|
|
|
- } else if (p->ainsn.insn_check_cc(regs->ARM_cpsr)) {
|
|
|
+ } else {
|
|
|
/* Probe hit and conditional execution check ok. */
|
|
|
set_current_kprobe(p);
|
|
|
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
|
|
@@ -304,13 +318,6 @@ void __kprobes kprobe_handler(struct pt_regs *regs)
|
|
|
}
|
|
|
reset_current_kprobe();
|
|
|
}
|
|
|
- } else {
|
|
|
- /*
|
|
|
- * Probe hit but conditional execution check failed,
|
|
|
- * so just skip the instruction and continue as if
|
|
|
- * nothing had happened.
|
|
|
- */
|
|
|
- singlestep_skip(p, regs);
|
|
|
}
|
|
|
} else if (cur) {
|
|
|
/* We probably hit a jprobe. Call its break handler. */
|
|
@@ -434,6 +441,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs)
|
|
|
struct hlist_node *tmp;
|
|
|
unsigned long flags, orig_ret_address = 0;
|
|
|
unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
|
|
|
+ kprobe_opcode_t *correct_ret_addr = NULL;
|
|
|
|
|
|
INIT_HLIST_HEAD(&empty_rp);
|
|
|
kretprobe_hash_lock(current, &head, &flags);
|
|
@@ -456,14 +464,34 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs)
|
|
|
/* another task is sharing our hash bucket */
|
|
|
continue;
|
|
|
|
|
|
+ orig_ret_address = (unsigned long)ri->ret_addr;
|
|
|
+
|
|
|
+ if (orig_ret_address != trampoline_address)
|
|
|
+ /*
|
|
|
+ * This is the real return address. Any other
|
|
|
+ * instances associated with this task are for
|
|
|
+ * other calls deeper on the call stack
|
|
|
+ */
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ kretprobe_assert(ri, orig_ret_address, trampoline_address);
|
|
|
+
|
|
|
+ correct_ret_addr = ri->ret_addr;
|
|
|
+ hlist_for_each_entry_safe(ri, tmp, head, hlist) {
|
|
|
+ if (ri->task != current)
|
|
|
+ /* another task is sharing our hash bucket */
|
|
|
+ continue;
|
|
|
+
|
|
|
+ orig_ret_address = (unsigned long)ri->ret_addr;
|
|
|
if (ri->rp && ri->rp->handler) {
|
|
|
__this_cpu_write(current_kprobe, &ri->rp->kp);
|
|
|
get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
|
|
|
+ ri->ret_addr = correct_ret_addr;
|
|
|
ri->rp->handler(ri, regs);
|
|
|
__this_cpu_write(current_kprobe, NULL);
|
|
|
}
|
|
|
|
|
|
- orig_ret_address = (unsigned long)ri->ret_addr;
|
|
|
recycle_rp_inst(ri, &empty_rp);
|
|
|
|
|
|
if (orig_ret_address != trampoline_address)
|
|
@@ -475,7 +503,6 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs)
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- kretprobe_assert(ri, orig_ret_address, trampoline_address);
|
|
|
kretprobe_hash_unlock(current, &flags);
|
|
|
|
|
|
hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
|