Kprobes: Temporary disarming of reentrant probe
PATCH 1/5 Kprobes: Temporary disarming of reentrant probe
Hi,
Please provide your feedback on this patchset.
Thanks
Prasanna
In situations where a kprobes handler calls a routine which has a probe on it, then kprobes_handler() disarms the new probe forever. This patch removes the above limitation by temporarily disarming the new probe. When the another probe hits while handling the old probe, the kprobes_handler() saves previous kprobes state and handles the new probe without calling the new kprobes registered handlers. kprobe_post_handler() restores back the previous kprobes state and the normal execution continues.
However on x86_64 architecture, re-rentrancy is provided only through pre_handler(). If a routine having probe is referenced through post_handler(), then the probes on that routine are disarmed forever, since the exception stack is gets changed after the processor single steps the instruction of the new probe.
This patch includes generic changes to support temporary disarming on reentrancy of probes.
---
---
linux-2.6.12-rc4-mm2-prasanna/include/linux/kprobes.h | 9 +++++++++ linux-2.6.12-rc4-mm2-prasanna/kernel/kprobes.c | 1 + 2 files changed, 10 insertions(+)
include/linux/kprobes.h renamed from include/linux/kprobes.h~kprobes-temporary-disarming-on-reentrancy-generic +9 -0
@@ -36,6 +36,12 @@#include <asm/kprobes.h> +/* kprobe_status settings */ +#define KPROBE_HIT_ACTIVE 0x00000001 +#define KPROBE_HIT_SS 0x00000002 +#define KPROBE_REENTER 0x00000004 +#define KPROBE_HIT_SSDONE 0x00000008 +struct kprobe ;struct pt_regs ;struct kretprobe ;
@@ -55,6 +61,9 @@ struct kprobe {/* list of kprobes for multi-handler support */ struct list_head list; +/*count the number of times this probe was temporarily disarmed */ +unsigned long nmissed; +/* location of the probe point */ kprobe_opcode_t *addr;
kernel/kprobes.c renamed from kernel/kprobes.c~kprobes-temporary-disarming-on-reentrancy-generic +1 -0
@@ -334,6 +334,7 @@ int register_kprobe(struct kprobe *p) }spin_lock_irqsave (&kprobe_lock, flags); old_p =get_kprobe (p->addr); + p->nmissed =0 ;if (old_p) { ret =register_aggr_kprobe (old_p, p); goto out;
_
Signature · 5 lines
Prasanna S Panchamukhi Linux Technology Center India Software Labs, IBM Bangalore Ph: 91-80-25044636 <[email protected]>
Re: [PATCH 2/5] Kprobes: Temporary disarming of reentrant probe for i386
This patch includes i386 architecture specific changes to support temporary disarming on reentrancy of probes.
---
---
linux-2.6.12-rc4-mm2-prasanna/arch/i386/kernel/kprobes.c | 62 +++++++++++---- 1 files changed, 49 insertions(+), 13 deletions(-)
arch/i386/kernel/kprobes.c renamed from arch/i386/kernel/kprobes.c~kprobes-temporary-disarming-on-reentrancy-i386 +49 -13
@@ -37,12 +37,10 @@#include <asm/kdebug.h> #include <asm/desc.h> -/* kprobe_status settings */ -#define KPROBE_HIT_ACTIVE 0x00000001 -#define KPROBE_HIT_SS 0x00000002 -static struct kprobe *current_kprobe;static unsigned long kprobe_status, kprobe_old_eflags, kprobe_saved_eflags; +static struct kprobe *kprobe_prev; +static unsigned long kprobe_status_prev, kprobe_old_eflags_prev, kprobe_saved_eflags_prev;static struct pt_regs jprobe_saved_regs;static long *jprobe_saved_esp;/* copy of the kernel stack at the probe fire time */
@@ -93,6 +91,31 @@ void arch_remove_kprobe(struct kprobe *p { } +static inline void save_previous_kprobe (void ) +{ + kprobe_prev = current_kprobe; + kprobe_status_prev = kprobe_status; + kprobe_old_eflags_prev = kprobe_old_eflags; + kprobe_saved_eflags_prev = kprobe_saved_eflags; +} + +static inline void restore_previous_kprobe (void ) +{ + current_kprobe = kprobe_prev; + kprobe_status = kprobe_status_prev; + kprobe_old_eflags = kprobe_old_eflags_prev; + kprobe_saved_eflags = kprobe_saved_eflags_prev; +} + +static inline void set_current_kprobe (struct kprobe *p,struct pt_regs *regs) +{ + current_kprobe = p; + kprobe_saved_eflags = kprobe_old_eflags + = (regs->eflags & (TF_MASK |IF_MASK )); +if (is_IF_modifier (p->opcode)) + kprobe_saved_eflags &= ~IF_MASK ; +} +static inline void prepare_singlestep (struct kprobe *p,struct pt_regs *regs) { regs->eflags |=TF_MASK ;
@@ -184,9 +207,18 @@ static int kprobe_handler(struct pt_regsunlock_kprobes (); goto no_kprobe; } -arch_disarm_kprobe (p); - regs->eip = (unsigned long )p->addr; - ret =1 ; +/* 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 (); +set_current_kprobe (p, regs); + p->nmissed++; +prepare_singlestep (p, regs); + kprobe_status =KPROBE_REENTER ; +return 1 ; }else { p = current_kprobe;if (p->break_handler && p->break_handler (p, regs)) {
@@ -221,11 +253,7 @@ static int kprobe_handler(struct pt_regs } kprobe_status =KPROBE_HIT_ACTIVE ; - current_kprobe = p; - kprobe_saved_eflags = kprobe_old_eflags - = (regs->eflags & (TF_MASK |IF_MASK )); -if (is_IF_modifier (p->opcode)) - kprobe_saved_eflags &= ~IF_MASK ; +set_current_kprobe (p, regs);if (p->pre_handler && p->pre_handler (p, regs))/* handler has already set things up, so skip ss setup */
@@ -370,14 +398,22 @@ static inline int post_kprobe_handler(stif (!kprobe_running ())return 0 ; -if (current_kprobe->post_handler) +if ((kprobe_status !=KPROBE_REENTER ) && current_kprobe->post_handler) { + kprobe_status =KPROBE_HIT_SSDONE ; current_kprobe->post_handler (current_kprobe, regs,0 ); + }if (current_kprobe->post_handler != trampoline_post_handler)resume_execution (current_kprobe, regs); regs->eflags |= kprobe_saved_eflags; +/*Restore back the original saved kprobes variables and continue. */ +if (kprobe_status ==KPROBE_REENTER ) { +restore_previous_kprobe (); + goto out; + }unlock_kprobes (); +out:preempt_enable_no_resched ();/*
_
Signature · 8 lines
Have a Nice Day! Thanks & Regards Prasanna S Panchamukhi Linux Technology Center India Software Labs, IBM Bangalore Ph: 91-80-25044636 <[email protected]>
Re: [PATCH 3/5] Kprobes: Temporary disarming of reentrant probe for x86_64
This patch includes x86_64 architecture specific changes to support temporary disarming on reentrancy of probes.
---
---
linux-2.6.12-rc4-mm2-prasanna/arch/x86_64/kernel/kprobes.c | 73 ++++++++++--- 1 files changed, 60 insertions(+), 13 deletions(-)
arch/x86_64/kernel/kprobes.c renamed from arch/x86_64/kernel/kprobes.c~kprobes-temporary-disarming-on-reentrancy-x86_64 +60 -13
@@ -43,12 +43,10 @@static DECLARE_MUTEX (kprobe_mutex); -/* kprobe_status settings */ -#define KPROBE_HIT_ACTIVE 0x00000001 -#define KPROBE_HIT_SS 0x00000002 -static struct kprobe *current_kprobe;static unsigned long kprobe_status, kprobe_old_rflags, kprobe_saved_rflags; +static struct kprobe *kprobe_prev; +static unsigned long kprobe_status_prev, kprobe_old_rflags_prev, kprobe_saved_rflags_prev;static struct pt_regs jprobe_saved_regs;static long *jprobe_saved_rsp;static kprobe_opcode_t *get_insn_slot (void );
@@ -238,6 +236,31 @@ void arch_remove_kprobe(struct kprobe *pdown (&kprobe_mutex); } +static inline void save_previous_kprobe (void ) +{ + kprobe_prev = current_kprobe; + kprobe_status_prev = kprobe_status; + kprobe_old_rflags_prev = kprobe_old_rflags; + kprobe_saved_rflags_prev = kprobe_saved_rflags; +} + +static inline void restore_previous_kprobe (void ) +{ + current_kprobe = kprobe_prev; + kprobe_status = kprobe_status_prev; + kprobe_old_rflags = kprobe_old_rflags_prev; + kprobe_saved_rflags = kprobe_saved_rflags_prev; +} + +static inline void set_current_kprobe (struct kprobe *p,struct pt_regs *regs) +{ + current_kprobe = p; + kprobe_saved_rflags = kprobe_old_rflags + = (regs->eflags & (TF_MASK |IF_MASK )); +if (is_IF_modifier (p->ainsn.insn)) + kprobe_saved_rflags &= ~IF_MASK ; +} +static void prepare_singlestep (struct kprobe *p,struct pt_regs *regs) { regs->eflags |=TF_MASK ;
@@ -273,10 +296,30 @@ int kprobe_handler(struct pt_regs *regs) regs->eflags |= kprobe_saved_rflags;unlock_kprobes (); goto no_kprobe; + }else if (kprobe_status ==KPROBE_HIT_SSDONE ) { +/* TODO: Provide re-entrancy from +* post_kprobes_handler() and avoid exception +* stack corruption while single-stepping on +* the instruction of the new probe. +*/ +arch_disarm_kprobe (p); + regs->rip = (unsigned long )p->addr; + ret =1 ; + }else { +/* We have reentered the kprobe_handler(), since +* another probe was hit while within the +* handler. We here save the original kprobe +* variables and just single step on instruction +* of the new probe without calling any user +* handlers. +*/ +save_previous_kprobe (); +set_current_kprobe (p, regs); + p->nmissed++; +prepare_singlestep (p, regs); + kprobe_status =KPROBE_REENTER ; +return 1 ; } -arch_disarm_kprobe (p); - regs->rip = (unsigned long )p->addr; - ret =1 ; }else { p = current_kprobe;if (p->break_handler && p->break_handler (p, regs)) {
@@ -306,11 +349,7 @@ int kprobe_handler(struct pt_regs *regs) } kprobe_status =KPROBE_HIT_ACTIVE ; - current_kprobe = p; - kprobe_saved_rflags = kprobe_old_rflags - = (regs->eflags & (TF_MASK |IF_MASK )); -if (is_IF_modifier (p->ainsn.insn)) - kprobe_saved_rflags &= ~IF_MASK ; +set_current_kprobe (p, regs);if (p->pre_handler && p->pre_handler (p, regs))/* handler has already set things up, so skip ss setup */
@@ -411,13 +450,21 @@ int post_kprobe_handler(struct pt_regs *if (!kprobe_running ())return 0 ; -if (current_kprobe->post_handler) +if ((kprobe_status !=KPROBE_REENTER ) && current_kprobe->post_handler) { + kprobe_status =KPROBE_HIT_SSDONE ; current_kprobe->post_handler (current_kprobe, regs,0 ); + }resume_execution (current_kprobe, regs); regs->eflags |= kprobe_saved_rflags; +/*Restore back the original saved kprobes variables and continue. */ +if (kprobe_status ==KPROBE_REENTER ) { +restore_previous_kprobe (); + goto out; + }unlock_kprobes (); +out:preempt_enable_no_resched ();/*
_
Signature · 5 lines
Prasanna S Panchamukhi Linux Technology Center India Software Labs, IBM Bangalore Ph: 91-80-25044636 <[email protected]>
Re: [PATCH 4/5] Kprobes: Temporary disarming of reentrant probe for ppc64
This patch includes ppc64 architecture specific changes to support temporary disarming on reentrancy of probes.
---
---
linux-2.6.12-rc4-mm2-prasanna/arch/ppc64/kernel/kprobes.c | 46 +++++++++++--- 1 files changed, 38 insertions(+), 8 deletions(-)
arch/ppc64/kernel/kprobes.c renamed from arch/ppc64/kernel/kprobes.c~kprobes-temporary-disarming-on-reentrancy-ppc64 +38 -8
@@ -36,12 +36,10 @@#include <asm/kdebug.h> #include <asm/sstep.h> -/* kprobe_status settings */ -#define KPROBE_HIT_ACTIVE 0x00000001 -#define KPROBE_HIT_SS 0x00000002 -static struct kprobe *current_kprobe;static unsigned long kprobe_status, kprobe_saved_msr; +static struct kprobe *kprobe_prev; +static unsigned long kprobe_status_prev, kprobe_saved_msr_prev;static struct pt_regs jprobe_saved_regs;int arch_prepare_kprobe (struct kprobe *p)
@@ -88,6 +86,20 @@ static inline void prepare_singlestep(st regs->nip = (unsigned long )&p->ainsn.insn; } +static inline void save_previous_kprobe (void ) +{ + kprobe_prev = current_kprobe; + kprobe_status_prev = kprobe_status; + kprobe_saved_msr_prev = kprobe_saved_msr; +} + +static inline void restore_previous_kprobe (void ) +{ + current_kprobe = kprobe_prev; + kprobe_status = kprobe_status_prev; + kprobe_saved_msr = kprobe_saved_msr_prev; +} +static inline int kprobe_handler (struct pt_regs *regs) {struct kprobe *p;
@@ -106,9 +118,19 @@ static inline int kprobe_handler(structunlock_kprobes (); goto no_kprobe; } -arch_disarm_kprobe (p); - regs->nip = (unsigned long )p->addr; - ret =1 ; +/* 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 (); + current_kprobe = p; + kprobe_saved_msr = regs->msr; + p->nmissed++; +prepare_singlestep (p, regs); + kprobe_status =KPROBE_REENTER ; +return 1 ; }else { p = current_kprobe;if (p->break_handler && p->break_handler (p, regs)) {
@@ -192,13 +214,21 @@ static inline int post_kprobe_handler(stif (!kprobe_running ())return 0 ; -if (current_kprobe->post_handler) +if ((kprobe_status !=KPROBE_REENTER ) && current_kprobe->post_handler) { + kprobe_status =KPROBE_HIT_SSDONE ; current_kprobe->post_handler (current_kprobe, regs,0 ); + }resume_execution (current_kprobe, regs); regs->msr |= kprobe_saved_msr; +/*Restore back the original saved kprobes variables and continue. */ +if (kprobe_status ==KPROBE_REENTER ) { +restore_previous_kprobe (); + goto out; + }unlock_kprobes (); +out:preempt_enable_no_resched ();/*
_
Signature · 5 lines
Prasanna S Panchamukhi Linux Technology Center India Software Labs, IBM Bangalore Ph: 91-80-25044636 <[email protected]>
Re: [PATCH 5/5] Kprobes: Temporary disarming of reentrant probe for sparc64
This patch includes sparc64 architecture specific changes to support temporary disarming on reentrancy of probes.
---
---
linux-2.6.12-rc4-mm2-prasanna/arch/sparc64/kernel/kprobes.c | 62 +++++++++--- 1 files changed, 49 insertions(+), 13 deletions(-)
arch/sparc64/kernel/kprobes.c renamed from arch/sparc64/kernel/kprobes.c~kprobes-temporary-disarming-on-reentrancy-sparc64 +49 -13
@@ -65,19 +65,40 @@ void arch_remove_kprobe(struct kprobe *p { } -/* kprobe_status settings */ -#define KPROBE_HIT_ACTIVE 0x00000001 -#define KPROBE_HIT_SS 0x00000002 -static struct kprobe *current_kprobe;static unsigned long current_kprobe_orig_tnpc;static unsigned long current_kprobe_orig_tstate_pil;static unsigned int kprobe_status; +static struct kprobe *kprobe_prev; +static unsigned long kprobe_orig_tnpc_prev; +static unsigned long kprobe_orig_tstate_pil_prev; +static unsigned int kprobe_status_prev; -static inline void prepare_singlestep (struct kprobe *p,struct pt_regs *regs) +static inline void save_previous_kprobe (void ) +{ + kprobe_status_prev = kprobe_status; + kprobe_orig_tnpc_prev = current_kprobe_orig_tnpc; + kprobe_orig_tstate_pil_prev = current_kprobe_orig_tstate_pil; + kprobe_prev = current_kprobe; +} + +static inline void restore_previous_kprobe (void ) +{ + kprobe_status = kprobe_status_prev; + current_kprobe_orig_tnpc = kprobe_orig_tnpc_prev; + current_kprobe_orig_tstate_pil = kprobe_orig_tstate_pil_prev; + current_kprobe = kprobe_prev; +} + +static inline void set_current_kprobe (struct kprobe *p,struct pt_regs *regs) { current_kprobe_orig_tnpc = regs->tnpc; current_kprobe_orig_tstate_pil = (regs->tstate &TSTATE_PIL ); + current_kprobe = p; +} + +static inline void prepare_singlestep (struct kprobe *p,struct pt_regs *regs) +{ regs->tstate |=TSTATE_PIL ;/*single step inline, if it a breakpoint instruction*/
@@ -110,12 +131,18 @@ static int kprobe_handler(struct pt_regsunlock_kprobes (); goto no_kprobe; } -arch_disarm_kprobe (p); - regs->tpc = (unsigned long ) p->addr; - regs->tnpc = current_kprobe_orig_tnpc; - regs->tstate = ((regs->tstate & ~TSTATE_PIL ) | - current_kprobe_orig_tstate_pil); - ret =1 ; +/* 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 (); +set_current_kprobe (p, regs); + p->nmissed++; + kprobe_status =KPROBE_REENTER ; +prepare_singlestep (p, regs); +return 1 ; }else { p = current_kprobe;if (p->break_handler && p->break_handler (p, regs))
@@ -143,8 +170,8 @@ static int kprobe_handler(struct pt_regs goto no_kprobe; } +set_current_kprobe (p, regs); kprobe_status =KPROBE_HIT_ACTIVE ; - current_kprobe = p;if (p->pre_handler && p->pre_handler (p, regs))return 1 ;
@@ -250,12 +277,20 @@ static inline int post_kprobe_handler(stif (!kprobe_running ())return 0 ; -if (current_kprobe->post_handler) +if ((kprobe_status !=KPROBE_REENTER ) && current_kprobe->post_handler) { + kprobe_status =KPROBE_HIT_SSDONE ; current_kprobe->post_handler (current_kprobe, regs,0 ); + }resume_execution (current_kprobe, regs); +/*Restore back the original saved kprobes variables and continue. */ +if (kprobe_status ==KPROBE_REENTER ) { +restore_previous_kprobe (); + goto out; + }unlock_kprobes (); +out:preempt_enable_no_resched ();return 1 ;
@@ -397,3 +432,4 @@ int longjmp_break_handler(struct kprobe }return 0 ; } +
_
Signature · 5 lines
Prasanna S Panchamukhi Linux Technology Center India Software Labs, IBM Bangalore Ph: 91-80-25044636 <[email protected]>
Re: [PATCH 3/5] Kprobes: Temporary disarming of reentrant probe for x86_64
In reply to Prasanna S PanchamukhiQuoted 3 lines · Prasanna S Panchamukhi
This patch includes
x86_64architecture specific changes to support temporary disarming on reentrancy of probes.
Fine for me (for after 2.6.12)
-Andi
Re: [PATCH 2/5] Kprobes: Temporary disarming of reentrant probe for i386
In reply to Prasanna S PanchamukhiQuoted 3 lines · Prasanna S Panchamukhi
This patch includes i386 architecture specific changes to support temporary disarming on reentrancy of probes.
The patches are all fine from my side.
-Andi
include/linux/kprobes.h struct kprobe { · line 65
Quoted 6 lines
include/linux/kprobes.h renamed from include/linux/kprobes.h~kprobes-temporary-disarming-on-reentrancy-generic +2
-0@@ -55,6 +61,9 @@ struct kprobe {/* list of kprobes for multi-handler support */ struct list_head list; +/*count the number of times this probe was temporarily disarmed */ +unsigned long nmissed;
You declare a variable.
kernel/kprobes.c int register_kprobe(struct kprobe *p) · line 337
Quoted 12 lines
include/linux/kprobes.h renamed from include/linux/kprobes.h~kprobes-temporary-disarming-on-reentrancy-generic +1
-0+/* location of the probe point */ kprobe_opcode_t *addr;diff -puN
kernel/kprobes.c~kprobes-temporary-disarming-on-reentrancy-generickernel/kprobes.ckernel/kprobes.c renamed from kernel/kprobes.c~kprobes-temporary-disarming-on-reentrancy-generic +1
-0@@ -334,6 +334,7 @@ int register_kprobe(struct kprobe *p) }spin_lock_irqsave (&kprobe_lock, flags); old_p =get_kprobe (p->addr); + p->nmissed =0 ;
And then you set it to 0.
And nothing more. Surely this patch does not do anything. Looks like some code is missing.
-Andi
Andi Kleen wrote:
Hi Andi,
Quoted 27 lines
include/linux/kprobes.h struct kprobe { · line 65
Quoted 6 lines
include/linux/kprobes.h renamed from include/linux/kprobes.h~kprobes-temporary-disarming-on-reentrancy-generic +2
-0@@ -55,6 +61,9 @@ struct kprobe {/* list of kprobes for multi-handler support */ struct list_head list; +/*count the number of times this probe was temporarily disarmed */ +unsigned long nmissed;You declare a variable.
kernel/kprobes.c int register_kprobe(struct kprobe *p) · line 337
Quoted 12 lines
include/linux/kprobes.h renamed from include/linux/kprobes.h~kprobes-temporary-disarming-on-reentrancy-generic +1
-0+/* location of the probe point */ kprobe_opcode_t *addr;diff -puN
kernel/kprobes.c~kprobes-temporary-disarming-on-reentrancy-generickernel/kprobes.ckernel/kprobes.c renamed from kernel/kprobes.c~kprobes-temporary-disarming-on-reentrancy-generic +1
-0@@ -334,6 +334,7 @@ int register_kprobe(struct kprobe *p) }spin_lock_irqsave (&kprobe_lock, flags); old_p =get_kprobe (p->addr); + p->nmissed =0 ;And then you set it to 0.
Quoted 2 lines
And nothing more. Surely this patch does not do anything. Looks like some code is missing.
No Andi - nmissed is incremented in the arch/xxx/kernel/kprobes.c everytime the probe is "reentered". This is part of the subsequent patches in the series.
Ananth