Skip to contentSkip to conversation

x86: kprobes change kprobe_handler flow

PATCH In review Harvey Harrison 4 participants 19 messages Dec 28, 2007 – Jan 4, 2008 x86

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 x86: kprobes change kprobe_handler flow

Make the control flow of kprobe_handler more obvious.

Collapse the separate if blocks/gotos with if/else blocks this unifies the duplication of the check for a breakpoint instruction race with another cpu.

---
Masami, please have a look at this, I think it's much more obvious written this way. The way the old code fell through or not was rather non-obvious. Some further work eliminating the nested returns and creating a out: and preempt_out: target at the end of the function would make it easier to notice preempt imbalances in later changes.

 arch/x86/kernel/kprobes.c |   48 ++++++++++++++++----------------------------
 1 files changed, 18 insertions(+), 30 deletions(-)
arch/x86/kernel/kprobes.c +18 -30
index 4e33329..d656215 100644
@@ -480,32 +480,22 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
 	preempt_disable();
 	kcb = get_kprobe_ctlblk();
 
-	/* Check we're not actually recursing */
-	if (kprobe_running()) {
-		p = get_kprobe(addr);
-		if (p) {
+	p = get_kprobe(addr);
+	if (p) {
+		/* Check we're not actually recursing */
+		if (kprobe_running()) {
 			ret = reenter_kprobe(p, regs, kcb);
 			if (kcb->kprobe_status == KPROBE_REENTER)
 				return 1;
+			goto no_kprobe;
 		} 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->ip = (unsigned long)addr;
-				ret = 1;
-				goto no_kprobe;
-			}
-			p = __get_cpu_var(current_kprobe);
-			if (p->break_handler && p->break_handler(p, regs))
-				goto ss_probe;
+			set_current_kprobe(p, regs, kcb);
+			kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+			if (p->pre_handler && p->pre_handler(p, regs))
+				/* handler set things up, skip ss setup */
+				return 1;
 		}
-		goto no_kprobe;
-	}
-
-	p = get_kprobe(addr);
-	if (!p) {
+	} else {
 		if (*addr != BREAKPOINT_INSTRUCTION) {
 			/*
 			 * The breakpoint instruction was removed right
@@ -518,18 +508,16 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
 			 */
 			regs->ip = (unsigned long)addr;
 			ret = 1;
+			goto no_kprobe;
+		}
+		if (kprobe_running()) {
+			p = __get_cpu_var(current_kprobe);
+			if (p->break_handler && p->break_handler(p, regs))
+				goto ss_probe;
+			goto no_kprobe;
 		}
-		/* Not one of ours: let kernel handle it */
-		goto no_kprobe;
 	}
 
-	set_current_kprobe(p, regs, kcb);
-	kcb->kprobe_status = KPROBE_HIT_ACTIVE;
-
-	if (p->pre_handler && p->pre_handler(p, regs))
-		/* handler has already set things up, so skip ss setup */
-		return 1;
-
 ss_probe:
 #if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
 	if (p->ainsn.boostable == 1 && !p->post_handler) {
Signature · 1 line
1.5.4.rc2.1097.gb6e0d
#1 lore raw
Quoted 5 lines · Harvey Harrison

Make the control flow of kprobe_handler more obvious.

Collapse the separate if blocks/gotos with if/else blocks this unifies the duplication of the check for a breakpoint instruction race with another cpu.

This is a patch derived from kprobe_handler of the ARM kprobes port. This further simplifies the current x86 kprobe_handler. The resulting definition is smaller, more readable, has no goto's and contains only a single call to get_kprobe.

---

arch/x86/kernel/kprobes_32.c +80 -78
index 3a020f7..5a626fd 100644
@@ -240,108 +240,110 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
 	*sara = (unsigned long) &kretprobe_trampoline;
 }
 
+static __always_inline void setup_singlestep(struct kprobe *p,
+					     struct pt_regs *regs,
+					     struct kprobe_ctlblk *kcb)
+{
+#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
+	if (p->ainsn.boostable == 1 && !p->post_handler) {
+		/* Boost up -- we can execute copied instructions directly */
+		reset_current_kprobe();
+		regs->eip = (unsigned long)p->ainsn.insn;
+		preempt_enable_no_resched();
+	} else {
+		prepare_singlestep(p, regs);
+		kcb->kprobe_status = KPROBE_HIT_SS;
+	}
+#else
+	prepare_singlestep(p, regs);
+	kcb->kprobe_status = KPROBE_HIT_SS;
+#endif
+}
+
 /*
  * Interrupts are disabled on entry as trap3 is an interrupt gate and they
  * remain disabled thorough out this function.
  */
 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;
+	}
 
-	/*
-	 * We don't want to be preempted for the entire
-	 * duration of kprobe processing
-	 */
 	preempt_disable();
 	kcb = get_kprobe_ctlblk();
+	cur = kprobe_running();
+	p = get_kprobe(addr);
 
-	/* 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? */
+				}
+				break;
+			default:
+				/* impossible cases */
+				BUG();
 			}
-		}
-		goto no_kprobe;
-	}
+		} else {
+			set_current_kprobe(p, regs, kcb);
+			kcb->kprobe_status = KPROBE_HIT_ACTIVE;
 
-	p = get_kprobe(addr);
-	if (!p) {
-		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.
+			 * 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
+			 * for jprobe processing, so get out doing nothing
+			 * more here.
 			 */
-			regs->eip -= sizeof(kprobe_opcode_t);
+			if (!p->pre_handler || !p->pre_handler(p, regs))
+				setup_singlestep(p, regs, kcb);
 			ret = 1;
 		}
-		/* Not one of ours: let kernel handle it */
-		goto no_kprobe;
-	}
-
-	set_current_kprobe(p, regs, kcb);
-	kcb->kprobe_status = KPROBE_HIT_ACTIVE;
-
-	if (p->pre_handler && p->pre_handler(p, regs))
-		/* handler has already set things up, so skip ss setup */
-		return 1;
+	} else if (cur) {
+		p = __get_cpu_var(current_kprobe);
+		if (p->break_handler && p->break_handler(p, regs)) {
+			setup_singlestep(p, regs, kcb);
+			ret = 1;
+		}
+	} /* else: not a kprobe fault; let the kernel handle it */
 
-ss_probe:
-#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
-	if (p->ainsn.boostable == 1 && !p->post_handler){
-		/* Boost up -- we can execute copied instructions directly */
-		reset_current_kprobe();
-		regs->eip = (unsigned long)p->ainsn.insn;
+	if (!ret)
 		preempt_enable_no_resched();
-		return 1;
-	}
-#endif
-	prepare_singlestep(p, regs);
-	kcb->kprobe_status = KPROBE_HIT_SS;
-	return 1;
-
-no_kprobe:
-	preempt_enable_no_resched();
 	return ret;
 }
 
arch/x86/kernel/kprobes_64.c +69 -75

171 lines not on this page · raw message

#2 lore raw
Quoted 11 lines · Abhishek Sagar
Quoted 5 lines · Harvey Harrison

Make the control flow of kprobe_handler more obvious.

Collapse the separate if blocks/gotos with if/else blocks this unifies the duplication of the check for a breakpoint instruction race with another cpu.

This is a patch derived from kprobe_handler of the ARM kprobes port. This further simplifies the current x86 kprobe_handler. The resulting definition is smaller, more readable, has no goto's and contains only a single call to get_kprobe.

hm, this patch does not apply to x86.git#mm, due to the fixes, unifications and cleanups done there. Could you send a patch against -mm or against x86.git? (see the tree-fetching instructions below) Thanks,

Ingo

--------------{ x86.git instructions }---------->

git-clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git linux-2.6.git cd linux-2.6.git
git-branch x86
git-checkout x86
git-pull git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-x86.git mm

(do subsequent pulls via "git-pull --force", as we frequently rebase the git tree. NOTE: this might override your own local changes, so do this only if you dont mind about losing thse changes in that tree.)

#3 lore raw
Quoted 14 lines · Ingo Molnar

hm, this patch does not apply to x86.git#mm, due to the fixes, unifications and cleanups done there. Could you send a patch against -mm or against x86.git? (see the tree-fetching instructions below) Thanks,

Ingo

--------------{ x86.git instructions }---------->

git-clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git linux-2.6.git cd linux-2.6.git
git-branch x86
git-checkout x86
git-pull git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-x86.git mm

Quoted 4 lines

(do subsequent pulls via "git-pull --force", as we frequently rebase the git tree. NOTE: this might override your own local changes, so do this only if you dont mind about losing thse changes in that tree.)

Thanks for pointing me to the right tree. I have made some code re-arrangements in this one.

---

arch/x86/kernel/kprobes.c +94 -71
index a72e02b..45adc8e 100644
@@ -441,6 +441,26 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
 	/* Replace the return addr with trampoline addr */
 	*sara = (unsigned long) &kretprobe_trampoline;
 }
