On 05/17/2016 11:29 PM, Masami Hiramatsu wrote:
On Tue, 17 May 2016 16:58:09 +0800
Huang Shijie <[email protected]> wrote:

On Wed, Apr 27, 2016 at 02:53:00PM -0400, David Long wrote:
+
+/*
+ * Interrupts need to be disabled before single-step mode is set, and not
+ * reenabled until after single-step mode ends.
+ * Without disabling interrupt on local CPU, there is a chance of
+ * interrupt occurrence in the period of exception return and  start of
+ * out-of-line single-step, that result in wrongly single stepping
+ * into the interrupt handler.
+ */
+static void __kprobes kprobes_save_local_irqflag(struct pt_regs *regs)
+{
+     struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();

Why not add a parameter for this function to save the @kcb?

Good catch, it should use same kcb of caller.


I've made the change for the next version of the patch.


+
+     kcb->saved_irqflag = regs->pstate;
+     regs->pstate |= PSR_I_BIT;
+}
+
+static void __kprobes kprobes_restore_local_irqflag(struct pt_regs *regs)
+{
+     struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
ditto.


I've made the change.

+
+     if (kcb->saved_irqflag & PSR_I_BIT)
+             regs->pstate |= PSR_I_BIT;
+     else
+             regs->pstate &= ~PSR_I_BIT;
+}
+
+static void __kprobes
+set_ss_context(struct kprobe_ctlblk *kcb, unsigned long addr)
+{
+     kcb->ss_ctx.ss_pending = true;
+     kcb->ss_ctx.match_addr = addr + sizeof(kprobe_opcode_t);
+}
+
+static void __kprobes clear_ss_context(struct kprobe_ctlblk *kcb)
+{
+     kcb->ss_ctx.ss_pending = false;
+     kcb->ss_ctx.match_addr = 0;
+}
+
+static void __kprobes setup_singlestep(struct kprobe *p,
+                                    struct pt_regs *regs,
+                                    struct kprobe_ctlblk *kcb, int reenter)
+{
+     unsigned long slot;
+
+     if (reenter) {
+             save_previous_kprobe(kcb);
+             set_current_kprobe(p);
+             kcb->kprobe_status = KPROBE_REENTER;
+     } else {
+             kcb->kprobe_status = KPROBE_HIT_SS;
+     }
+
+     if (p->ainsn.insn) {
+             /* prepare for single stepping */
+             slot = (unsigned long)p->ainsn.insn;
+
+             set_ss_context(kcb, slot);      /* mark pending ss */
+
+             if (kcb->kprobe_status == KPROBE_REENTER)
+                     spsr_set_debug_flag(regs, 0);
+
+             /* IRQs and single stepping do not mix well. */
+             kprobes_save_local_irqflag(regs);
+             kernel_enable_single_step(regs);
+             instruction_pointer(regs) = slot;
+     } else  {
+             BUG();

You'd better use BUG_ON(!p->ainsn.insn);


I have that change ready but the BUG*() is removed entirely in patch 07/10 and the indentation changed back to the above, resulting in more diffs and the same final code.

+     }
+}
+
+static int __kprobes reenter_kprobe(struct kprobe *p,
+                                 struct pt_regs *regs,
+                                 struct kprobe_ctlblk *kcb)
+{
+     switch (kcb->kprobe_status) {
+     case KPROBE_HIT_SSDONE:
+     case KPROBE_HIT_ACTIVE:
+             kprobes_inc_nmissed_count(p);
+             setup_singlestep(p, regs, kcb, 1);
+             break;
+     case KPROBE_HIT_SS:
+     case KPROBE_REENTER:
+             pr_warn("Unrecoverable kprobe detected at %p.\n", p->addr);
+             dump_kprobe(p);
+             BUG();
+             break;
+     default:
+             WARN_ON(1);
+             return 0;
+     }
+
+     return 1;
+}
+
+static void __kprobes
+post_kprobe_handler(struct kprobe_ctlblk *kcb, struct pt_regs *regs)
+{
+     struct kprobe *cur = kprobe_running();
+
+     if (!cur)
+             return;
+
+     /* return addr restore if non-branching insn */
+     if (cur->ainsn.restore.type == RESTORE_PC) {
+             instruction_pointer(regs) = cur->ainsn.restore.addr;
+             if (!instruction_pointer(regs))
+                     BUG();
+     }
+
+     /* restore back original saved kprobe variables and continue */
+     if (kcb->kprobe_status == KPROBE_REENTER) {
+             restore_previous_kprobe(kcb);
+             return;
+     }
+     /* call post handler */
+     kcb->kprobe_status = KPROBE_HIT_SSDONE;
+     if (cur->post_handler)  {
+             /* post_handler can hit breakpoint and single step
+              * again, so we enable D-flag for recursive exception.
+              */
+             cur->post_handler(cur, regs, 0);
+     }
+
+     reset_current_kprobe();
+}
+
+int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
+{
+     struct kprobe *cur = kprobe_running();
+     struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+     switch (kcb->kprobe_status) {
+     case KPROBE_HIT_SS:
+     case KPROBE_REENTER:
+             /*
+              * We are here because the instruction being single
+              * stepped caused a page fault. We reset the current
+              * kprobe and the ip points back to the probe address
+              * and allow the page fault handler to continue as a
+              * normal page fault.
+              */
+             instruction_pointer(regs) = (unsigned long)cur->addr;
+             if (!instruction_pointer(regs))
+                     BUG();

This can be BUG_ON(!instruction_pointer(regs)).


I've made the change.

+             if (kcb->kprobe_status == KPROBE_REENTER)
+                     restore_previous_kprobe(kcb);
+             else
+                     reset_current_kprobe();
+
+             break;
+     case KPROBE_HIT_ACTIVE:
+     case KPROBE_HIT_SSDONE:
+             /*
+              * We increment the nmissed count for accounting,
+              * we can also use npre/npostfault count for accounting
+              * these specific fault cases.
+              */
+             kprobes_inc_nmissed_count(cur);
+
+             /*
+              * We come here because instructions in the pre/post
+              * handler caused the page_fault, this could happen
+              * if handler tries to access user space by
+              * copy_from_user(), get_user() etc. Let the
+              * user-specified handler try to fix it first.
+              */
+             if (cur->fault_handler && cur->fault_handler(cur, regs, fsr))
+                     return 1;
+
+             /*
+              * In case the user-specified fault handler returned
+              * zero, try to fix up.
+              */
+             if (fixup_exception(regs))
+                     return 1;
+     }
+     return 0;
+}
+
+int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
+                                    unsigned long val, void *data)
+{
+     return NOTIFY_DONE;
+}
+
+static void __kprobes kprobe_handler(struct pt_regs *regs)
+{
+     struct kprobe *p, *cur_kprobe;
+     struct kprobe_ctlblk *kcb;
+     unsigned long addr = instruction_pointer(regs);
+
+     kcb = get_kprobe_ctlblk();
+     cur_kprobe = kprobe_running();
+
+     p = get_kprobe((kprobe_opcode_t *) addr);
+
+     if (p) {
+             if (cur_kprobe) {
+                     if (reenter_kprobe(p, regs, kcb))
+                             return;
+             } else {
+                     /* Probe hit */
+                     set_current_kprobe(p);
+                     kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+                     /*
+                      * If we have no pre-handler or it returned 0, we
+                      * continue with normal processing.  If we have a
+                      * pre-handler and it returned non-zero, it prepped
+                      * for calling the break_handler below on re-entry,
+                      * so get out doing nothing more here.
+                      *
+                      * pre_handler can hit a breakpoint and can step thru
+                      * before return, keep PSTATE D-flag enabled until
+                      * pre_handler return back.
+                      */
+                     if (!p->pre_handler || !p->pre_handler(p, regs)) {
+                             kcb->kprobe_status = KPROBE_HIT_SS;
The above line is duplicated.
You will set KPROBE_HIT_SS in the setup_singlestep.

Right.


I've removed it.


+                             setup_singlestep(p, regs, kcb, 0);
+                             return;
+                     }
+             }
+     } else if ((le32_to_cpu(*(kprobe_opcode_t *) addr) ==
+         BRK64_OPCODE_KPROBES) && cur_kprobe) {
+             /* We probably hit a jprobe.  Call its break handler. */
+             if (cur_kprobe->break_handler  &&
+                  cur_kprobe->break_handler(cur_kprobe, regs)) {
+                     kcb->kprobe_status = KPROBE_HIT_SS;
ditto

I've removed it.

+                     setup_singlestep(cur_kprobe, regs, kcb, 0);
+                     return;
+             }
+     }
+     /*
+      * The breakpoint instruction was removed right
+      * after we hit it.  Another cpu has removed
+      * either a probepoint or a debugger breakpoint
+      * at this address.  In either case, no further
+      * handling of this interrupt is appropriate.
+      * Return back to original instruction, and continue.
+      */
+}

Thanks,




Thanks,
-dl

Reply via email to