linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Masami Hiramatsu <mhiramat@redhat.com>
To: Abhishek Sagar <sagar.abhishek@gmail.com>,
	ananth@in.ibm.com, jkenisto@us.ibm.com
Cc: Harvey Harrison <harvey.harrison@gmail.com>,
	Ingo Molnar <mingo@elte.hu>, "H. Peter Anvin" <hpa@zytor.com>,
	LKML <linux-kernel@vger.kernel.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	qbarnes@gmail.com
Subject: Re: [PATCH] x86: kprobes change kprobe_handler flow
Date: Wed, 02 Jan 2008 11:00:22 -0500	[thread overview]
Message-ID: <477BB516.8040507@redhat.com> (raw)
In-Reply-To: <863e9df20801011224g8a3111dg1b09ce6726759d97@mail.gmail.com>

Hi,

Abhishek Sagar wrote:
>>>  static int __kprobes kprobe_handler(struct pt_regs *regs)
>>>  {
>>> -     struct kprobe *p;
>>>       int ret = 0;
>>>       kprobe_opcode_t *addr;
>>> +     struct kprobe *p, *cur;
>>>       struct kprobe_ctlblk *kcb;
>>>
>>>       addr = (kprobe_opcode_t *)(regs->eip - sizeof(kprobe_opcode_t));
>>> +     if (*addr != BREAKPOINT_INSTRUCTION) {
>>> +             /*
>>> +              * 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.
>>> +              * Back up over the (now missing) int3 and run
>>> +              * the original instruction.
>>> +              */
>>> +             regs->eip -= sizeof(kprobe_opcode_t);
>>> +             return 1;
>>> +     }
> 
> I have moved the above breakpoint removal check at the beginning of
> the function. The current check is for '!p' case only, whereas IMO
> this check should be performed for all cases. An external debugger may
> very well plant and remove a breakpoint on an address which has probe
> on it. This check is a race nevertheless, so its relative placing
> within kprobe_handler() would be best where its duplication can be
> avoided.

I think there is another reason; now we have disable_all_kprobes() which
disarms(removes BREAKPOINT instruction) all kprobes. So, this should be commented.

By the way, IMHO, if a debugger causes it, that might be a bug.
Since when the debugger removes a breakpoint, it should prevent the exception
caused by the breakpoint on other processors. In other words, the debugger
should work as transparently as possible.

>>> -     /* Check we're not actually recursing */
>>> -     if (kprobe_running()) {
>>> -             p = get_kprobe(addr);
>>> -             if (p) {
>>> -                     if (kcb->kprobe_status == KPROBE_HIT_SS &&
>>> -                             *p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
>>> -                             regs->eflags &= ~TF_MASK;
>>> -                             regs->eflags |= kcb->kprobe_saved_eflags;
>>> -                             goto no_kprobe;
>>> -                     }
>>> -                     /* We have reentered the kprobe_handler(), since
>>> -                      * another probe was hit while within the handler.
>>> -                      * We here save the original kprobes variables and
>>> -                      * just single step on the instruction of the new probe
>>> -                      * without calling any user handlers.
>>> -                      */
>>> -                     save_previous_kprobe(kcb);
>>> -                     set_current_kprobe(p, regs, kcb);
>>> -                     kprobes_inc_nmissed_count(p);
>>> -                     prepare_singlestep(p, regs);
>>> -                     kcb->kprobe_status = KPROBE_REENTER;
>>> -                     return 1;
>>> -             } else {
>>> -                     if (*addr != BREAKPOINT_INSTRUCTION) {
>>> -                     /* The breakpoint instruction was removed by
>>> -                      * another cpu right after we hit, no further
>>> -                      * handling of this interrupt is appropriate
>>> -                      */
>>> -                             regs->eip -= sizeof(kprobe_opcode_t);
>>> +     if (p) {
>>> +             if (cur) {
>>> +                     switch (kcb->kprobe_status) {
>>> +                     case KPROBE_HIT_ACTIVE:
>>> +                     case KPROBE_HIT_SSDONE:
>>> +                             /* a probe has been hit inside a
>>> +                              * user handler */
>>> +                             save_previous_kprobe(kcb);
>>> +                             set_current_kprobe(p, regs, kcb);
>>> +                             kprobes_inc_nmissed_count(p);
>>> +                             prepare_singlestep(p, regs);
>>> +                             kcb->kprobe_status = KPROBE_REENTER;
>>>                               ret = 1;
>>> -                             goto no_kprobe;
>>> -                     }
>>> -                     p = __get_cpu_var(current_kprobe);
>>> -                     if (p->break_handler && p->break_handler(p, regs)) {
>>> -                             goto ss_probe;
>>> +                             break;
>>> +                     case KPROBE_HIT_SS:
>>> +                             if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
>>> +                                     regs->eflags &= ~TF_MASK;
>>> +                                     regs->eflags |=
>>> +                                             kcb->kprobe_saved_eflags;
>>> +                             } else {
>>> +                                     /* BUG? */
>>> +                             }

I think whole of this case might have a bug. Since "p" is a recursing kprobe on
the instruction buffer of the running kprobe, *p->ainsn.insn is the next singlestep
target. I think KPROBE_HIT_SS should be treated as same as KPROBE_HIT_ACTIVE.

If the author thought a situation that a user inserts a kprobe on a breakpoint which
has been set by other debugger, we have to check it in !p case, because
get_kprobe(running_kprobe->ainsn.insn) will return NULL in that case.


>>> +                             break;
>>> +                     default:
>>> +                             /* impossible cases */
>>> +                             BUG();
>>>                       }
>>> -             }
> 
> Replaced deeply nested if-elses with a switch.

Originally, if (kcb->kprobe_status==KPROBE_HIT_SS) && !(*p->ainsn.insn == BREAKPOINT_INSTRUCTION),
it increments nmissed_count and change the status to KPROBE_REENTER. Please trace the flow carefully.

> 
> Please let me know if there are any changes on which you would like
> further clarification.
> 
>> --
>> Masami Hiramatsu
>>
>> Software Engineer
>> Hitachi Computer Products (America) Inc.
>> Software Solutions Division
>>
>> e-mail: mhiramat@redhat.com, masami.hiramatsu.pt@hitachi.com
> --
> 
> Thanks,
> Abhishek Sagar

Thank you,

-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America) Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com, masami.hiramatsu.pt@hitachi.com


  reply	other threads:[~2008-01-02 16:01 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-12-28  1:44 [PATCH] x86: kprobes change kprobe_handler flow Harvey Harrison
2007-12-31 13:03 ` Abhishek Sagar
2008-01-01 15:35   ` Ingo Molnar
2008-01-01 19:40     ` Abhishek Sagar
2008-01-01 20:19       ` Harvey Harrison
2008-01-01 20:54         ` Abhishek Sagar
2008-01-02 18:09       ` Masami Hiramatsu
2008-01-02 19:31         ` Abhishek Sagar
2008-01-02 20:23           ` Ingo Molnar
2008-01-02 21:56           ` Masami Hiramatsu
2008-01-03 17:15             ` Masami Hiramatsu
2008-01-03 21:31               ` Masami Hiramatsu
2008-01-04  6:34                 ` Abhishek Sagar
2008-01-03 18:12             ` Abhishek Sagar
2008-01-03 20:11               ` Masami Hiramatsu
2008-01-04  6:43                 ` Abhishek Sagar
2008-01-01 17:49   ` Masami Hiramatsu
2008-01-01 20:24     ` Abhishek Sagar
2008-01-02 16:00       ` Masami Hiramatsu [this message]
  -- strict thread matches above, loose matches on Subject: below --
2007-12-28  2:08 Harvey Harrison
2007-12-30  8:07 ` Masami Hiramatsu
2007-12-30 13:47   ` Ingo Molnar

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=477BB516.8040507@redhat.com \
    --to=mhiramat@redhat.com \
    --cc=ananth@in.ibm.com \
    --cc=harvey.harrison@gmail.com \
    --cc=hpa@zytor.com \
    --cc=jkenisto@us.ibm.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@elte.hu \
    --cc=qbarnes@gmail.com \
    --cc=sagar.abhishek@gmail.com \
    --cc=tglx@linutronix.de \
    /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).