+
+#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
+static __always_inline int setup_boost(struct kprobe *p, struct pt_regs *regs)
+{
+	if (p->ainsn.boostable == 1 && !p->post_handler) {
+		/* Boost up -- we can execute copied instructions directly */
+		reset_current_kprobe();
+		regs->ip = (unsigned long)p->ainsn.insn;
+		preempt_enable_no_resched();
+		return 0;
+	}
+	return 1;
+}
+#else
+static __always_inline int setup_boost(struct kprobe *p, struct pt_regs *regs)
+{
+	return 1;
+}
+#endif
+
 /*
  * We have reentered the kprobe_handler(), since another probe was hit while
  * within the handler. We save the original kprobes variables and just single
@@ -449,29 +469,47 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
 static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
 				    struct kprobe_ctlblk *kcb)
 {
-	if (kcb->kprobe_status == KPROBE_HIT_SS &&
-	    *p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
-		regs->flags &= ~X86_EFLAGS_TF;
-		regs->flags |= kcb->kprobe_saved_flags;
-		return 0;
+	int ret = 0;
+
+	switch (kcb->kprobe_status) {
+	case KPROBE_HIT_SSDONE:
 #ifdef CONFIG_X86_64
-	} else if (kcb->kprobe_status == KPROBE_HIT_SSDONE) {
-		/* TODO: Provide re-entrancy from post_kprobes_handler() and
-		 * avoid exception stack corruption while single-stepping on
+		/* 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->ip = (unsigned long)p->addr;
 		reset_current_kprobe();
-		return 1;
+		preempt_enable_no_resched();
+		ret = 1;
+		break;
 #endif
+	case KPROBE_HIT_ACTIVE:
+		/* 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;
+		break;
+	case KPROBE_HIT_SS:
+		if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
+			regs->flags &= ~TF_MASK;
+			regs->flags |= kcb->kprobe_saved_flags;
+		} else {
+			/* BUG? */
+		}
+		break;
+	default:
+		/* impossible cases */
+		BUG();
 	}
-	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;
+
+	return ret;
 }
 
 /*
@@ -480,82 +518,67 @@ static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
  */
 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->ip - 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->ip = (unsigned long)addr;
+		return 1;
+	}
 
-	/*
-	 * We don't want to be preempted for the entire
-	 * duration of kprobe processing
-	 */
 	preempt_disable();
 	kcb = get_kprobe_ctlblk();
-
+	cur = kprobe_running();
 	p = get_kprobe(addr);
+
 	if (p) {
-		/* Check we're not actually recursing */
-		if (kprobe_running()) {
+		if (cur) {
 			ret = reenter_kprobe(p, regs, kcb);
-			if (kcb->kprobe_status == KPROBE_REENTER)
-			{
-				ret = 1;
-				goto out;
-			}
-			goto preempt_out;
 		} else {
 			set_current_kprobe(p, regs, kcb);
 			kcb->kprobe_status = KPROBE_HIT_ACTIVE;
-			if (p->pre_handler && p->pre_handler(p, regs))
-			{
-				/* handler set things up, skip ss setup */
-				ret = 1;
-				goto out;
-			}
-		}
-	} else {
-		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.
+			 * 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
+			 * for jprobe processing, so get out doing nothing
+			 * more here.
 			 */
-			regs->ip = (unsigned long)addr;
+			if (!p->pre_handler || !p->pre_handler(p, regs)) {
+				if (setup_boost(p, regs)) {
+					prepare_singlestep(p, regs);
+					kcb->kprobe_status = KPROBE_HIT_SS;
+				}
+			}
 			ret = 1;
-			goto preempt_out;
 		}
-		if (kprobe_running()) {
-			p = __get_cpu_var(current_kprobe);
-			if (p->break_handler && p->break_handler(p, regs))
-				goto ss_probe;
+	} else if (cur) {
+		p = __get_cpu_var(current_kprobe);
+		if (p->break_handler && p->break_handler(p, regs)) {
+			if (setup_boost(p, regs)) {
+				prepare_singlestep(p, regs);
+				kcb->kprobe_status = KPROBE_HIT_SS;
+			}
+			ret = 1;
 		}
-		/* Not one of ours: let kernel handle it */
-		goto preempt_out;
-	}
+	} /* else: not a kprobe fault; let the kernel handle it */
 
-ss_probe:
-	ret = 1;
-#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
-	if (p->ainsn.boostable == 1 && !p->post_handler) {
-		/* Boost up -- we can execute copied instructions directly */
-		reset_current_kprobe();
-		regs->ip = (unsigned long)p->ainsn.insn;
-		goto preempt_out;
-	}
-#endif
-	prepare_singlestep(p, regs);
-	kcb->kprobe_status = KPROBE_HIT_SS;
-	goto out;
-
-preempt_out:
-	preempt_enable_no_resched();
-out:
+	if (!ret)
+		preempt_enable_no_resched();
 	return ret;
 }
 
#4 lore raw

Just a few nitpicks. I need to look closer at the reenter_kprobe changes, but it looks like this should lead to clearer flow than before. The whole !p/kprobe_running() differences were pretty twisty before.

Quoted 24 lines · Abhishek Sagar

Thanks for pointing me to the right tree. I have made some code re-arrangements in this one.

---

arch/x86/kernel/kprobes.c +10 -0
index a72e02b..45adc8e 100644
@@ -441,6 +441,26 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
 	/* Replace the return addr with trampoline addr */
 	*sara = (unsigned long) &kretprobe_trampoline;
 }
+
+#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
+static __always_inline int setup_boost(struct kprobe *p, struct pt_regs *regs)
+{
+	if (p->ainsn.boostable == 1 && !p->post_handler) {
+		/* Boost up -- we can execute copied instructions directly */
+		reset_current_kprobe();
+		regs->ip = (unsigned long)p->ainsn.insn;
+		preempt_enable_no_resched();
+		return 0;

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 463

Quoted 10 lines
arch/x86/kernel/kprobes.c +10 -0
+	}
+	return 1;
+}
+#else
+static __always_inline int setup_boost(struct kprobe *p, struct pt_regs *regs)
+{
+	return 1;
+}
+#endif
+

In the kernel __always_inline == inline, also I think it's nicer to only have one function declaration, and then ifdef the body of the function.

Something like:

static inline int setup_boost(struct kprobe *p, struct pt_regs *regs) {

#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
	if (p->ainsn.boostable == 1 && !p->post_handler) {
		/* Boost up -- we can execute copied instructions directly */
		reset_current_kprobe();
		regs->ip = (unsigned long)p->ainsn.insn;
		preempt_enable_no_resched();
		return 0;
	}
#endif
	return 1;
}
Quoted 82 lines
arch/x86/kernel/kprobes.c +37 -16
 /*
  * We have reentered the kprobe_handler(), since another probe was hit while
  * within the handler. We save the original kprobes variables and just single
@@ -449,29 +469,47 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
 static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
 				    struct kprobe_ctlblk *kcb)
 {
-	if (kcb->kprobe_status == KPROBE_HIT_SS &&
-	    *p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
-		regs->flags &= ~X86_EFLAGS_TF;
-		regs->flags |= kcb->kprobe_saved_flags;
-		return 0;
+	int ret = 0;
+
+	switch (kcb->kprobe_status) {
+	case KPROBE_HIT_SSDONE:
 #ifdef CONFIG_X86_64
-	} else if (kcb->kprobe_status == KPROBE_HIT_SSDONE) {
-		/* TODO: Provide re-entrancy from post_kprobes_handler() and
-		 * avoid exception stack corruption while single-stepping on
+		/* 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->ip = (unsigned long)p->addr;
 		reset_current_kprobe();
-		return 1;
+		preempt_enable_no_resched();
+		ret = 1;
+		break;
 #endif
+	case KPROBE_HIT_ACTIVE:
+		/* 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;
+		break;
+	case KPROBE_HIT_SS:
+		if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
+			regs->flags &= ~TF_MASK;
+			regs->flags |= kcb->kprobe_saved_flags;
+		} else {
+			/* BUG? */
+		}
+		break;
+	default:
+		/* impossible cases */
+		BUG();
 	}
-	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;
+
+	return ret;
 }
 
 /*
@@ -480,82 +518,67 @@ static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
  */
 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->ip - sizeof(kprobe_opcode_t));
