linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: dave.long@linaro.org (David Long)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v12 05/10] arm64: Kprobes with single stepping support
Date: Thu, 26 May 2016 15:25:54 -0400	[thread overview]
Message-ID: <57474DC2.5070400@linaro.org> (raw)
In-Reply-To: <20160518122935.39fd7acc63d6f9c17fcbe275@kernel.org>

On 05/17/2016 11:29 PM, Masami Hiramatsu wrote:
> On Tue, 17 May 2016 16:58:09 +0800
> Huang Shijie <shijie.huang@arm.com> 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	other threads:[~2016-05-26 19:25 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-04-27 18:52 [PATCH v12 00/10] arm64: Add kernel probes (kprobes) support David Long
2016-04-27 18:52 ` [PATCH v12 01/10] arm64: Add HAVE_REGS_AND_STACK_ACCESS_API feature David Long
2016-04-28 16:08   ` Marc Zyngier
2016-05-13 19:07     ` David Long
2016-05-17  9:14   ` Huang Shijie
2016-05-20  4:18     ` David Long
2016-04-27 18:52 ` [PATCH v12 02/10] arm64: Add more test functions to insn.c David Long
2016-04-27 18:52 ` [PATCH v12 03/10] arm64: add conditional instruction simulation support David Long
2016-04-27 18:52 ` [PATCH v12 04/10] arm64: Blacklist non-kprobe-able symbols David Long
2016-04-27 18:53 ` [PATCH v12 05/10] arm64: Kprobes with single stepping support David Long
2016-05-12 15:01   ` James Morse
2016-05-18  4:04     ` Masami Hiramatsu
2016-05-20  5:16     ` David Long
2016-05-17  8:58   ` Huang Shijie
2016-05-18  3:29     ` Masami Hiramatsu
2016-05-26 19:25       ` David Long [this message]
2016-05-26 15:40     ` David Long
2016-05-17  9:10   ` Huang Shijie
2016-06-01  5:15     ` David Long
2016-04-27 18:53 ` [PATCH v12 06/10] arm64: Treat all entry code as non-kprobe-able David Long
2016-05-12 14:49   ` James Morse
2016-05-20  5:28     ` David Long
2016-05-26 15:26     ` David Long
2016-04-27 18:53 ` [PATCH v12 07/10] arm64: kprobes instruction simulation support David Long
2016-05-19  1:52   ` Huang Shijie
2016-05-26 19:28     ` David Long
2016-04-27 18:53 ` [PATCH v12 08/10] arm64: Add trampoline code for kretprobes David Long
2016-04-27 18:53 ` [PATCH v12 09/10] arm64: Add kernel return probes support (kretprobes) David Long
2016-04-27 18:53 ` [PATCH v12 10/10] kprobes: Add arm64 case in kprobe example module David Long
2016-05-17  9:57   ` Huang Shijie
2016-05-17 10:24     ` Mark Brown
2016-05-18  1:31       ` Huang Shijie
2016-05-11 15:33 ` [PATCH v12 00/10] arm64: Add kernel probes (kprobes) support James Morse
2016-05-12  2:26   ` Li Bin
2016-05-13 20:02     ` David Long
2016-05-18  2:24     ` Huang Shijie

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=57474DC2.5070400@linaro.org \
    --to=dave.long@linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).