+	if (*addr != BREAKPOINT_INSTRUCTION) {
+		/*
+		 * The breakpoint instruction was removed right

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 539

Quoted 10 lines
arch/x86/kernel/kprobes.c +10 -0
+		 * 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->ip = (unsigned long)addr;
+		return 1;
+	}

This return is fine I guess, but after the preempt_disable() I like the goto approach as it will be easier to see what paths enable preemption again and which don't....bonus points if we can move this to the caller or make sure we reenable in all cases before returning and pull in the code in the caller that does this for us.

But I guess your approach of using ret to test whether we need to reenable preemption or not would work as a signal to the caller that they need to reenable preemption.

Quoted 90 lines
arch/x86/kernel/kprobes.c +25 -50
-	/*
-	 * We don't want to be preempted for the entire
-	 * duration of kprobe processing
-	 */
 	preempt_disable();
 	kcb = get_kprobe_ctlblk();
-
+	cur = kprobe_running();
 	p = get_kprobe(addr);
+
 	if (p) {
-		/* Check we're not actually recursing */
-		if (kprobe_running()) {
+		if (cur) {
 			ret = reenter_kprobe(p, regs, kcb);
-			if (kcb->kprobe_status == KPROBE_REENTER)
-			{
-				ret = 1;
-				goto out;
-			}
-			goto preempt_out;
 		} else {
 			set_current_kprobe(p, regs, kcb);
 			kcb->kprobe_status = KPROBE_HIT_ACTIVE;
-			if (p->pre_handler && p->pre_handler(p, regs))
-			{
-				/* handler set things up, skip ss setup */
-				ret = 1;
-				goto out;
-			}
-		}
-	} else {
-		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.
+			 * 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
+			 * for jprobe processing, so get out doing nothing
+			 * more here.
 			 */
-			regs->ip = (unsigned long)addr;
+			if (!p->pre_handler || !p->pre_handler(p, regs)) {
+				if (setup_boost(p, regs)) {
+					prepare_singlestep(p, regs);
+					kcb->kprobe_status = KPROBE_HIT_SS;
+				}
+			}
 			ret = 1;
-			goto preempt_out;
 		}
-		if (kprobe_running()) {
-			p = __get_cpu_var(current_kprobe);
-			if (p->break_handler && p->break_handler(p, regs))
-				goto ss_probe;
+	} else if (cur) {
+		p = __get_cpu_var(current_kprobe);
+		if (p->break_handler && p->break_handler(p, regs)) {
+			if (setup_boost(p, regs)) {
+				prepare_singlestep(p, regs);
+				kcb->kprobe_status = KPROBE_HIT_SS;
+			}
+			ret = 1;
 		}
-		/* Not one of ours: let kernel handle it */
-		goto preempt_out;
-	}
+	} /* else: not a kprobe fault; let the kernel handle it */
 
-ss_probe:
-	ret = 1;
-#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
-	if (p->ainsn.boostable == 1 && !p->post_handler) {
-		/* Boost up -- we can execute copied instructions directly */
-		reset_current_kprobe();
-		regs->ip = (unsigned long)p->ainsn.insn;
-		goto preempt_out;
-	}
-#endif
-	prepare_singlestep(p, regs);
-	kcb->kprobe_status = KPROBE_HIT_SS;

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 584

Quoted 10 lines
arch/x86/kernel/kprobes.c +2 -5
-	goto out;
-
-preempt_out:
-	preempt_enable_no_resched();
-out:
+	if (!ret)
+		preempt_enable_no_resched();
 	return ret;
 }
 

Cheers,

Harvey

#5 lore raw
Quoted 26 lines · Harvey Harrison
Quoted 9 lines
arch/x86/kernel/kprobes.c +9 -0
+#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
+static __always_inline int setup_boost(struct kprobe *p, struct pt_regs *regs)
+{
+     if (p->ainsn.boostable == 1 && !p->post_handler) {
+             /* Boost up -- we can execute copied instructions directly */
+             reset_current_kprobe();
+             regs->ip = (unsigned long)p->ainsn.insn;
+             preempt_enable_no_resched();
+             return 0;

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 463

Quoted 10 lines
arch/x86/kernel/kprobes.c +10 -0
+     }
+     return 1;
+}
+#else
+static __always_inline int setup_boost(struct kprobe *p, struct pt_regs *regs)
+{
+     return 1;
+}
+#endif
+

In the kernel __always_inline == inline, also I think it's nicer to only have one function declaration, and then ifdef the body of the function.

Something like:

static inline int setup_boost(struct kprobe *p, struct pt_regs *regs) {

Quoted 11 lines
#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
        if (p->ainsn.boostable == 1 && !p->post_handler) {
                /* Boost up -- we can execute copied instructions directly */
                reset_current_kprobe();
                regs->ip = (unsigned long)p->ainsn.insn;
                preempt_enable_no_resched();
                return 0;
        }
#endif
        return 1;
}

Ok...will include this after I pick up some more comments.

Quoted 29 lines
Quoted 12 lines

static int __kprobes kprobe_handler(struct pt_regs *regs) {

arch/x86/kernel/kprobes.c +4 -1
-     struct kprobe *p;
      int ret = 0;
      kprobe_opcode_t *addr;
+     struct kprobe *p, *cur;
      struct kprobe_ctlblk *kcb;
 
      addr = (kprobe_opcode_t *)(regs->ip - sizeof(kprobe_opcode_t));
+     if (*addr != BREAKPOINT_INSTRUCTION) {
+             /*
+              * The breakpoint instruction was removed right

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 539

Quoted 10 lines
arch/x86/kernel/kprobes.c +10 -0
+              * 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->ip = (unsigned long)addr;
+             return 1;
+     }

This return is fine I guess, but after the preempt_disable() I like the goto approach as it will be easier to see what paths enable preemption again and which don't....bonus points if we can move this to the caller or make sure we reenable in all cases before returning and pull in the code in the caller that does this for us.

Quoted 3 lines

But I guess your approach of using ret to test whether we need to reenable preemption or not would work as a signal to the caller that they need to reenable preemption.

Hmm...since enabling preemption is tied to 'ret', anyone reading kprobe_handler will have to follow around all calls which modify it. There are some checks in the current kprobe_handler definition made just to do what you're saying, i.e, to push all preemption enable/disables in krpobe_handler. LIke this one (from the current x86 kprobe_handler):

------------
ret = reenter_kprobe(p, regs, kcb);
if (kcb->kprobe_status == KPROBE_REENTER)

{
    ret = 1;
    goto out;
}

goto preempt_out;

-------------

This is just confusing because we're not actually making any exceptions here for the KPROBE_REENTER case (which has been partially handled in reenter_kprobe), rather just tricking our way out of preemption enabling for a cpl of cases in reenter_kprobe.

Quoted 3 lines

Cheers,

Harvey

Thanks,
Abhishek

#6 lore raw
In reply to Abhishek Sagar

Hi Abhishek,

Thank you for good work.

Quoted 13 lines · Abhishek Sagar
arch/x86/kernel/kprobes.c +9 -0
@@ -441,6 +441,26 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
 	/* Replace the return addr with trampoline addr */
 	*sara = (unsigned long) &kretprobe_trampoline;
 }
+
+#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
+static __always_inline int setup_boost(struct kprobe *p, struct pt_regs *regs)
+{
+	if (p->ainsn.boostable == 1 && !p->post_handler) {
+		/* Boost up -- we can execute copied instructions directly */
+		reset_current_kprobe();
+		regs->ip = (unsigned long)p->ainsn.insn;
+		preempt_enable_no_resched();

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 462

Quoted 10 lines
arch/x86/kernel/kprobes.c +10 -0
+		return 0;
+	}
+	return 1;
+}
+#else
+static __always_inline int setup_boost(struct kprobe *p, struct pt_regs *regs)
+{
+	return 1;
+}
+#endif

I think setup_singlestep() in your first patch is better, because it avoided code duplication(*).

Quoted 14 lines
arch/x86/kernel/kprobes.c +2 -5
+
 /*
  * We have reentered the kprobe_handler(), since another probe was hit while
  * within the handler. We save the original kprobes variables and just single
@@ -449,29 +469,47 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
 static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
 				    struct kprobe_ctlblk *kcb)
 {
-	if (kcb->kprobe_status == KPROBE_HIT_SS &&
-	    *p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
-		regs->flags &= ~X86_EFLAGS_TF;
-		regs->flags |= kcb->kprobe_saved_flags;
-		return 0;
+	int ret = 0;

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 479

Quoted 10 lines
arch/x86/kernel/kprobes.c +6 -3
+
+	switch (kcb->kprobe_status) {
+	case KPROBE_HIT_SSDONE:
 #ifdef CONFIG_X86_64
-	} else if (kcb->kprobe_status == KPROBE_HIT_SSDONE) {
-		/* TODO: Provide re-entrancy from post_kprobes_handler() and
-		 * avoid exception stack corruption while single-stepping on
+		/* TODO: Provide re-entrancy from
+		 * post_kprobes_handler() and avoid exception
+		 * stack corruption while single-stepping on

Why would you modify it?

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 485

Quoted 7 lines
arch/x86/kernel/kprobes.c +1 -1
 		 * the instruction of the new probe.
 		 */
 		arch_disarm_kprobe(p);
 		regs->ip = (unsigned long)p->addr;
 		reset_current_kprobe();
-		return 1;
+		preempt_enable_no_resched();

I think preepmt_enable here is good idea.

Quoted 11 lines
arch/x86/kernel/kprobes.c +10 -0
+		ret = 1;
+		break;
 #endif
+	case KPROBE_HIT_ACTIVE:
+		/* 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;

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 506

Quoted 10 lines
arch/x86/kernel/kprobes.c +10 -0
+		ret = 1;
+		break;
+	case KPROBE_HIT_SS:
+		if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
+			regs->flags &= ~TF_MASK;
+			regs->flags |= kcb->kprobe_saved_flags;
+		} else {
+			/* BUG? */
+		}
+		break;

If my thought is correct, we don't need to use swich-case here, Because there are only 2 cases, KPROBE_HIT_SSDONE (x86-64 only) or others.
As a result, this function just setups re-entrance.

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 509

Quoted 3 lines
arch/x86/kernel/kprobes.c +3 -0
+	default:
+		/* impossible cases */
+		BUG();

I think no need to check that here.

Quoted 26 lines
arch/x86/kernel/kprobes.c +6 -7
 	}
-	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;
+
+	return ret;
 }
 
 /*
@@ -480,82 +518,67 @@ static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
  */
 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->ip - sizeof(kprobe_opcode_t));
+	if (*addr != BREAKPOINT_INSTRUCTION) {
+		/*
+		 * The breakpoint instruction was removed right

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 539

Quoted 10 lines
arch/x86/kernel/kprobes.c +10 -0
+		 * 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->ip = (unsigned long)addr;
+		return 1;
+	}

I think this block changing would better be separated from this patch, because it changes code flow logically.

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 543

Quoted 9 lines
arch/x86/kernel/kprobes.c +1 -5
-	/*
-	 * We don't want to be preempted for the entire
-	 * duration of kprobe processing
-	 */
 	preempt_disable();
 	kcb = get_kprobe_ctlblk();
-
+	cur = kprobe_running();

I think you don't need "cur", because kprobe_running() is called just once on each path.

Quoted 36 lines
arch/x86/kernel/kprobes.c +5 -24
 	p = get_kprobe(addr);
+
 	if (p) {
-		/* Check we're not actually recursing */
-		if (kprobe_running()) {
+		if (cur) {
 			ret = reenter_kprobe(p, regs, kcb);
-			if (kcb->kprobe_status == KPROBE_REENTER)
-			{
-				ret = 1;
-				goto out;
-			}
-			goto preempt_out;
 		} else {
 			set_current_kprobe(p, regs, kcb);
 			kcb->kprobe_status = KPROBE_HIT_ACTIVE;
-			if (p->pre_handler && p->pre_handler(p, regs))
-			{
-				/* handler set things up, skip ss setup */
-				ret = 1;
-				goto out;
-			}
-		}
-	} else {
-		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.
+			 * If we have no pre-handler or it returned 0, we
+			 * continue with normal processing.  If we have a

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 564

Quoted 10 lines
arch/x86/kernel/kprobes.c +8 -1
+			 * pre-handler and it returned non-zero, it prepped
+			 * for calling the break_handler below on re-entry
+			 * for jprobe processing, so get out doing nothing
+			 * more here.
 			 */
-			regs->ip = (unsigned long)addr;
+			if (!p->pre_handler || !p->pre_handler(p, regs)) {
+				if (setup_boost(p, regs)) {
+					prepare_singlestep(p, regs);
+					kcb->kprobe_status = KPROBE_HIT_SS;

(*) duplication 1

Quoted 5 lines
arch/x86/kernel/kprobes.c +2 -1
+				}
+			}
 			ret = 1;
-			goto preempt_out;
 		}

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 574

Quoted 10 lines
arch/x86/kernel/kprobes.c +6 -4
-		if (kprobe_running()) {
-			p = __get_cpu_var(current_kprobe);
-			if (p->break_handler && p->break_handler(p, regs))
-				goto ss_probe;
+	} else if (cur) {
+		p = __get_cpu_var(current_kprobe);
+		if (p->break_handler && p->break_handler(p, regs)) {
+			if (setup_boost(p, regs)) {
+				prepare_singlestep(p, regs);
+				kcb->kprobe_status = KPROBE_HIT_SS;

(*) duplication 2

Quoted 17 lines
arch/x86/kernel/kprobes.c +3 -12
+			}
+			ret = 1;
 		}
-		/* Not one of ours: let kernel handle it */
-		goto preempt_out;
-	}
+	} /* else: not a kprobe fault; let the kernel handle it */
 
-ss_probe:
-	ret = 1;
-#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
-	if (p->ainsn.boostable == 1 && !p->post_handler) {
-		/* Boost up -- we can execute copied instructions directly */
-		reset_current_kprobe();
-		regs->ip = (unsigned long)p->ainsn.insn;
-		goto preempt_out;
-	}

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 581

Quoted 10 lines
arch/x86/kernel/kprobes.c +2 -8
-#endif
-	prepare_singlestep(p, regs);
-	kcb->kprobe_status = KPROBE_HIT_SS;
-	goto out;
-
-preempt_out:
-	preempt_enable_no_resched();
-out:
+	if (!ret)
+		preempt_enable_no_resched();

I think this is a bit ugly...
Why would you avoid using mutiple "return"s in this function? I think you can remove "ret" from this function.

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,

Quoted 4 lines
arch/x86/kernel/kprobes.c +0 -0
 	return ret;
 }
 
 

Thank you,

Signature · 7 lines
Masami Hiramatsu

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

e-mail: [email protected], [email protected]
#7 lore raw
Quoted 2 lines · Masami Hiramatsu

I think setup_singlestep() in your first patch is better, because it avoided code duplication(*).

Will retain it then.

Quoted 20 lines
Quoted 9 lines

static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
struct kprobe_ctlblk *kcb)
{

arch/x86/kernel/kprobes.c +1 -5
-     if (kcb->kprobe_status == KPROBE_HIT_SS &&
-         *p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
-             regs->flags &= ~X86_EFLAGS_TF;
-             regs->flags |= kcb->kprobe_saved_flags;
-             return 0;
+     int ret = 0;

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 479

Quoted 10 lines
arch/x86/kernel/kprobes.c +6 -3
+
+     switch (kcb->kprobe_status) {
+     case KPROBE_HIT_SSDONE:
 #ifdef CONFIG_X86_64
-     } else if (kcb->kprobe_status == KPROBE_HIT_SSDONE) {
-             /* TODO: Provide re-entrancy from post_kprobes_handler() and
-              * avoid exception stack corruption while single-stepping on
+             /* TODO: Provide re-entrancy from
+              * post_kprobes_handler() and avoid exception
+              * stack corruption while single-stepping on
Quoted 1 line

Why would you modify it?

Do you mean the comment? I had this code in kprobe_handler earlier and it consistently exceeded 80 columns in my case. Will fix it anyways.

Quoted 22 lines
Quoted 11 lines
arch/x86/kernel/kprobes.c +10 -0
+             ret = 1;
+             break;
 #endif
+     case KPROBE_HIT_ACTIVE:
+             /* 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;

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 506

Quoted 10 lines
arch/x86/kernel/kprobes.c +10 -0
+             ret = 1;
+             break;
+     case KPROBE_HIT_SS:
+             if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
+                     regs->flags &= ~TF_MASK;
+                     regs->flags |= kcb->kprobe_saved_flags;
+             } else {
+                     /* BUG? */
+             }
+             break;
Quoted 4 lines

If my thought is correct, we don't need to use swich-case here, Because there are only 2 cases, KPROBE_HIT_SSDONE (x86-64 only) or others.
As a result, this function just setups re-entrance.

As you've also pointed out in your previous reply, this case is peculiar and therefore I believe it should be marked as a BUG(). I've left the original case, if (kcb->kprobe_status==KPROBE_HIT_SS) && (*p->ainsn.insn == BREAKPOINT_INSTRUCTION), untouched and is handled as it was before. However, if (kcb->kprobe_status==KPROBE_HIT_SS) && !(*p->ainsn.insn == BREAKPOINT_INSTRUCTION), then instead of incrementing nmissed count like before, it should cry out a BUG. This is not an ordinary recursive probe handling case which should update the nmissed count.

Quoted 5 lines

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 509

Quoted 3 lines
arch/x86/kernel/kprobes.c +3 -0
+     default:
+             /* impossible cases */
+             BUG();

I think no need to check that here.

It may not get hit at runtime but is quite informative.

Quoted 23 lines
Quoted 12 lines

static int __kprobes kprobe_handler(struct pt_regs *regs) {

arch/x86/kernel/kprobes.c +4 -1
-     struct kprobe *p;
      int ret = 0;
      kprobe_opcode_t *addr;
+     struct kprobe *p, *cur;
      struct kprobe_ctlblk *kcb;
 
      addr = (kprobe_opcode_t *)(regs->ip - sizeof(kprobe_opcode_t));
+     if (*addr != BREAKPOINT_INSTRUCTION) {
+             /*
+              * The breakpoint instruction was removed right

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 539

Quoted 10 lines
arch/x86/kernel/kprobes.c +10 -0
+              * 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->ip = (unsigned long)addr;
+             return 1;
+     }
Quoted 2 lines

I think this block changing would better be separated from this patch, because it changes code flow logically.

Agreed. I'll address this in another thread, but for now it is safest to check it for all cases right at the entry of kprobe_handler().

Quoted 10 lines

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 543

Quoted 9 lines
arch/x86/kernel/kprobes.c +1 -5
-     /*
-      * We don't want to be preempted for the entire
-      * duration of kprobe processing
-      */
      preempt_disable();
      kcb = get_kprobe_ctlblk();
-
+     cur = kprobe_running();
Quoted 2 lines

I think you don't need "cur", because kprobe_running() is called just once on each path.

Will get rid of that.

Quoted 24 lines

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 564

Quoted 5 lines
arch/x86/kernel/kprobes.c +4 -1
-                     regs->ip = (unsigned long)addr;
+                     if (!p->pre_handler || !p->pre_handler(p, regs)) {
+                             if (setup_boost(p, regs)) {
+                                     prepare_singlestep(p, regs);
+                                     kcb->kprobe_status = KPROBE_HIT_SS;

(*) duplication 1

Quoted 5 lines
arch/x86/kernel/kprobes.c +2 -1
+                             }
+                     }
                      ret = 1;
-                     goto preempt_out;
              }

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 574

Quoted 10 lines
arch/x86/kernel/kprobes.c +6 -4
-             if (kprobe_running()) {
-                     p = __get_cpu_var(current_kprobe);
-                     if (p->break_handler && p->break_handler(p, regs))
-                             goto ss_probe;
+     } else if (cur) {
+             p = __get_cpu_var(current_kprobe);
+             if (p->break_handler && p->break_handler(p, regs)) {
+                     if (setup_boost(p, regs)) {
+                             prepare_singlestep(p, regs);
+                             kcb->kprobe_status = KPROBE_HIT_SS;
Quoted 1 line

(*) duplication 2

Will replace it with setup_singlestep().

Quoted 23 lines
Quoted 23 lines
arch/x86/kernel/kprobes.c +3 -18
+                     }
+                     ret = 1;
              }
-             /* Not one of ours: let kernel handle it */
-             goto preempt_out;
-     }
+     } /* else: not a kprobe fault; let the kernel handle it */
 
-ss_probe:
-     ret = 1;
-#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
-     if (p->ainsn.boostable == 1 && !p->post_handler) {
-             /* Boost up -- we can execute copied instructions directly */
-             reset_current_kprobe();
-             regs->ip = (unsigned long)p->ainsn.insn;
-             goto preempt_out;
-     }
-#endif
-     prepare_singlestep(p, regs);
-     kcb->kprobe_status = KPROBE_HIT_SS;
-     goto out;
-
-preempt_out:
Quoted 8 lines

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 581

Quoted 4 lines
arch/x86/kernel/kprobes.c +2 -2
-     preempt_enable_no_resched();
-out:
+     if (!ret)
+             preempt_enable_no_resched();

I think this is a bit ugly...
Why would you avoid using mutiple "return"s in this function? I think you can remove "ret" from this function.

Hmm...there the are no deeply nested if-elses nor overlapping paths which need to be avoided. All the nested checks unroll cleanly too.

Quoted 10 lines

Thank you,

--
Masami Hiramatsu

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

e-mail: [email protected], [email protected]

--

Thanks for your review!
Abhishek

#8 lore raw
Quoted 7 lines · Abhishek Sagar
Quoted 5 lines

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 509

Quoted 3 lines
arch/x86/kernel/kprobes.c +3 -0
+     default:
+             /* impossible cases */
+             BUG();

I think no need to check that here.

It may not get hit at runtime but is quite informative.

sidenote: please use WARN_ON(1) instead. In the case of the impossible triggering somehow (the Linux kernel is notorious for that ;-), we'd have a nice WARN_ON(1) in the dmesg, instead of a crashed box.

Ingo

#9 lore raw
In reply to Abhishek Sagar

Hi Abhishek,

Quoted 5 lines · Abhishek Sagar
Quoted 2 lines · Masami Hiramatsu

I think setup_singlestep() in your first patch is better, because it avoided code duplication(*).

Will retain it then.

Thank you very much.

Quoted 19 lines
Quoted 19 lines
Quoted 19 lines

static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
struct kprobe_ctlblk *kcb)
{

arch/x86/kernel/kprobes.c +7 -8
-     if (kcb->kprobe_status == KPROBE_HIT_SS &&
-         *p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
-             regs->flags &= ~X86_EFLAGS_TF;
-             regs->flags |= kcb->kprobe_saved_flags;
-             return 0;
+     int ret = 0;
+
+     switch (kcb->kprobe_status) {
+     case KPROBE_HIT_SSDONE:
 #ifdef CONFIG_X86_64
-     } else if (kcb->kprobe_status == KPROBE_HIT_SSDONE) {
-             /* TODO: Provide re-entrancy from post_kprobes_handler() and
-              * avoid exception stack corruption while single-stepping on
+             /* TODO: Provide re-entrancy from
+              * post_kprobes_handler() and avoid exception
+              * stack corruption while single-stepping on
Quoted 4 lines
Quoted 1 line

Why would you modify it?

Do you mean the comment? I had this code in kprobe_handler earlier and it consistently exceeded 80 columns in my case. Will fix it anyways.

Yes, my previous patch already took care of it. Thanks.

Quoted 26 lines
Quoted 21 lines
Quoted 11 lines
arch/x86/kernel/kprobes.c +10 -0
+             ret = 1;
+             break;
 #endif
+     case KPROBE_HIT_ACTIVE:
+             /* 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;

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 506

Quoted 10 lines
arch/x86/kernel/kprobes.c +10 -0
+             ret = 1;
+             break;
+     case KPROBE_HIT_SS:
+             if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
+                     regs->flags &= ~TF_MASK;
+                     regs->flags |= kcb->kprobe_saved_flags;
+             } else {
+                     /* BUG? */
+             }
+             break;
Quoted 4 lines

If my thought is correct, we don't need to use swich-case here, Because there are only 2 cases, KPROBE_HIT_SSDONE (x86-64 only) or others.
As a result, this function just setups re-entrance.

Quoted 9 lines

As you've also pointed out in your previous reply, this case is peculiar and therefore I believe it should be marked as a BUG(). I've left the original case, if (kcb->kprobe_status==KPROBE_HIT_SS) && (*p->ainsn.insn == BREAKPOINT_INSTRUCTION), untouched and is handled as it was before. However, if (kcb->kprobe_status==KPROBE_HIT_SS) && !(*p->ainsn.insn == BREAKPOINT_INSTRUCTION), then instead of incrementing nmissed count like before, it should cry out a BUG. This is not an ordinary recursive probe handling case which should update the nmissed count.

Hmm, I can not agree, because it is possible to insert a kprobe into kprobe's instruction buffer. If it should be a bug, we must check it when registering the kprobe.
(And also, in *p->ainsn.insn == BREAKPOINT_INSTRUCTION case, I doubt that the kernel can handle this "orphaned" breakpoint, because the breakpoint address has been changed.)

Anyway, if you would like to change the logic, please separate it from cleanup patch.

Quoted 6 lines
Quoted 4 lines

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 509

Quoted 3 lines
arch/x86/kernel/kprobes.c +3 -0
+     default:
+             /* impossible cases */
+             BUG();

I think no need to check that here.

It may not get hit at runtime but is quite informative.

OK, I see.

Quoted 23 lines
Quoted 23 lines
Quoted 23 lines

static int __kprobes kprobe_handler(struct pt_regs *regs) {

arch/x86/kernel/kprobes.c +14 -1
-     struct kprobe *p;
      int ret = 0;
      kprobe_opcode_t *addr;
+     struct kprobe *p, *cur;
      struct kprobe_ctlblk *kcb;
 
      addr = (kprobe_opcode_t *)(regs->ip - 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->ip = (unsigned long)addr;
+             return 1;
+     }
Quoted 5 lines
Quoted 2 lines

I think this block changing would better be separated from this patch, because it changes code flow logically.

Agreed. I'll address this in another thread, but for now it is safest to check it for all cases right at the entry of kprobe_handler().

Thanks,

Quoted 23 lines
Quoted 14 lines
Quoted 14 lines
arch/x86/kernel/kprobes.c +0 -14
-ss_probe:
-     ret = 1;
-#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
-     if (p->ainsn.boostable == 1 && !p->post_handler) {
-             /* Boost up -- we can execute copied instructions directly */
-             reset_current_kprobe();
-             regs->ip = (unsigned long)p->ainsn.insn;
-             goto preempt_out;
-     }
-#endif
-     prepare_singlestep(p, regs);
-     kcb->kprobe_status = KPROBE_HIT_SS;
-     goto out;
-
Quoted 8 lines

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 581

Quoted 5 lines
arch/x86/kernel/kprobes.c +2 -3
-preempt_out:
-     preempt_enable_no_resched();
-out:
+     if (!ret)
+             preempt_enable_no_resched();

I think this is a bit ugly...
Why would you avoid using mutiple "return"s in this function? I think you can remove "ret" from this function.

Quoted 2 lines

Hmm...there the are no deeply nested if-elses nor overlapping paths which need to be avoided. All the nested checks unroll cleanly too.

Oh, I just mentioned about this "if (!ret)" condition. Could you try to remove this "ret" variable?
I think some "ret=1" could be replaced by "return 1" easily.

Thank you,

Quoted 3 lines

Thanks for your review!
Abhishek

Signature · 7 lines
Masami Hiramatsu

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

e-mail: [email protected], [email protected]
#10 lore raw

Hi Abhishek,

Masami Hiramatsu wrote:
...

Quoted 22 lines
Quoted 12 lines
Quoted 8 lines

arch/x86/kernel/kprobes.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 506

Quoted 8 lines
arch/x86/kernel/kprobes.c +8 -0
+     case KPROBE_HIT_SS:
+             if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
+                     regs->flags &= ~TF_MASK;
+                     regs->flags |= kcb->kprobe_saved_flags;
+             } else {
+                     /* BUG? */
+             }
+             break;
Quoted 4 lines

If my thought is correct, we don't need to use swich-case here, Because there are only 2 cases, KPROBE_HIT_SSDONE (x86-64 only) or others.
As a result, this function just setups re-entrance.

Quoted 9 lines

As you've also pointed out in your previous reply, this case is peculiar and therefore I believe it should be marked as a BUG(). I've left the original case, if (kcb->kprobe_status==KPROBE_HIT_SS) && (*p->ainsn.insn == BREAKPOINT_INSTRUCTION), untouched and is handled as it was before. However, if (kcb->kprobe_status==KPROBE_HIT_SS) && !(*p->ainsn.insn == BREAKPOINT_INSTRUCTION), then instead of incrementing nmissed count like before, it should cry out a BUG. This is not an ordinary recursive probe handling case which should update the nmissed count.

Quoted 3 lines

Hmm, I can not agree, because it is possible to insert a kprobe into kprobe's instruction buffer. If it should be a bug, we must check it when registering the kprobe.

I discussed it with other maintainers and knew that current kprobes does not allow user to insert a kprobe to another kprobe's instruction buffer, because register_kprobe ensures the insertion address is text. Now I changed my mind. I think that case (p && kprobe_running() && kcb->kprobe_status==KPROBE_HIT_SS) is BUG(), even if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION).

Quoted 3 lines

(And also, in *p->ainsn.insn == BREAKPOINT_INSTRUCTION case, I doubt that the kernel can handle this "orphaned" breakpoint, because the breakpoint address has been changed.)

I also changed my mind. In this case, the kernel debugger can retrieve correct breakpoint address by using kprobe_running() as below.
---
kp = kprobe_running();
if (kp)

	addr = kp->addr;
else
	addr = regs->ip;

---

The last discussion point is that we should restore flags or not if (!p && kprobe_running() && kcb->kprobe_status==KPROBE_HIT_SS). I think we do not need to do that if the debugger premises that kprobes exists.

Thank you,

Signature · 7 lines
Masami Hiramatsu

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

e-mail: [email protected], [email protected]
#11 lore raw

Hi Abhishek,

Quoted 10 lines · Masami Hiramatsu
Quoted 3 lines

Hmm, I can not agree, because it is possible to insert a kprobe into kprobe's instruction buffer. If it should be a bug, we must check it when registering the kprobe.

I discussed it with other maintainers and knew that current kprobes does not allow user to insert a kprobe to another kprobe's instruction buffer, because register_kprobe ensures the insertion address is text. Now I changed my mind. I think that case (p && kprobe_running() && kcb->kprobe_status==KPROBE_HIT_SS) is BUG(), even if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION).

I could understand what the original code did at last.
If a kprobe is inserted on a breakpoint which other debugger inserts, it single step inline instead of out-of-line.(this is done in prepare_singlestep) In this case, (p && kprobe_running() && kcb->kprobe_status==KPROBE_HIT_SS) is true and we need pass the control to the debugger.
And if (*p->ainsn.insn != BREAKPOINT_INSTRUCTION) (or (p != kprobe_running())) in that case, there may be some bugs.

Now I think your original suggestion is correct.
Please fix it in another patch.

Thank you very much,

Signature · 7 lines
Masami Hiramatsu

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

e-mail: [email protected], [email protected]
#12 lore raw
Quoted 7 lines · Masami Hiramatsu

I could understand what the original code did at last.
If a kprobe is inserted on a breakpoint which other debugger inserts, it single step inline instead of out-of-line.(this is done in prepare_singlestep) In this case, (p && kprobe_running() && kcb->kprobe_status==KPROBE_HIT_SS) is true and we need pass the control to the debugger.
And if (*p->ainsn.insn != BREAKPOINT_INSTRUCTION) (or (p != kprobe_running())) in that case, there may be some bugs.

Yes, we can only fault while singlestepping for a unique case, which is when we're singlestepping (in-line) a breakpoint because a probe was installed on it. All other scenarios are a BUG . That's also assuming that no exception will preempt singlestepping, whose codepath has a probe on it.

Quoted 2 lines

Now I think your original suggestion is correct.
Please fix it in another patch.

Ok.

Quoted 8 lines

--
Masami Hiramatsu

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

e-mail: [email protected], [email protected]

Thanks,
Abhishek Sagar

#13 lore raw
In reply to Masami Hiramatsu
Quoted 18 lines · Masami Hiramatsu
Quoted 10 lines
Quoted 1 line

As a result, this function just setups re-entrance.

As you've also pointed out in your previous reply, this case is peculiar and therefore I believe it should be marked as a BUG(). I've left the original case, if (kcb->kprobe_status==KPROBE_HIT_SS) && (*p->ainsn.insn == BREAKPOINT_INSTRUCTION), untouched and is handled as it was before. However, if (kcb->kprobe_status==KPROBE_HIT_SS) && !(*p->ainsn.insn == BREAKPOINT_INSTRUCTION), then instead of incrementing nmissed count like before, it should cry out a BUG. This is not an ordinary recursive probe handling case which should update the nmissed count.

Hmm, I can not agree, because it is possible to insert a kprobe into kprobe's instruction buffer. If it should be a bug, we must check it when registering the kprobe.
(And also, in *p->ainsn.insn == BREAKPOINT_INSTRUCTION case, I doubt that the kernel can handle this "orphaned" breakpoint, because the breakpoint address has been changed.)

Quoted 2 lines

Anyway, if you would like to change the logic, please separate it from cleanup patch.

Okay. For now, I've restored the original behavior.

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

Review

Quoted 3 lines
(quoted diff) +2 -1
-out:
+     if (!ret)
+             preempt_enable_no_resched();

I think this is a bit ugly...
Why would you avoid using mutiple "return"s in this function? I think you can remove "ret" from this function.

Quoted 6 lines
Quoted 2 lines

Hmm...there the are no deeply nested if-elses nor overlapping paths which need to be avoided. All the nested checks unroll cleanly too.

Oh, I just mentioned about this "if (!ret)" condition. Could you try to remove this "ret" variable?
I think some "ret=1" could be replaced by "return 1" easily.

Done. You should find the desired changed in this patch.

---

arch/x86/kernel/kprobes.c +86 -67
index a72e02b..06f8d08 100644
@@ -441,6 +441,34 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
 	/* Replace the return addr with trampoline addr */
 	*sara = (unsigned long) &kretprobe_trampoline;
 }
+
+static void __kprobes recursive_singlestep(struct kprobe *p,
+					   struct pt_regs *regs,
+					   struct kprobe_ctlblk *kcb)
+{
+	save_previous_kprobe(kcb);
+	set_current_kprobe(p, regs, kcb);
+	kprobes_inc_nmissed_count(p);
+	prepare_singlestep(p, regs);
+	kcb->kprobe_status = KPROBE_REENTER;
+}
+
+static void __kprobes setup_singlestep(struct kprobe *p, struct pt_regs *regs,
+				       struct kprobe_ctlblk *kcb)
+{
+#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
+	if (p->ainsn.boostable == 1 && !p->post_handler) {
+		/* Boost up -- we can execute copied instructions directly */
+		reset_current_kprobe();
+		regs->ip = (unsigned long)p->ainsn.insn;
+		preempt_enable_no_resched();
+		return;
+	}
+#endif
+	prepare_singlestep(p, regs);
+	kcb->kprobe_status = KPROBE_HIT_SS;
+}
+
 /*
  * We have reentered the kprobe_handler(), since another probe was hit while
  * within the handler. We save the original kprobes variables and just single
@@ -449,13 +477,9 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
 static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
 				    struct kprobe_ctlblk *kcb)
 {
-	if (kcb->kprobe_status == KPROBE_HIT_SS &&
-	    *p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
-		regs->flags &= ~X86_EFLAGS_TF;
-		regs->flags |= kcb->kprobe_saved_flags;
-		return 0;
+	switch (kcb->kprobe_status) {
+	case KPROBE_HIT_SSDONE:
 #ifdef CONFIG_X86_64
-	} else if (kcb->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.
@@ -463,14 +487,26 @@ static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
 		arch_disarm_kprobe(p);
 		regs->ip = (unsigned long)p->addr;
 		reset_current_kprobe();
-		return 1;
+		preempt_enable_no_resched();
+		break;
 #endif
+	case KPROBE_HIT_ACTIVE:
+		recursive_singlestep(p, regs, kcb);
+		break;
+	case KPROBE_HIT_SS:
+		if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
+			regs->flags &= ~TF_MASK;
+			regs->flags |= kcb->kprobe_saved_flags;
+			return 0;
+		} else {
+			recursive_singlestep(p, regs, kcb);
+		}
+		break;
+	default:
+		/* impossible cases */
+		WARN_ON(1);
 	}
-	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;
 }
 
@@ -480,83 +516,66 @@ static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
  */
 static int __kprobes kprobe_handler(struct pt_regs *regs)
 {
-	struct kprobe *p;
-	int ret = 0;
 	kprobe_opcode_t *addr;
+	struct kprobe *p;
 	struct kprobe_ctlblk *kcb;
 
 	addr = (kprobe_opcode_t *)(regs->ip - 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->ip = (unsigned long)addr;
+		return 1;
+	}
 
 	/*
 	 * We don't want to be preempted for the entire
-	 * duration of kprobe processing
+	 * duration of kprobe processing. We conditionally
+	 * re-enable preemption at the end of this function,
+	 * and also in reenter_kprobe() and setup_singlestep().
 	 */
 	preempt_disable();
-	kcb = get_kprobe_ctlblk();
 
+	kcb = get_kprobe_ctlblk();
 	p = get_kprobe(addr);
+
 	if (p) {
-		/* Check we're not actually recursing */
 		if (kprobe_running()) {
-			ret = reenter_kprobe(p, regs, kcb);
-			if (kcb->kprobe_status == KPROBE_REENTER)
-			{
-				ret = 1;
-				goto out;
-			}
-			goto preempt_out;
+			if (reenter_kprobe(p, regs, kcb))
+				return 1;
 		} else {
 			set_current_kprobe(p, regs, kcb);
 			kcb->kprobe_status = KPROBE_HIT_ACTIVE;
-			if (p->pre_handler && p->pre_handler(p, regs))
-			{
-				/* handler set things up, skip ss setup */
-				ret = 1;
-				goto out;
-			}
-		}
-	} else {
-		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.
+			 * 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
+			 * for jprobe processing, so get out doing nothing
+			 * more here.
 			 */
-			regs->ip = (unsigned long)addr;
-			ret = 1;
-			goto preempt_out;
+			if (!p->pre_handler || !p->pre_handler(p, regs))
+				setup_singlestep(p, regs, kcb);
+			return 1;
 		}
-		if (kprobe_running()) {
-			p = __get_cpu_var(current_kprobe);
-			if (p->break_handler && p->break_handler(p, regs))
-				goto ss_probe;
+	} else if (kprobe_running()) {
+		p = __get_cpu_var(current_kprobe);
+		if (p->break_handler && p->break_handler(p, regs)) {
+			setup_singlestep(p, regs, kcb);
+			return 1;
 		}
-		/* Not one of ours: let kernel handle it */
-		goto preempt_out;
-	}
+	} /* else: not a kprobe fault; let the kernel handle it */
 
-ss_probe:
-	ret = 1;
-#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
-	if (p->ainsn.boostable == 1 && !p->post_handler) {
-		/* Boost up -- we can execute copied instructions directly */
-		reset_current_kprobe();
-		regs->ip = (unsigned long)p->ainsn.insn;
-		goto preempt_out;
-	}
-#endif
-	prepare_singlestep(p, regs);
-	kcb->kprobe_status = KPROBE_HIT_SS;
-	goto out;
-
-preempt_out:
 	preempt_enable_no_resched();
-out:
-	return ret;
+	return 0;
 }
 
 /*
#14 lore raw
Quoted 1 line · Abhishek Sagar

Masami Hiramatsu wrote:

...

Quoted 1 line

Done. You should find the desired changed in this patch.

Well done!
This cleans it up very well.
I have just one more comment.

Quoted 13 lines
arch/x86/kernel/kprobes.c +7 -1
@@ -463,14 +487,26 @@ static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
 		arch_disarm_kprobe(p);
 		regs->ip = (unsigned long)p->addr;
 		reset_current_kprobe();
-		return 1;
+		preempt_enable_no_resched();
+		break;
 #endif
+	case KPROBE_HIT_ACTIVE:
+		recursive_singlestep(p, regs, kcb);
+		break;
+	case KPROBE_HIT_SS:
+		if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION) {

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 507

Quoted 10 lines
arch/x86/kernel/kprobes.c +10 -0
+			regs->flags &= ~TF_MASK;
+			regs->flags |= kcb->kprobe_saved_flags;
+			return 0;
+		} else {
+			recursive_singlestep(p, regs, kcb);
+		}
+		break;
+	default:
+		/* impossible cases */
+		WARN_ON(1);

WARN_ON() does not call panic(). Since the kernel doesn't stop, we need to prepare singlestep after that.

How about this?
---
+ case KPROBE_HIT_ACTIVE:
+ break;
+ case KPROBE_HIT_SS:
+ if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
+ regs->flags &= ~TF_MASK;
+ regs->flags |= kcb->kprobe_saved_flags;
+ return 0;
+ }
+ break;
+ default:
+ /* impossible cases */
+ WARN_ON(1);
}
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;
}
---

Thank you,

Signature · 7 lines
Masami Hiramatsu

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

e-mail: [email protected], [email protected]
#15 lore raw
Quoted 13 lines · Masami Hiramatsu

arch/x86/kernel/kprobes.c static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, · line 507

Quoted 12 lines
arch/x86/kernel/kprobes.c +12 -0
+     case KPROBE_HIT_SS:
+             if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
+                     regs->flags &= ~TF_MASK;
+                     regs->flags |= kcb->kprobe_saved_flags;
+                     return 0;
+             } else {
+                     recursive_singlestep(p, regs, kcb);
+             }
+             break;
+     default:
+             /* impossible cases */
+             WARN_ON(1);
Quoted 2 lines

WARN_ON() does not call panic(). Since the kernel doesn't stop, we need to prepare singlestep after that.

We shouldn't panic in 'default'. First, we'll never hit this case, and if we do then we can be sure that the fault is not due to a probe. So instead of singlestepping we'll let the kernel handle it. If it cant, it'll panic/die() for us. I'll try to cleanup this case and incorporate these suggestions in a separate patch, as u suggested.

Quoted 7 lines

Masami Hiramatsu

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

e-mail: [email protected], [email protected]

--
Thanks,
Abhishek Sagar

#16 lore raw
In reply to Abhishek Sagar

Hello Abhishek,

Quoted 11 lines · Abhishek Sagar
Quoted 5 lines · Harvey Harrison

Make the control flow of kprobe_handler more obvious.

Collapse the separate if blocks/gotos with if/else blocks this unifies the duplication of the check for a breakpoint instruction race with another cpu.

This is a patch derived from kprobe_handler of the ARM kprobes port. This further simplifies the current x86 kprobe_handler. The resulting definition is smaller, more readable, has no goto's and contains only a single call to get_kprobe.

Thank you.
As far as I can see, this patch did more than cleanup. Could you separate changing logic from cleanup and explain why the logic should be changed?
That will be very useful for reviewers.

Quoted 19 lines

---

arch/x86/kernel/kprobes_32.c +7 -0
index 3a020f7..5a626fd 100644
@@ -240,108 +240,110 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
 	*sara = (unsigned long) &kretprobe_trampoline;
 }
 
+static __always_inline void setup_singlestep(struct kprobe *p,
+					     struct pt_regs *regs,
+					     struct kprobe_ctlblk *kcb)
+{
+#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
+	if (p->ainsn.boostable == 1 && !p->post_handler) {
+		/* Boost up -- we can execute copied instructions directly */

arch/x86/kernel/kprobes_32.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 259

Quoted 10 lines
arch/x86/kernel/kprobes_32.c +10 -0
+		reset_current_kprobe();
+		regs->eip = (unsigned long)p->ainsn.insn;
+		preempt_enable_no_resched();
+	} else {
+		prepare_singlestep(p, regs);
+		kcb->kprobe_status = KPROBE_HIT_SS;
+	}
+#else
+	prepare_singlestep(p, regs);
+	kcb->kprobe_status = KPROBE_HIT_SS;

please avoid code duplication.

Quoted 346 lines
arch/x86/kernel/kprobes_32.c +63 -78
+#endif
+}
+
 /*
  * Interrupts are disabled on entry as trap3 is an interrupt gate and they
  * remain disabled thorough out this function.
  */
 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;
+	}
 
-	/*
-	 * We don't want to be preempted for the entire
-	 * duration of kprobe processing
-	 */
 	preempt_disable();
 	kcb = get_kprobe_ctlblk();
+	cur = kprobe_running();
+	p = get_kprobe(addr);
 
-	/* 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? */
+				}
+				break;
+			default:
+				/* impossible cases */
+				BUG();
 			}
-		}
-		goto no_kprobe;
-	}
+		} else {
+			set_current_kprobe(p, regs, kcb);
+			kcb->kprobe_status = KPROBE_HIT_ACTIVE;
 
-	p = get_kprobe(addr);
-	if (!p) {
-		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.
+			 * 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
+			 * for jprobe processing, so get out doing nothing
+			 * more here.
 			 */
-			regs->eip -= sizeof(kprobe_opcode_t);
+			if (!p->pre_handler || !p->pre_handler(p, regs))
+				setup_singlestep(p, regs, kcb);
 			ret = 1;
 		}
-		/* Not one of ours: let kernel handle it */
-		goto no_kprobe;
-	}
-
-	set_current_kprobe(p, regs, kcb);
-	kcb->kprobe_status = KPROBE_HIT_ACTIVE;
-
-	if (p->pre_handler && p->pre_handler(p, regs))
-		/* handler has already set things up, so skip ss setup */
-		return 1;
+	} else if (cur) {
+		p = __get_cpu_var(current_kprobe);
+		if (p->break_handler && p->break_handler(p, regs)) {
+			setup_singlestep(p, regs, kcb);
+			ret = 1;
+		}
+	} /* else: not a kprobe fault; let the kernel handle it */
 
-ss_probe:
-#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
-	if (p->ainsn.boostable == 1 && !p->post_handler){
-		/* Boost up -- we can execute copied instructions directly */
-		reset_current_kprobe();
-		regs->eip = (unsigned long)p->ainsn.insn;
+	if (!ret)
 		preempt_enable_no_resched();
-		return 1;
-	}
-#endif
-	prepare_singlestep(p, regs);
-	kcb->kprobe_status = KPROBE_HIT_SS;
-	return 1;
-
-no_kprobe:
-	preempt_enable_no_resched();
 	return ret;
 }
 
arch/x86/kernel/kprobes_64.c +69 -75
index 5df19a9..788114c 100644
@@ -278,30 +278,47 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
 	*sara = (unsigned long) &kretprobe_trampoline;
 }
 
-int __kprobes kprobe_handler(struct pt_regs *regs)
+static int __kprobes kprobe_handler(struct pt_regs *regs)
 {
-	struct kprobe *p;
 	int ret = 0;
-	kprobe_opcode_t *addr = (kprobe_opcode_t *)(regs->rip - sizeof(kprobe_opcode_t));
+	kprobe_opcode_t *addr;
+	struct kprobe *p, *cur;
 	struct kprobe_ctlblk *kcb;
 
-	/*
-	 * We don't want to be preempted for the entire
-	 * duration of kprobe processing
-	 */
+	addr = (kprobe_opcode_t *)(regs->rip - 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->rip = (unsigned long)addr;
+		return 1;
+	}
+
 	preempt_disable();
 	kcb = get_kprobe_ctlblk();
+	cur = kprobe_running();
+	p = get_kprobe(addr);
 
-	/* 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_rflags;
-				goto no_kprobe;
-			} else if (kcb->kprobe_status == KPROBE_HIT_SSDONE) {
+	if (p) {
+		if (cur) {
+			switch (kcb->kprobe_status) {
+			case KPROBE_HIT_ACTIVE:
+				/* 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;
+				break;
+			case KPROBE_HIT_SSDONE:
 				/* TODO: Provide re-entrancy from
 				 * post_kprobes_handler() and avoid exception
 				 * stack corruption while single-stepping on
@@ -311,72 +328,49 @@ int __kprobes kprobe_handler(struct pt_regs *regs)
 				regs->rip = (unsigned long)p->addr;
 				reset_current_kprobe();
 				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(kcb);
-				set_current_kprobe(p, regs, kcb);
-				kprobes_inc_nmissed_count(p);
-				prepare_singlestep(p, regs);
-				kcb->kprobe_status = KPROBE_REENTER;
-				return 1;
+				break;
+			case KPROBE_HIT_SS:
+				if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
+					regs->eflags &= ~TF_MASK;
+					regs->eflags |=
+						kcb->kprobe_saved_eflags;
+				} else {
+					/* BUG? */
+				}
+				break;
+			default:
+				/* impossible cases */
+				BUG();
 			}
 		} 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->rip = (unsigned long)addr;
-				ret = 1;
-				goto no_kprobe;
-			}
-			p = __get_cpu_var(current_kprobe);
-			if (p->break_handler && p->break_handler(p, regs)) {
-				goto ss_probe;
-			}
-		}
-		goto no_kprobe;
-	}
+			set_current_kprobe(p, regs, kcb);
+			kcb->kprobe_status = KPROBE_HIT_ACTIVE;
 
-	p = get_kprobe(addr);
-	if (!p) {
-		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.
+			 * 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
+			 * for jprobe processing, so get out doing nothing
+			 * more here.
 			 */
-			regs->rip = (unsigned long)addr;
+			if (!p->pre_handler || !p->pre_handler(p, regs)) {
+				prepare_singlestep(p, regs);
+				kcb->kprobe_status = KPROBE_HIT_SS;
+			}
 			ret = 1;
 		}
-		/* Not one of ours: let kernel handle it */
-		goto no_kprobe;
-	}
-
-	set_current_kprobe(p, regs, kcb);
-	kcb->kprobe_status = KPROBE_HIT_ACTIVE;
-
-	if (p->pre_handler && p->pre_handler(p, regs))
-		/* handler has already set things up, so skip ss setup */
-		return 1;
-
-ss_probe:
-	prepare_singlestep(p, regs);
-	kcb->kprobe_status = KPROBE_HIT_SS;
-	return 1;
+	} else if (cur) {
+		p = __get_cpu_var(current_kprobe);
+		if (p->break_handler && p->break_handler(p, regs)) {
+			prepare_singlestep(p, regs);
+			kcb->kprobe_status = KPROBE_HIT_SS;
+			ret = 1;
+		}
+	} /* else: not a kprobe fault; let the kernel handle it */
 
-no_kprobe:
-	preempt_enable_no_resched();
+	if (!ret)
+		preempt_enable_no_resched();
 	return ret;
 }
 
Signature · 7 lines
Masami Hiramatsu

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

e-mail: [email protected], [email protected]
#17 lore raw
Quoted 2 lines · Masami Hiramatsu

Could you separate changing logic from cleanup and explain why the logic should be changed?

The major portion of logical changes is re-routing of code flow by removing gotos. Hopefully all the cases have been covered. There are a couple of changes that I'd like to address (see below).

Quoted 11 lines
Quoted 11 lines
arch/x86/kernel/kprobes_32.c +11 -0
+static __always_inline void setup_singlestep(struct kprobe *p,
+                                          struct pt_regs *regs,
+                                          struct kprobe_ctlblk *kcb)
+{
+#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
+     if (p->ainsn.boostable == 1 && !p->post_handler) {
+             /* Boost up -- we can execute copied instructions directly */
+             reset_current_kprobe();
+             regs->eip = (unsigned long)p->ainsn.insn;
+             preempt_enable_no_resched();
+     } else {
Quoted 8 lines

arch/x86/kernel/kprobes_32.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 259

Quoted 6 lines
arch/x86/kernel/kprobes_32.c +6 -0
+             prepare_singlestep(p, regs);
+             kcb->kprobe_status = KPROBE_HIT_SS;
+     }
+#else
+     prepare_singlestep(p, regs);
+     kcb->kprobe_status = KPROBE_HIT_SS;

please avoid code duplication.

I've addressed it in the latest patch.

Quoted 8 lines
Quoted 8 lines

static int __kprobes kprobe_handler(struct pt_regs *regs) {

arch/x86/kernel/kprobes_32.c +1 -1
-     struct kprobe *p;
      int ret = 0;
      kprobe_opcode_t *addr;
+     struct kprobe *p, *cur;
      struct kprobe_ctlblk *kcb;
Quoted 14 lines
Quoted 4 lines
      addr = (kprobe_opcode_t *)(regs->eip - sizeof(kprobe_opcode_t));
arch/x86/kernel/kprobes_32.c +3 -0
+     if (*addr != BREAKPOINT_INSTRUCTION) {
+             /*
+              * The breakpoint instruction was removed right

arch/x86/kernel/kprobes_32.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 287

Quoted 10 lines
arch/x86/kernel/kprobes_32.c +10 -0
+              * 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.

Quoted 55 lines
Quoted 55 lines
arch/x86/kernel/kprobes_32.c +19 -34
-     /* 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 {
Quoted 8 lines

arch/x86/kernel/kprobes_32.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 299

Quoted 8 lines
arch/x86/kernel/kprobes_32.c +6 -1
+                                     /* BUG? */
+                             }
+                             break;
+                     default:
+                             /* impossible cases */
+                             BUG();
                      }
-             }

Replaced deeply nested if-elses with a switch.

Please let me know if there are any changes on which you would like further clarification.

Quoted 8 lines

--
Masami Hiramatsu

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

e-mail: [email protected], [email protected]

--

Thanks,
Abhishek Sagar

#18 lore raw

Hi,

Quoted 23 lines · Abhishek Sagar
Quoted 8 lines
Quoted 8 lines

static int __kprobes kprobe_handler(struct pt_regs *regs) {

arch/x86/kernel/kprobes_32.c +1 -1
-     struct kprobe *p;
      int ret = 0;
      kprobe_opcode_t *addr;
+     struct kprobe *p, *cur;
      struct kprobe_ctlblk *kcb;
Quoted 14 lines
Quoted 4 lines
      addr = (kprobe_opcode_t *)(regs->eip - sizeof(kprobe_opcode_t));
arch/x86/kernel/kprobes_32.c +3 -0
+     if (*addr != BREAKPOINT_INSTRUCTION) {
+             /*
+              * The breakpoint instruction was removed right

arch/x86/kernel/kprobes_32.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 287

Quoted 10 lines
arch/x86/kernel/kprobes_32.c +10 -0
+              * 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;
+     }
Quoted 7 lines

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.

Quoted 48 lines
Quoted 48 lines
Quoted 48 lines
arch/x86/kernel/kprobes_32.c +13 -34
-     /* 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;
Quoted 8 lines
Quoted 8 lines

arch/x86/kernel/kprobes_32.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 315

Quoted 8 lines
arch/x86/kernel/kprobes_32.c +8 -0
+                     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.

Quoted 8 lines
Quoted 6 lines

arch/x86/kernel/kprobes_32.c void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, · line 299

Quoted 6 lines
arch/x86/kernel/kprobes_32.c +4 -1
+                             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.

Quoted 11 lines

Please let me know if there are any changes on which you would like further clarification.

Quoted 7 lines

--
Masami Hiramatsu

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

Quoted 5 lines
Quoted 1 line

e-mail: [email protected], [email protected]

--

Thanks,
Abhishek Sagar

Thank you,

Signature · 7 lines
Masami Hiramatsu

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

e-mail: [email protected], [email protected]