Skip to contentSkip to conversation

Why do kprobes and uprobes singlestep?

RFC PATCH In review Andy Lutomirski 8 participants 41 messages Feb 23, 2021 – Mar 23, 2021

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

A while back, I let myself be convinced that kprobes genuinely need to single-step the kernel on occasion, and I decided that this sucked but I could live with it. it would, however, be Really Really Nice (tm) if we could have a rule that anyone running x86 Linux who single-steps the kernel (e.g. kgdb and nothing else) gets to keep all the pieces when the system falls apart around them. Specifically, if we don't allow kernel single-stepping and if we suitably limit kernel instruction breakpoints (the latter isn't actually a major problem), then we don't really really need to use IRET to return to the kernel, and that means we can avoid some massive NMI nastiness.

But I was contemplating the code, and I'm no longer convinced. Uprobes seem to single-step user code for no discernable reason. (They want to trap after executing an out of line instruction, AFAICT. Surely INT3 or even CALL after the out-of-line insn would work as well or better.) Why does kprobe single-step? I spend a while staring at the code, and it was entirely unclear to me what the purpose of the single-step is.

--Andy

#1 lore raw
Quoted 10 lines · On Tue, 23 Feb 2021 15:24:19 -0800 Andy Lutomirski <[email protected]> wrote:

A while back, I let myself be convinced that kprobes genuinely need to single-step the kernel on occasion, and I decided that this sucked but I could live with it. it would, however, be Really Really Nice (tm) if we could have a rule that anyone running x86 Linux who single-steps the kernel (e.g. kgdb and nothing else) gets to keep all the pieces when the system falls apart around them. Specifically, if we don't allow kernel single-stepping and if we suitably limit kernel instruction breakpoints (the latter isn't actually a major problem), then we don't really really need to use IRET to return to the kernel, and that means we can avoid some massive NMI nastiness.

Would you mean using "pop regs + popf + ret" instead of IRET after int3 handled for avoiding IRET releasing the NMI mask? Yeah, it is possible. I don't complain about that.

However, what is the relationship between the IRET and single-stepping? I think we can do same thing in do_debug...

Quoted 7 lines

But I was contemplating the code, and I'm no longer convinced. Uprobes seem to single-step user code for no discernable reason. (They want to trap after executing an out of line instruction, AFAICT. Surely INT3 or even CALL after the out-of-line insn would work as well or better.) Why does kprobe single-step? I spend a while staring at the code, and it was entirely unclear to me what the purpose of the single-step is.

For kprobes, there are 2 major reasons for (still relaying on) single stepping. One is to provide post_handler, another is executing the original code, which is replaced by int3, without modifying code nor emulation. Indeed, most of the instructions actually not depends on the ip register, in that case (and user doesn't set post_handler), kprobe already skips single stepping (a.k.a. kprobe booster, jump back to the kernel code after executing out-of-line instruction.)
However, since some instructions, e.g. jump, call and ret, changes the ip register (and stack), we have to do a fixup afterwards.

But yes, it is possible to emulate, as same as arm/arm64 does. I just concern about side-effects of the emulation, need to be carefully implemented.

Thank you,

Signature · 1 line
Masami Hiramatsu <[email protected]>
#2 lore raw
Quoted 15 lines · Masami Hiramatsu
Quoted 10 lines · On Tue, 23 Feb 2021 15:24:19 -0800 Andy Lutomirski <[email protected]> wrote:

A while back, I let myself be convinced that kprobes genuinely need to single-step the kernel on occasion, and I decided that this sucked but I could live with it. it would, however, be Really Really Nice (tm) if we could have a rule that anyone running x86 Linux who single-steps the kernel (e.g. kgdb and nothing else) gets to keep all the pieces when the system falls apart around them. Specifically, if we don't allow kernel single-stepping and if we suitably limit kernel instruction breakpoints (the latter isn't actually a major problem), then we don't really really need to use IRET to return to the kernel, and that means we can avoid some massive NMI nastiness.

Quoted 3 lines

Would you mean using "pop regs + popf + ret" instead of IRET after int3 handled for avoiding IRET releasing the NMI mask? Yeah, it is possible. I don't complain about that.

Yes, more or less.

Quoted 3 lines

However, what is the relationship between the IRET and single-stepping? I think we can do same thing in do_debug...

Because there is no way to single-step without using IRET. POPF; RET will trap after RET and you won't make forward progress.

Quoted 9 lines
Quoted 8 lines

But I was contemplating the code, and I'm no longer convinced. Uprobes seem to single-step user code for no discernable reason. (They want to trap after executing an out of line instruction, AFAICT. Surely INT3 or even CALL after the out-of-line insn would work as well or better.) Why does kprobe single-step? I spend a while staring at the code, and it was entirely unclear to me what the purpose of the single-step is.

Quoted 3 lines

For kprobes, there are 2 major reasons for (still relaying on) single stepping. One is to provide post_handler, another is executing the original code, which is replaced by int3, without modifying code nor emulation.

I don't follow. Suppose we execute out of line. If we originally have:

INSN

we replace it with:

INT3

and we have, out of line:

INSN [but with displacement modified if it's RIP-relative]

right now, we single-step the out of line copy. But couldn't we instead do:

INSN [but with displacement modified if it's RIP-relative] INT3

or even

INSN [but with displacement modified if it's RIP-relative] JMP kprobe_post_handler

and avoid single-stepping?

I guess I see the point for CALL, JMP and RET, but it seems like we could emulate those cases instead fairly easily.

#3 lore raw
Quoted 20 lines · On Wed, 24 Feb 2021 11:45:10 -0800 Andy Lutomirski <[email protected]> wrote:
Quoted 15 lines · Masami Hiramatsu
Quoted 10 lines · On Tue, 23 Feb 2021 15:24:19 -0800 Andy Lutomirski <[email protected]> wrote:

A while back, I let myself be convinced that kprobes genuinely need to single-step the kernel on occasion, and I decided that this sucked but I could live with it. it would, however, be Really Really Nice (tm) if we could have a rule that anyone running x86 Linux who single-steps the kernel (e.g. kgdb and nothing else) gets to keep all the pieces when the system falls apart around them. Specifically, if we don't allow kernel single-stepping and if we suitably limit kernel instruction breakpoints (the latter isn't actually a major problem), then we don't really really need to use IRET to return to the kernel, and that means we can avoid some massive NMI nastiness.

Quoted 3 lines

Would you mean using "pop regs + popf + ret" instead of IRET after int3 handled for avoiding IRET releasing the NMI mask? Yeah, it is possible. I don't complain about that.

Quoted 8 lines

Yes, more or less.

Quoted 3 lines

However, what is the relationship between the IRET and single-stepping? I think we can do same thing in do_debug...

Because there is no way to single-step without using IRET. POPF; RET will trap after RET and you won't make forward progress.

Ah, indeed. "POPF; RET" is not atomically exceute.

Quoted 20 lines
Quoted 11 lines
Quoted 7 lines

But I was contemplating the code, and I'm no longer convinced. Uprobes seem to single-step user code for no discernable reason. (They want to trap after executing an out of line instruction, AFAICT. Surely INT3 or even CALL after the out-of-line insn would work as well or better.) Why does kprobe single-step? I spend a while staring at the code, and it was entirely unclear to me what the purpose of the single-step is.

For kprobes, there are 2 major reasons for (still relaying on) single stepping. One is to provide post_handler, another is executing the original code, which is replaced by int3, without modifying code nor emulation.

I don't follow. Suppose we execute out of line. If we originally have:

INSN

we replace it with:

INT3

Quoted 8 lines

and we have, out of line:

INSN [but with displacement modified if it's RIP-relative]

right now, we single-step the out of line copy. But couldn't we instead do:

INSN [but with displacement modified if it's RIP-relative] INT3

If the INSN is "jmp +127", it will skip the INT3. So those instructions must be identified and emulated. We did it already in the arm64 (see commit 7ee31a3aa8f4 ("arm64: kprobes: Use BRK instead of single-step when executing
instructions out-of-line")), because arm64 already emulated the branch
instructions. I have to check x86 insns can be emulated without side-effects.

Quoted 5 lines

or even

INSN [but with displacement modified if it's RIP-relative] JMP kprobe_post_handler

This needs a sequence of push-regs etc. ;)

Quoted 5 lines

and avoid single-stepping?

I guess I see the point for CALL, JMP and RET, but it seems like we could emulate those cases instead fairly easily.

OK, let's try to do it. I think it should be possible because even in the current code, resume fixup code (adjust IP register) works only for a few groups of instructions.

Thank you,

Signature · 1 line
Masami Hiramatsu <[email protected]>
#4 lore raw
Quoted 61 lines · Masami Hiramatsu
Quoted 20 lines · On Wed, 24 Feb 2021 11:45:10 -0800 Andy Lutomirski <[email protected]> wrote:
Quoted 15 lines · Masami Hiramatsu
Quoted 10 lines · On Tue, 23 Feb 2021 15:24:19 -0800 Andy Lutomirski <[email protected]> wrote:

A while back, I let myself be convinced that kprobes genuinely need to single-step the kernel on occasion, and I decided that this sucked but I could live with it. it would, however, be Really Really Nice (tm) if we could have a rule that anyone running x86 Linux who single-steps the kernel (e.g. kgdb and nothing else) gets to keep all the pieces when the system falls apart around them. Specifically, if we don't allow kernel single-stepping and if we suitably limit kernel instruction breakpoints (the latter isn't actually a major problem), then we don't really really need to use IRET to return to the kernel, and that means we can avoid some massive NMI nastiness.

Quoted 3 lines

Would you mean using "pop regs + popf + ret" instead of IRET after int3 handled for avoiding IRET releasing the NMI mask? Yeah, it is possible. I don't complain about that.

Quoted 8 lines

Yes, more or less.

Quoted 3 lines

However, what is the relationship between the IRET and single-stepping? I think we can do same thing in do_debug...

Because there is no way to single-step without using IRET. POPF; RET will trap after RET and you won't make forward progress.

Ah, indeed. "POPF; RET" is not atomically exceute.

Quoted 26 lines
Quoted 11 lines
Quoted 7 lines

But I was contemplating the code, and I'm no longer convinced. Uprobes seem to single-step user code for no discernable reason. (They want to trap after executing an out of line instruction, AFAICT. Surely INT3 or even CALL after the out-of-line insn would work as well or better.) Why does kprobe single-step? I spend a while staring at the code, and it was entirely unclear to me what the purpose of the single-step is.

For kprobes, there are 2 major reasons for (still relaying on) single stepping. One is to provide post_handler, another is executing the original code, which is replaced by int3, without modifying code nor emulation.

I don't follow. Suppose we execute out of line. If we originally have:

INSN

we replace it with:

INT3

and we have, out of line:

INSN [but with displacement modified if it's RIP-relative]

right now, we single-step the out of line copy. But couldn't we instead do:

Quoted 8 lines
Quoted 2 lines

INSN [but with displacement modified if it's RIP-relative] INT3

If the INSN is "jmp +127", it will skip the INT3. So those instructions must be identified and emulated. We did it already in the arm64 (see commit 7ee31a3aa8f4 ("arm64: kprobes: Use BRK instead of single-step when executing
instructions out-of-line")), because arm64 already emulated the branch
instructions. I have to check x86 insns can be emulated without side-effects.

Off the top of my head:

JMP changes RIP but has no other side effects. Jcc is the same except that the condition needs checking, which would be a bit tedious.

CALL changes RIP and does a push but has no other side effects. We have special infrastructure to emulate CALL from int3 context:
int3_emulate_call().

RET pops and changes RIP. No other side effects.

RET imm is rare. I don't think it occurs in the kernel at all.

LRET is rare. I don't think kprobe needs to support it.

IRET is rare, and trying to kprobe it seems likely to cause a disaster, although it's within the realm of possibility that the IRET in sync_core() could work.

JMP FAR and CALL FAR are rare. I see no reason to support them.

Quoted 10 lines
Quoted 6 lines

or even

INSN [but with displacement modified if it's RIP-relative] JMP kprobe_post_handler

This needs a sequence of push-regs etc. ;)

Quoted 1 line
Quoted 8 lines
Quoted 4 lines

and avoid single-stepping?

I guess I see the point for CALL, JMP and RET, but it seems like we could emulate those cases instead fairly easily.

OK, let's try to do it. I think it should be possible because even in the current code, resume fixup code (adjust IP register) works only for a few groups of instructions.

I suspect that emulating them would give a nice performance boost, too. Single-stepping is very slow on x86.

I should let you know, though, that I might have found a sneaky alternative solution to handling NMIs, so this is a bit lower priority from my perspective than I thought it was. I'm not quite 100% convinced my idea works, but I'll play with it.

--Andy

Quoted 5 lines

Thank you,

--
Masami Hiramatsu <[email protected]>

#5 lore raw
Quoted 71 lines · On Wed, 24 Feb 2021 22:03:12 -0800 Andy Lutomirski <[email protected]> wrote:
Quoted 61 lines · Masami Hiramatsu
Quoted 20 lines · On Wed, 24 Feb 2021 11:45:10 -0800 Andy Lutomirski <[email protected]> wrote:
Quoted 15 lines · Masami Hiramatsu
Quoted 10 lines · On Tue, 23 Feb 2021 15:24:19 -0800 Andy Lutomirski <[email protected]> wrote:

A while back, I let myself be convinced that kprobes genuinely need to single-step the kernel on occasion, and I decided that this sucked but I could live with it. it would, however, be Really Really Nice (tm) if we could have a rule that anyone running x86 Linux who single-steps the kernel (e.g. kgdb and nothing else) gets to keep all the pieces when the system falls apart around them. Specifically, if we don't allow kernel single-stepping and if we suitably limit kernel instruction breakpoints (the latter isn't actually a major problem), then we don't really really need to use IRET to return to the kernel, and that means we can avoid some massive NMI nastiness.

Quoted 3 lines

Would you mean using "pop regs + popf + ret" instead of IRET after int3 handled for avoiding IRET releasing the NMI mask? Yeah, it is possible. I don't complain about that.

Quoted 8 lines

Yes, more or less.

Quoted 3 lines

However, what is the relationship between the IRET and single-stepping? I think we can do same thing in do_debug...

Because there is no way to single-step without using IRET. POPF; RET will trap after RET and you won't make forward progress.

Ah, indeed. "POPF; RET" is not atomically exceute.

Quoted 26 lines
Quoted 11 lines
Quoted 7 lines

But I was contemplating the code, and I'm no longer convinced. Uprobes seem to single-step user code for no discernable reason. (They want to trap after executing an out of line instruction, AFAICT. Surely INT3 or even CALL after the out-of-line insn would work as well or better.) Why does kprobe single-step? I spend a while staring at the code, and it was entirely unclear to me what the purpose of the single-step is.

For kprobes, there are 2 major reasons for (still relaying on) single stepping. One is to provide post_handler, another is executing the original code, which is replaced by int3, without modifying code nor emulation.

I don't follow. Suppose we execute out of line. If we originally have:

INSN

we replace it with:

INT3

and we have, out of line:

INSN [but with displacement modified if it's RIP-relative]

right now, we single-step the out of line copy. But couldn't we instead do:

Quoted 8 lines
Quoted 2 lines

INSN [but with displacement modified if it's RIP-relative] INT3

If the INSN is "jmp +127", it will skip the INT3. So those instructions must be identified and emulated. We did it already in the arm64 (see commit 7ee31a3aa8f4 ("arm64: kprobes: Use BRK instead of single-step when executing
instructions out-of-line")), because arm64 already emulated the branch
instructions. I have to check x86 insns can be emulated without side-effects.

Quoted 8 lines

Off the top of my head:

JMP changes RIP but has no other side effects. Jcc is the same except that the condition needs checking, which would be a bit tedious.

CALL changes RIP and does a push but has no other side effects. We have special infrastructure to emulate CALL from int3 context:
int3_emulate_call().

Yeah, I remember that a gap was introduced for int3_emulate_call(). These helps me to implement emulation.

Quoted 8 lines

RET pops and changes RIP. No other side effects.

RET imm is rare. I don't think it occurs in the kernel at all.

LRET is rare. I don't think kprobe needs to support it.

JMP FAR and CALL FAR are rare. I see no reason to support them.

I see those are rare, but supporting those is not hard.

Quoted 4 lines

IRET is rare, and trying to kprobe it seems likely to cause a disaster, although it's within the realm of possibility that the IRET in sync_core() could work.

Agreed. Iret should not be probed.

Quoted 13 lines
Quoted 13 lines
Quoted 4 lines

or even

INSN [but with displacement modified if it's RIP-relative] JMP kprobe_post_handler

This needs a sequence of push-regs etc. ;)

Quoted 5 lines

and avoid single-stepping?

I guess I see the point for CALL, JMP and RET, but it seems like we could emulate those cases instead fairly easily.

Quoted 6 lines
Quoted 3 lines

OK, let's try to do it. I think it should be possible because even in the current code, resume fixup code (adjust IP register) works only for a few groups of instructions.

I suspect that emulating them would give a nice performance boost, too. Single-stepping is very slow on x86.

Yeah, that's same on arm64. Jean reported eliminating single-step gained the performance.

Quoted 5 lines

I should let you know, though, that I might have found a sneaky alternative solution to handling NMIs, so this is a bit lower priority from my perspective than I thought it was. I'm not quite 100% convinced my idea works, but I'll play with it.

Does that involve kprobes? Anyway, I'll try to remove single-step by emulation and int3.

Thank you,

Quoted 8 lines

--Andy

Quoted 5 lines

Thank you,

--
Masami Hiramatsu <[email protected]>

Signature · 1 line
Masami Hiramatsu <[email protected]>
#6 lore raw

cover x86/kprobes: Remoev single-step trap from x86 kprobes

In reply to Masami Hiramatsu

Hi Andy,

Here is the patch to remove the single-step debug trap from the x86 kprobe. This uses int3 as you suggested instead of the debug trap, for removing the iret which returns to kernel.
Some instructions must be emulated and some instructions becomes not able to be probed, but as far as I can see those are not rare case.

TODOs/discussions
- extend kprobe debugfs interface to show the arch-dependent flags, like [BOOST], [EMULATE].
- Consider this emulation code split into a file and share it with uprobe (uprobe already has its own emulator) and alternatives (it has int3_emulate_*) but it can be overengineering.
- Add testcases for emulator. Current kprobe smoke test is not arch specific. Maybe better to probe an assembly target code so that it can test "boosted", "emulated" or "int3-single-stepped" cases.

Thank you,

---

Masami Hiramatsu (1):
x86/kprobes: Use int3 instead of debug trap for single-step

 arch/x86/include/asm/kprobes.h |   21 +-
 arch/x86/kernel/kprobes/core.c |  515 ++++++++++++++++++++++++++--------------
 arch/x86/kernel/traps.c        |    3 
 3 files changed, 351 insertions(+), 188 deletions(-)

--
Masami Hiramatsu (Linaro) <[email protected]>

#7 lore raw

RFC PATCH 1/1 x86/kprobes: Use int3 instead of debug trap for single-step

Use int3 instead of debug trap exception for single-stepping the probed instructions. Some instructions which change the ip registers or modify IF flags are emulated because those are not able to be single-stepped by int3 or may allow the interrupt while single-stepping.

This actually changes the kprobes behavior.

- kprobes can not probe following instructions; int3, iret, far jmp/call which get absolute address as immediate, indirect far jmp/call, indirect near jmp/call with addressing by memory (register-based indirect jmp/call are OK), and vmcall/vmlaunch/vmresume/vmxoff.

- If the kprobe post_handler doesn't set before registering, it may not be called in some case even if you set it afterwards. (IOW, kprobe booster is enabled at registration, user can not change it)

But both are rare issue, unsupported instructions will not be used in the kernel (or rarely used), and post_handlers are rarely used (I don't see it except for the test code).

---
 arch/x86/include/asm/kprobes.h |   21 +-
 arch/x86/kernel/kprobes/core.c |  515 ++++++++++++++++++++++++++--------------
 arch/x86/kernel/traps.c        |    3 
 3 files changed, 351 insertions(+), 188 deletions(-)
arch/x86/include/asm/kprobes.h +16 -5
index d20a3d6be36e..bd7f5886a789 100644
@@ -65,10 +65,22 @@ struct arch_specific_insn {
 	 * a post_handler).
 	 */
 	unsigned boostable:1;
-	unsigned if_modifier:1;
-	unsigned is_call:1;
-	unsigned is_pushf:1;
-	unsigned is_abs_ip:1;
+	unsigned char size;	/* The size of insn */
+	union {
+		unsigned char opcode;
+		struct {
+			unsigned char type;
+		} jcc;
+		struct {
+			unsigned char type;
+			unsigned char asize;
+		} loop;
+		struct {
+			unsigned char reg;
+		} indirect;
+	};
+	s32 rel32;	/* relative offset must be s32, s16, or s8 */
+	void (*emulate_op)(struct kprobe *p, struct pt_regs *regs);
 	/* Number of bytes of text poked */
 	int tp_len;
 };
@@ -107,7 +119,6 @@ extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
 extern int kprobe_exceptions_notify(struct notifier_block *self,
 				    unsigned long val, void *data);
 extern int kprobe_int3_handler(struct pt_regs *regs);
-extern int kprobe_debug_handler(struct pt_regs *regs);
 
 #else
 
arch/x86/kernel/kprobes/core.c +335 -180

633 lines not on this page · raw message

arch/x86/kernel/traps.c +0 -3
index 7f5aec758f0e..1235c85c8b3a 100644
@@ -890,9 +890,6 @@ static __always_inline void exc_debug_kernel(struct pt_regs *regs,
 	if ((dr6 & DR_STEP) && is_sysenter_singlestep(regs))
 		dr6 &= ~DR_STEP;
 
-	if (kprobe_debug_handler(regs))
-		goto out;
-
 	/*
 	 * The kernel doesn't use INT1
 	 */
#8 lore raw

Re: [RFC PATCH 1/1] x86/kprobes: Use int3 instead of debug trap for single-step

arch/x86/kernel/kprobes/core.c void free_insn_page(void *page) · line 467

Quoted 8 lines · Masami Hiramatsu
arch/x86/kernel/kprobes/core.c +8 -0
+static void kprobe_emulate_jmp(struct kprobe *p, struct pt_regs *regs)
+{
+	unsigned long ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;
+
+	ip += p->ainsn.rel32;
+	int3_emulate_jmp(regs, ip);
+}
+NOKPROBE_SYMBOL(kprobe_emulate_jmp);

Would it make sense to have this be something like:

static void kprobe_emulate_jmp(struct kprobe *p, struct pt_regs *regs, bool cond)

{
	unsigned long ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;

	if (cond)
		ip += p->ainsn.rel32;
	int3_emulate_jmp(regs, ip);
}
Quoted 7 lines
arch/x86/kernel/kprobes/core.c +7 -0
+static void kprobe_emulate_jcc(struct kprobe *p, struct pt_regs *regs)
+{
+	bool invert = p->ainsn.jcc.type & 1;
+	bool match;
+
+	if (p->ainsn.jcc.type < 0xc) {
+		match = regs->flags & jcc_mask[p->ainsn.jcc.type >> 1];

arch/x86/kernel/kprobes/core.c void free_insn_page(void *page) · line 494

Quoted 10 lines
arch/x86/kernel/kprobes/core.c +10 -0
+	} else {
+		match = ((regs->flags & X86_EFLAGS_SF) >> X86_EFLAGS_SF_BIT) ^
+			((regs->flags & X86_EFLAGS_OF) >> X86_EFLAGS_OF_BIT);
+		if (p->ainsn.jcc.type >= 0xe)
+			match = match && (regs->flags & X86_EFLAGS_ZF);
+	}
+	if ((match && !invert) || (!match && invert))
+		kprobe_emulate_jmp(p, regs);
+	else
+		regs->ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;

Then you can do:

	kprobe_emulate_jmp(p, regs, match);
Quoted 26 lines
arch/x86/kernel/kprobes/core.c +26 -0
+}
+NOKPROBE_SYMBOL(kprobe_emulate_jcc);
+
+static void kprobe_emulate_loop(struct kprobe *p, struct pt_regs *regs)
+{
+	bool match;
+
+	if (p->ainsn.loop.type != 3) {	/* LOOP* */
+		if (p->ainsn.loop.asize == 32)
+			match = ((*(u32 *)&regs->cx)--) != 0;
+#ifdef CONFIG_X86_64
+		else if (p->ainsn.loop.asize == 64)
+			match = ((*(u64 *)&regs->cx)--) != 0;
+#endif
+		else
+			match = ((*(u16 *)&regs->cx)--) != 0;
+	} else {			/* JCXZ */
+		if (p->ainsn.loop.asize == 32)
+			match = *(u32 *)(&regs->cx) == 0;
+#ifdef CONFIG_X86_64
+		else if (p->ainsn.loop.asize == 64)
+			match = *(u64 *)(&regs->cx) == 0;
+#endif
+		else
+			match = *(u16 *)(&regs->cx) == 0;
+	}

arch/x86/kernel/kprobes/core.c void free_insn_page(void *page) · line 530

Quoted 10 lines
arch/x86/kernel/kprobes/core.c +10 -0
+
+	if (p->ainsn.loop.type == 0)	/* LOOPNE */
+		match = match && !(regs->flags & X86_EFLAGS_ZF);
+	else if (p->ainsn.loop.type == 1)	/* LOOPE */
+		match = match && (regs->flags & X86_EFLAGS_ZF);
+
+	if (match)
+		kprobe_emulate_jmp(p, regs);
+	else
+		regs->ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;

and here.

Quoted 2 lines
(quoted diff) +2 -0
+}
+NOKPROBE_SYMBOL(kprobe_emulate_loop);
#9 lore raw

Re: [RFC PATCH 1/1] x86/kprobes: Use int3 instead of debug trap for single-step

In reply to Masami Hiramatsu

arch/x86/kernel/kprobes/core.c static void set_resume_flags(struct kprobe *p, struct insn *insn) · line 679

Quoted 8 lines · Masami Hiramatsu
arch/x86/kernel/kprobes/core.c +7 -0
+	default:
+		if ((opcode & 0xf0) == 0x70) {
+			/* 1 byte conditional jump */
+			p->ainsn.emulate_op = kprobe_emulate_jcc;
+			p->ainsn.jcc.type = opcode & 0xf;
+			p->ainsn.rel32 = *(char *)insn->immediate.bytes;
+		}
 	}

Would it make sense to write that as:

	case 0x70 ... 0x7f:
		/* 1 byte conditional jump */
		p->ainsn.emulate_op = kprobe_emulate_jcc;
		p->ainsn.jcc.type = opcode & 0xf;
		p->ainsn.rel32 = *(char *)insn->immediate.bytes;
		break;

instead? Preferably right before the 0x0f case :-)

Acked-by on v1 1/1
#10 lore raw

Re: [RFC PATCH 1/1] x86/kprobes: Use int3 instead of debug trap for single-step

In reply to Masami Hiramatsu
Quoted 19 lines · Masami Hiramatsu

Use int3 instead of debug trap exception for single-stepping the probed instructions. Some instructions which change the ip registers or modify IF flags are emulated because those are not able to be single-stepped by int3 or may allow the interrupt while single-stepping.

This actually changes the kprobes behavior.

- kprobes can not probe following instructions; int3, iret, far jmp/call which get absolute address as immediate, indirect far jmp/call, indirect near jmp/call with addressing by memory (register-based indirect jmp/call are OK), and vmcall/vmlaunch/vmresume/vmxoff.

- If the kprobe post_handler doesn't set before registering, it may not be called in some case even if you set it afterwards. (IOW, kprobe booster is enabled at registration, user can not change it)

Quoted 6 lines

But both are rare issue, unsupported instructions will not be used in the kernel (or rarely used), and post_handlers are rarely used (I don't see it except for the test code).

Very nice!

Aside of a few nits:

#11 lore raw

Re: [RFC PATCH 1/1] x86/kprobes: Use int3 instead of debug trap for single-step

Quoted 1 line · Peter Zijlstra

Aside of a few nits:

something like so..

arch/x86/kernel/kprobes/core.c +18 -16
@@ -457,13 +457,20 @@ static void kprobe_emulate_call(struct k
 }
 NOKPROBE_SYMBOL(kprobe_emulate_call);
 
-static void kprobe_emulate_jmp(struct kprobe *p, struct pt_regs *regs)
+static __always_inline 
+void __kprobe_emulate_jmp(struct kprobe *p, struct pt_regs *regs, bool cond)
 {
 	unsigned long ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;
 
-	ip += p->ainsn.rel32;
+	if (cond)
+		ip += p->ainsn.rel32;
 	int3_emulate_jmp(regs, ip);
 }
+
+static void kprobe_emulate_jmp(struct kprobe *p, struct pt_regs *regs)
+{
+	__kprobe_emulate_jmp(p, regs, true);
+}
 NOKPROBE_SYMBOL(kprobe_emulate_jmp);
 
 static const unsigned long jcc_mask[6] = {
@@ -488,10 +495,7 @@ static void kprobe_emulate_jcc(struct kp
 		if (p->ainsn.jcc.type >= 0xe)
 			match = match && (regs->flags & X86_EFLAGS_ZF);
 	}
-	if ((match && !invert) || (!match && invert))
-		kprobe_emulate_jmp(p, regs);
-	else
-		regs->ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;
+	__kprobe_emulate_jmp(p, regs, (match && !invert) || (!match && invert));
 }
 NOKPROBE_SYMBOL(kprobe_emulate_jcc);
 
@@ -524,10 +528,7 @@ static void kprobe_emulate_loop(struct k
 	else if (p->ainsn.loop.type == 1)	/* LOOPE */
 		match = match && (regs->flags & X86_EFLAGS_ZF);
 
-	if (match)
-		kprobe_emulate_jmp(p, regs);
-	else
-		regs->ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;
+	__kprobe_emulate_jmp(p, regs, match);
 }
 NOKPROBE_SYMBOL(kprobe_emulate_loop);
 
@@ -613,6 +614,12 @@ static int prepare_emulation(struct kpro
 		else
 			p->ainsn.rel32 = *(s32 *)&insn->immediate.value;
 		break;
+	case 0x70 ... 0x7f:
+		/* 1 byte conditional jump */
+		p->ainsn.emulate_op = kprobe_emulate_jcc;
+		p->ainsn.jcc.type = opcode & 0xf;
+		p->ainsn.rel32 = *(char *)insn->immediate.bytes;
+		break;
 	case 0x0f:
 		opcode = insn->opcode.bytes[1];
 		if ((opcode & 0xf0) == 0x80) {
@@ -667,12 +674,7 @@ static int prepare_emulation(struct kpro
 #endif
 		break;
 	default:
-		if ((opcode & 0xf0) == 0x70) {
-			/* 1 byte conditional jump */
-			p->ainsn.emulate_op = kprobe_emulate_jcc;
-			p->ainsn.jcc.type = opcode & 0xf;
-			p->ainsn.rel32 = *(char *)insn->immediate.bytes;
-		}
+		break;
 	}
 	p->ainsn.size = insn->length;
 
#12 lore raw

Re: [RFC PATCH 1/1] x86/kprobes: Use int3 instead of debug trap for single-step

Quoted 5 lines · On Tue, 2 Mar 2021 09:54:43 +0100 Peter Zijlstra <[email protected]> wrote:
Quoted 1 line · Peter Zijlstra

Aside of a few nits:

something like so..

Thanks Peter,
I completely agree with that change!
Let me update the patch.

Quoted 77 lines
arch/x86/kernel/kprobes/core.c +18 -16
@@ -457,13 +457,20 @@ static void kprobe_emulate_call(struct k
 }
 NOKPROBE_SYMBOL(kprobe_emulate_call);
 
-static void kprobe_emulate_jmp(struct kprobe *p, struct pt_regs *regs)
+static __always_inline 
+void __kprobe_emulate_jmp(struct kprobe *p, struct pt_regs *regs, bool cond)
 {
 	unsigned long ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;
 
-	ip += p->ainsn.rel32;
+	if (cond)
+		ip += p->ainsn.rel32;
 	int3_emulate_jmp(regs, ip);
 }
+
+static void kprobe_emulate_jmp(struct kprobe *p, struct pt_regs *regs)
+{
+	__kprobe_emulate_jmp(p, regs, true);
+}
 NOKPROBE_SYMBOL(kprobe_emulate_jmp);
 
 static const unsigned long jcc_mask[6] = {
@@ -488,10 +495,7 @@ static void kprobe_emulate_jcc(struct kp
 		if (p->ainsn.jcc.type >= 0xe)
 			match = match && (regs->flags & X86_EFLAGS_ZF);
 	}
-	if ((match && !invert) || (!match && invert))
-		kprobe_emulate_jmp(p, regs);
-	else
-		regs->ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;
+	__kprobe_emulate_jmp(p, regs, (match && !invert) || (!match && invert));
 }
 NOKPROBE_SYMBOL(kprobe_emulate_jcc);
 
@@ -524,10 +528,7 @@ static void kprobe_emulate_loop(struct k
 	else if (p->ainsn.loop.type == 1)	/* LOOPE */
 		match = match && (regs->flags & X86_EFLAGS_ZF);
 
-	if (match)
-		kprobe_emulate_jmp(p, regs);
-	else
-		regs->ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;
+	__kprobe_emulate_jmp(p, regs, match);
 }
 NOKPROBE_SYMBOL(kprobe_emulate_loop);
 
@@ -613,6 +614,12 @@ static int prepare_emulation(struct kpro
 		else
 			p->ainsn.rel32 = *(s32 *)&insn->immediate.value;
 		break;
+	case 0x70 ... 0x7f:
+		/* 1 byte conditional jump */
+		p->ainsn.emulate_op = kprobe_emulate_jcc;
+		p->ainsn.jcc.type = opcode & 0xf;
+		p->ainsn.rel32 = *(char *)insn->immediate.bytes;
+		break;
 	case 0x0f:
 		opcode = insn->opcode.bytes[1];
 		if ((opcode & 0xf0) == 0x80) {
@@ -667,12 +674,7 @@ static int prepare_emulation(struct kpro
 #endif
 		break;
 	default:
-		if ((opcode & 0xf0) == 0x70) {
-			/* 1 byte conditional jump */
-			p->ainsn.emulate_op = kprobe_emulate_jcc;
-			p->ainsn.jcc.type = opcode & 0xf;
-			p->ainsn.rel32 = *(char *)insn->immediate.bytes;
-		}
+		break;
 	}
 	p->ainsn.size = insn->length;
 
Signature · 1 line
Masami Hiramatsu <[email protected]>
#13 lore raw

Re: [RFC PATCH 1/1] x86/kprobes: Use int3 instead of debug trap for single-step

In reply to Peter Zijlstra
Quoted 4 lines · Peter Zijlstra
(quoted diff) +4 -0
+static void kprobe_emulate_jcc(struct kprobe *p, struct pt_regs *regs)
+{
+	bool invert = p->ainsn.jcc.type & 1;
+	bool match;

Review

Quoted 10 lines
(quoted diff) +10 -0
+
+	if (p->ainsn.jcc.type < 0xc) {
+		match = regs->flags & jcc_mask[p->ainsn.jcc.type >> 1];
+	} else {
+		match = ((regs->flags & X86_EFLAGS_SF) >> X86_EFLAGS_SF_BIT) ^
+			((regs->flags & X86_EFLAGS_OF) >> X86_EFLAGS_OF_BIT);
+		if (p->ainsn.jcc.type >= 0xe)
+			match = match && (regs->flags & X86_EFLAGS_ZF);
+	}
+	__kprobe_emulate_jmp(p, regs, (match && !invert) || (!match && invert));

Also, isn't that: 'match ^ invert' ?

Quoted 1 line

+}

#14 lore raw

cover x86/kprobes: Remoev single-step trap from x86 kprobes

In reply to Masami Hiramatsu

Hi,

Here is a series of patches to remove the single-step debug trap from the x86 kprobe.

The first 2 patches ([1/3][2/3]) are bugfixes which I've sent recently;

https://lore.kernel.org/lkml/161425451732.93763.18329509061375062554.stgit@devnote2/

And [3/3] is actually the patch to remove single-step from kprobes. The RFC version is here;

https://lore.kernel.org/lkml/161460768474.430263.18425867006584111900.stgit@devnote2/

This uses int3 as Andy suggested instead of the debug trap, for removing the IRET which returns to kernel.
Some instructions must be emulated and some instructions becomes not able to be probed, but as far as I can see those are not rare case.

Thank you,

---

Masami Hiramatsu (3):
x86/kprobes: Retrieve correct opcode for group instruction x86/kprobes: Identify far indirect JMP correctly x86/kprobes: Use int3 instead of debug trap for single-step

 arch/x86/include/asm/kprobes.h |   21 +-
 arch/x86/kernel/kprobes/core.c |  524 ++++++++++++++++++++++++++--------------
 arch/x86/kernel/traps.c        |    3 
 3 files changed, 358 insertions(+), 190 deletions(-)

--
Masami Hiramatsu (Linaro) <[email protected]>

#15 lore raw

PATCH 1/3 x86/kprobes: Retrieve correct opcode for group instruction

Since the opcodes start from 0xff are group5 instruction group which is not 2 bytes opcode but the extended opcode determined by the MOD/RM byte.

The commit abd82e533d88 ("x86/kprobes: Do not decode opcode in resume_execution()") used insn->opcode.bytes[1], but that is not correct. We have to refer the insn->modrm.bytes[1] instead.

Fixes abd82e533d88 ("x86/kprobes: Do not decode opcode in resume_execution()")
---
 arch/x86/kernel/kprobes/core.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
arch/x86/kernel/kprobes/core.c +5 -1
index df776cdca327..08674e7a5d7b 100644
@@ -448,7 +448,11 @@ static void set_resume_flags(struct kprobe *p, struct insn *insn)
 		break;
 #endif
 	case 0xff:
-		opcode = insn->opcode.bytes[1];
+		/*
+		 * Since the 0xff is an extended group opcode, the instruction
+		 * is determined by the MOD/RM byte.
+		 */
+		opcode = insn->modrm.bytes[0];
 		if ((opcode & 0x30) == 0x10) {
 			/*
 			 * call absolute, indirect
#16 lore raw

[tip: x86/core] x86/kprobes: Retrieve correct opcode for group instruction

The following commit has been merged into the x86/core branch of tip:

The following commit has been merged into the x86/core branch of tip:

Commit-ID:     d60ad3d46f1d04a282c56159f1deb675c12733fd
Gitweb:        https://git.kernel.org/tip/d60ad3d46f1d04a282c56159f1deb675c12733fd
Author:        Masami Hiramatsu <[email protected]>
AuthorDate:    Wed, 03 Mar 2021 00:25:24 +09:00
Committer:     Peter Zijlstra <[email protected]>
CommitterDate: Tue, 23 Mar 2021 16:07:55 +01:00

x86/kprobes: Retrieve correct opcode for group instruction

Since the opcodes start from 0xff are group5 instruction group which is not 2 bytes opcode but the extended opcode determined by the MOD/RM byte.

The commit abd82e533d88 ("x86/kprobes: Do not decode opcode in resume_execution()") used insn->opcode.bytes[1], but that is not correct. We have to refer the insn->modrm.bytes[1] instead.

---
 arch/x86/kernel/kprobes/core.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
arch/x86/kernel/kprobes/core.c +5 -1
index 60a540f..9b31790 100644
@@ -453,7 +453,11 @@ static void set_resume_flags(struct kprobe *p, struct insn *insn)
 		break;
 #endif
 	case 0xff:
-		opcode = insn->opcode.bytes[1];
+		/*
+		 * Since the 0xff is an extended group opcode, the instruction
+		 * is determined by the MOD/RM byte.
+		 */
+		opcode = insn->modrm.bytes[0];
 		if ((opcode & 0x30) == 0x10) {
 			/*
 			 * call absolute, indirect
#17 lore raw

PATCH 2/3 x86/kprobes: Identify far indirect JMP correctly

In reply to Masami Hiramatsu

Since Grp5 far indirect JMP is FF "mod 101 r/m", it should be (modrm & 0x38) == 0x28, and near indirect JMP is also 0x38 == 0x20. So we can mask modrm with 0x30 and check 0x20.
This is actually what the original code does, it also doesn't care the last bit. So the result code is same.

Thus, I think this is just a cosmetic cleanup.

---
 arch/x86/kernel/kprobes/core.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
arch/x86/kernel/kprobes/core.c +1 -2
index 08674e7a5d7b..be76568d57a5 100644
@@ -462,8 +462,7 @@ static void set_resume_flags(struct kprobe *p, struct insn *insn)
 			p->ainsn.is_call = 1;
 			p->ainsn.is_abs_ip = 1;
 			break;
-		} else if (((opcode & 0x31) == 0x20) ||
-			   ((opcode & 0x31) == 0x21)) {
+		} else if ((opcode & 0x30) == 0x20) {
 			/*
 			 * jmp near and far, absolute indirect
 			 * ip is correct.
#18 lore raw

[tip: x86/core] x86/kprobes: Identify far indirect JMP correctly

The following commit has been merged into the x86/core branch of tip:

The following commit has been merged into the x86/core branch of tip:

Commit-ID:     a194acd316f93f3435a64de3b37dca2b5a77b338
Gitweb:        https://git.kernel.org/tip/a194acd316f93f3435a64de3b37dca2b5a77b338
Author:        Masami Hiramatsu <[email protected]>
AuthorDate:    Wed, 03 Mar 2021 00:25:34 +09:00
Committer:     Peter Zijlstra <[email protected]>
CommitterDate: Tue, 23 Mar 2021 16:07:56 +01:00

x86/kprobes: Identify far indirect JMP correctly

Since Grp5 far indirect JMP is FF "mod 101 r/m", it should be (modrm & 0x38) == 0x28, and near indirect JMP is also 0x38 == 0x20. So we can mask modrm with 0x30 and check 0x20.
This is actually what the original code does, it also doesn't care the last bit. So the result code is same.

Thus, I think this is just a cosmetic cleanup.

---
 arch/x86/kernel/kprobes/core.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
arch/x86/kernel/kprobes/core.c +1 -2
index 9b31790..f6ec57f 100644
@@ -467,8 +467,7 @@ static void set_resume_flags(struct kprobe *p, struct insn *insn)
 			p->ainsn.is_call = 1;
 			p->ainsn.is_abs_ip = 1;
 			break;
-		} else if (((opcode & 0x31) == 0x20) ||
-			   ((opcode & 0x31) == 0x21)) {
+		} else if ((opcode & 0x30) == 0x20) {
 			/*
 			 * jmp near and far, absolute indirect
 			 * ip is correct.
#19 lore raw

PATCH 3/3 x86/kprobes: Use int3 instead of debug trap for single-step

In reply to Masami Hiramatsu

Use int3 instead of debug trap exception for single-stepping the probed instructions. Some instructions which change the ip registers or modify IF flags are emulated because those are not able to be single-stepped by int3 or may allow the interrupt while single-stepping.

This actually changes the kprobes behavior.

- kprobes can not probe following instructions; int3, iret, far jmp/call which get absolute address as immediate, indirect far jmp/call, indirect near jmp/call with addressing by memory (register-based indirect jmp/call are OK), and vmcall/vmlaunch/vmresume/vmxoff.

- If the kprobe post_handler doesn't set before registering, it may not be called in some case even if you set it afterwards. (IOW, kprobe booster is enabled at registration, user can not change it)

But both are rare issue, unsupported instructions will not be used in the kernel (or rarely used), and post_handlers are rarely used (I don't see it except for the test code).

---
Changes in v1:
- According to Peter's suggestion, merge the conditional jump emulation code and use range style of switch-case for 1 byte conditional jump

---
 arch/x86/include/asm/kprobes.h |   21 +-
 arch/x86/kernel/kprobes/core.c |  517 ++++++++++++++++++++++++++--------------
 arch/x86/kernel/traps.c        |    3 
 3 files changed, 353 insertions(+), 188 deletions(-)
arch/x86/include/asm/kprobes.h +16 -5
index d20a3d6be36e..bd7f5886a789 100644
@@ -65,10 +65,22 @@ struct arch_specific_insn {
 	 * a post_handler).
 	 */
 	unsigned boostable:1;
-	unsigned if_modifier:1;
-	unsigned is_call:1;
-	unsigned is_pushf:1;
-	unsigned is_abs_ip:1;
+	unsigned char size;	/* The size of insn */
+	union {
+		unsigned char opcode;
+		struct {
+			unsigned char type;
+		} jcc;
+		struct {
+			unsigned char type;
+			unsigned char asize;
+		} loop;
+		struct {
+			unsigned char reg;
+		} indirect;
+	};
+	s32 rel32;	/* relative offset must be s32, s16, or s8 */
+	void (*emulate_op)(struct kprobe *p, struct pt_regs *regs);
 	/* Number of bytes of text poked */
 	int tp_len;
 };
@@ -107,7 +119,6 @@ extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
 extern int kprobe_exceptions_notify(struct notifier_block *self,
 				    unsigned long val, void *data);
 extern int kprobe_int3_handler(struct pt_regs *regs);
-extern int kprobe_debug_handler(struct pt_regs *regs);
 
 #else
 
arch/x86/kernel/kprobes/core.c +337 -180

635 lines not on this page · raw message

arch/x86/kernel/traps.c +0 -3
index 7f5aec758f0e..1235c85c8b3a 100644
@@ -890,9 +890,6 @@ static __always_inline void exc_debug_kernel(struct pt_regs *regs,
 	if ((dr6 & DR_STEP) && is_sysenter_singlestep(regs))
 		dr6 &= ~DR_STEP;
 
-	if (kprobe_debug_handler(regs))
-		goto out;
-
 	/*
 	 * The kernel doesn't use INT1
 	 */
#20 lore raw

[tip: x86/core] x86/kprobes: Use int3 instead of debug trap for single-step

The following commit has been merged into the x86/core branch of tip:

The following commit has been merged into the x86/core branch of tip:

Commit-ID:     6256e668b7af9d81472e03c6a171630c08f8858a
Gitweb:        https://git.kernel.org/tip/6256e668b7af9d81472e03c6a171630c08f8858a
Author:        Masami Hiramatsu <[email protected]>
AuthorDate:    Wed, 03 Mar 2021 00:25:46 +09:00
Committer:     Peter Zijlstra <[email protected]>
CommitterDate: Tue, 23 Mar 2021 16:07:56 +01:00

x86/kprobes: Use int3 instead of debug trap for single-step

Use int3 instead of debug trap exception for single-stepping the probed instructions. Some instructions which change the ip registers or modify IF flags are emulated because those are not able to be single-stepped by int3 or may allow the interrupt while single-stepping.

This actually changes the kprobes behavior.

- kprobes can not probe following instructions; int3, iret, far jmp/call which get absolute address as immediate, indirect far jmp/call, indirect near jmp/call with addressing by memory (register-based indirect jmp/call are OK), and vmcall/vmlaunch/vmresume/vmxoff.

- If the kprobe post_handler doesn't set before registering, it may not be called in some case even if you set it afterwards. (IOW, kprobe booster is enabled at registration, user can not change it)

But both are rare issue, unsupported instructions will not be used in the kernel (or rarely used), and post_handlers are rarely used (I don't see it except for the test code).

---
 arch/x86/include/asm/kprobes.h |  21 +-
 arch/x86/kernel/kprobes/core.c | 517 ++++++++++++++++++++------------
 arch/x86/kernel/traps.c        |   3 +-
 3 files changed, 353 insertions(+), 188 deletions(-)
arch/x86/include/asm/kprobes.h +16 -5
index d20a3d6..bd7f588 100644
@@ -65,10 +65,22 @@ struct arch_specific_insn {
 	 * a post_handler).
 	 */
 	unsigned boostable:1;
-	unsigned if_modifier:1;
-	unsigned is_call:1;
-	unsigned is_pushf:1;
-	unsigned is_abs_ip:1;
+	unsigned char size;	/* The size of insn */
+	union {
+		unsigned char opcode;
+		struct {
+			unsigned char type;
+		} jcc;
+		struct {
+			unsigned char type;
+			unsigned char asize;
+		} loop;
+		struct {
+			unsigned char reg;
+		} indirect;
+	};
+	s32 rel32;	/* relative offset must be s32, s16, or s8 */
+	void (*emulate_op)(struct kprobe *p, struct pt_regs *regs);
 	/* Number of bytes of text poked */
 	int tp_len;
 };
@@ -107,7 +119,6 @@ extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
 extern int kprobe_exceptions_notify(struct notifier_block *self,
 				    unsigned long val, void *data);
 extern int kprobe_int3_handler(struct pt_regs *regs);
-extern int kprobe_debug_handler(struct pt_regs *regs);
 
 #else
 
arch/x86/kernel/kprobes/core.c +337 -180

636 lines not on this page · raw message

arch/x86/kernel/traps.c +0 -3
index 0da8d2a..a5d2540 100644
@@ -890,9 +890,6 @@ static __always_inline void exc_debug_kernel(struct pt_regs *regs,
 	if ((dr6 & DR_STEP) && is_sysenter_singlestep(regs))
 		dr6 &= ~DR_STEP;
 
-	if (kprobe_debug_handler(regs))
-		goto out;
-
 	/*
 	 * The kernel doesn't use INT1
 	 */
#21 lore raw

Re: [PATCH -tip 0/3] x86/kprobes: Remoev single-step trap from x86 kprobes

In reply to Masami Hiramatsu

Hi Andy,

Would you think you still need this series to remove iret to kernel?

Thank you,

Quoted 36 lines · On Wed, 3 Mar 2021 00:25:12 +0900 Masami Hiramatsu <[email protected]> wrote:

Hi,

Here is a series of patches to remove the single-step debug trap from the x86 kprobe.

The first 2 patches ([1/3][2/3]) are bugfixes which I've sent recently;

https://lore.kernel.org/lkml/161425451732.93763.18329509061375062554.stgit@devnote2/

And [3/3] is actually the patch to remove single-step from kprobes. The RFC version is here;

https://lore.kernel.org/lkml/161460768474.430263.18425867006584111900.stgit@devnote2/

This uses int3 as Andy suggested instead of the debug trap, for removing the IRET which returns to kernel.
Some instructions must be emulated and some instructions becomes not able to be probed, but as far as I can see those are not rare case.

Thank you,

---

Masami Hiramatsu (3):
x86/kprobes: Retrieve correct opcode for group instruction x86/kprobes: Identify far indirect JMP correctly x86/kprobes: Use int3 instead of debug trap for single-step

 arch/x86/include/asm/kprobes.h |   21 +-
 arch/x86/kernel/kprobes/core.c |  524 ++++++++++++++++++++++++++--------------
 arch/x86/kernel/traps.c        |    3 
 3 files changed, 358 insertions(+), 190 deletions(-)

--
Masami Hiramatsu (Linaro) <[email protected]>

Signature · 1 line
Masami Hiramatsu <[email protected]>
#22 lore raw

Re: [PATCH -tip 0/3] x86/kprobes: Remoev single-step trap from x86 kprobes

Quoted 3 lines · Masami Hiramatsu

Hi Andy,

Would you think you still need this series to remove iret to kernel?

They're an improvement anyway, let me queue them so that they don't get lost.

I'll line them up for tip/x86/core unless anybody else thinks of a better place for them and tells me in time ;-)

#23 lore raw

Re: [PATCH -tip 0/3] x86/kprobes: Remoev single-step trap from x86 kprobes

Quoted 11 lines · Peter Zijlstra
Quoted 3 lines · Masami Hiramatsu

Hi Andy,

Would you think you still need this series to remove iret to kernel?

They're an improvement anyway, let me queue them so that they don't get lost.

I'll line them up for tip/x86/core unless anybody else thinks of a better place for them and tells me in time ;-)

I agree. The new code is quite nice. I'm still not quite sure what I'm doing with IRET, but thank you very much for the kprobe improvements.

#24 lore raw
In reply to Andy Lutomirski
Quoted 2 lines · Andy Lutomirski

I guess I see the point for CALL, JMP and RET, but it seems like we could emulate those cases instead fairly easily.

Today, yes. CALL emulation was 'recently' made possible by having #BP have a stack gap. We have emulation for all 3 those instructions implemented in asm/text-patching.h, see int3_emulate_$insn().

#25 lore raw
In reply to Andy Lutomirski

Hi Andy,

sorry for delay.

Quoted 11 lines · Andy Lutomirski

A while back, I let myself be convinced that kprobes genuinely need to single-step the kernel on occasion, and I decided that this sucked but I could live with it. it would, however, be Really Really Nice (tm) if we could have a rule that anyone running x86 Linux who single-steps the kernel (e.g. kgdb and nothing else) gets to keep all the pieces when the system falls apart around them. Specifically, if we don't allow kernel single-stepping and if we suitably limit kernel instruction breakpoints (the latter isn't actually a major problem), then we don't really really need to use IRET to return to the kernel, and that means we can avoid some massive NMI nastiness.

Not sure I understand you correctly, I know almost nothing about low-level x86 magic.

But I guess this has nothing to do with uprobes, they do not single-step in kernel mode, right?

Quoted 4 lines

Uprobes seem to single-step user code for no discernable reason. (They want to trap after executing an out of line instruction, AFAICT. Surely INT3 or even CALL after the out-of-line insn would work as well or better.)

Uprobes use single-step from the very beginning, probably because this is the most simple and "standard" way to implement xol.

And please note that CALL/JMP/etc emulation was added much later to fix the problems with non-canonical addresses, and this emulation it still incomplete.

Oleg.

#26 lore raw
Quoted 18 lines · Oleg Nesterov

Hi Andy,

sorry for delay.

Quoted 11 lines · Andy Lutomirski

A while back, I let myself be convinced that kprobes genuinely need to single-step the kernel on occasion, and I decided that this sucked but I could live with it. it would, however, be Really Really Nice (tm) if we could have a rule that anyone running x86 Linux who single-steps the kernel (e.g. kgdb and nothing else) gets to keep all the pieces when the system falls apart around them. Specifically, if we don't allow kernel single-stepping and if we suitably limit kernel instruction breakpoints (the latter isn't actually a major problem), then we don't really really need to use IRET to return to the kernel, and that means we can avoid some massive NMI nastiness.

Quoted 5 lines

Not sure I understand you correctly, I know almost nothing about low-level x86 magic.

But I guess this has nothing to do with uprobes, they do not single-step in kernel mode, right?

They single-step user code, though, and the code that makes this work is quite ugly. Single-stepping on x86 is a mess.

Quoted 11 lines
Quoted 5 lines

Uprobes seem to single-step user code for no discernable reason. (They want to trap after executing an out of line instruction, AFAICT. Surely INT3 or even CALL after the out-of-line insn would work as well or better.)

Uprobes use single-step from the very beginning, probably because this is the most simple and "standard" way to implement xol.

And please note that CALL/JMP/etc emulation was added much later to fix the problems with non-canonical addresses, and this emulation it still incomplete.

Is there something like a uprobe test suite? How maintained / actively used is uprobe?

--Andy

#27 lore raw
Quoted 8 lines · Andy Lutomirski
Quoted 3 lines · Oleg Nesterov

But I guess this has nothing to do with uprobes, they do not single-step in kernel mode, right?

They single-step user code, though, and the code that makes this work is quite ugly. Single-stepping on x86 is a mess.

But this doesn't really differ from, say, gdb doing si ? OK, except uprobes have to hook DIE_DEBUG. Nevermind...

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

Uprobes seem to single-step user code for no discernable reason. (They want to trap after executing an out of line instruction, AFAICT. Surely INT3 or even CALL after the out-of-line insn would work as well or better.)

Quoted 7 lines
Quoted 5 lines

Uprobes use single-step from the very beginning, probably because this is the most simple and "standard" way to implement xol.

And please note that CALL/JMP/etc emulation was added much later to fix the problems with non-canonical addresses, and this emulation it still incomplete.

Is there something like a uprobe test suite?

Afaik, no.

Quoted 1 line

How maintained /

Add Srikar who sent the initial implementation. I can only say that I am glad that ./scripts/get_maintainer.pl no longer mentions me ;) I did some changes (including emulation) but a) this was a long ago and b) only because I was forced^W asked to fix the numerous bugs in this code.

Quoted 1 line

actively used is uprobe?

I have no idea, sorry ;)

Oleg.

#28 lore raw
Quoted 8 lines · Oleg Nesterov
Quoted 3 lines · Andy Lutomirski
Quoted 3 lines · Oleg Nesterov
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

But I guess this has nothing to do with uprobes, they do not single-step in kernel mode, right?

They single-step user code, though, and the code that makes this work is quite ugly. Single-stepping on x86 is a mess.

But this doesn't really differ from, say, gdb doing si ? OK, except uprobes have to hook DIE_DEBUG. Nevermind...

Also, gdb doing so isn’t great either. Single stepping over a pushf instruction, signal delivery, or a syscall on x86 is a mess.

Quoted 29 lines
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines

Uprobes seem to single-step user code for no discernable reason. (They want to trap after executing an out of line instruction, AFAICT. Surely INT3 or even CALL after the out-of-line insn would work as well or better.)

Quoted 7 lines
Quoted 5 lines

Uprobes use single-step from the very beginning, probably because this is the most simple and "standard" way to implement xol.

And please note that CALL/JMP/etc emulation was added much later to fix the problems with non-canonical addresses, and this emulation it still incomplete.

Is there something like a uprobe test suite?

Afaik, no.

Quoted 1 line

How maintained /

Add Srikar who sent the initial implementation. I can only say that I am glad that ./scripts/get_maintainer.pl no longer mentions me ;) I did some changes (including emulation) but a) this was a long ago and b) only because I was forced^W asked to fix the numerous bugs in this code.

Quoted 1 line

actively used is uprobe?

I have no idea, sorry ;)

Oleg.

#29 lore raw
In reply to Andy Lutomirski
Quoted 3 lines · Andy Lutomirski

Is there something like a uprobe test suite? How maintained / actively used is uprobe?

uprobe+bpf is heavily used in production.
selftests/bpf has only one test for it though.

Why are you asking?

#30 lore raw
Quoted 12 lines · Alexei Starovoitov
Quoted 3 lines · Andy Lutomirski

Is there something like a uprobe test suite? How maintained / actively used is uprobe?

uprobe+bpf is heavily used in production.
selftests/bpf has only one test for it though.

Why are you asking?

Because the integration with the x86 entry code is a mess, and I want to know whether to mark it BROKEN or how to make sure the any cleanups actually work.

#31 lore raw
Quoted 9 lines · Andy Lutomirski
Quoted 9 lines · Alexei Starovoitov
Quoted 3 lines · Andy Lutomirski

Is there something like a uprobe test suite? How maintained / actively used is uprobe?

Quoted 6 lines
Quoted 4 lines

uprobe+bpf is heavily used in production.
selftests/bpf has only one test for it though.

Why are you asking?

Because the integration with the x86 entry code is a mess, and I want to know whether to mark it BROKEN or how to make sure the any cleanups actually work.

Any test case to repro the issue you found?
Is it a bug or just messy code?
Nowadays a good chunk of popular applications (python, mysql, etc) has USDTs in them.
Issues reported with bcc:
https://github.com/iovisor/bcc/issues?q=is%3Aissue+USDT
Similar thing with bpftrace.
Both standard USDT and semaphore based are used in the wild. uprobe for containers has been a long standing feature request. If you can improve uprobe performance that would be awesome. That's another thing that people report often. We optimized it a bit. More can be done.

#32 lore raw
Quoted 16 lines · Alexei Starovoitov
Quoted 12 lines · Andy Lutomirski
Quoted 12 lines
Quoted 3 lines · Alexei Starovoitov
Quoted 3 lines · Andy Lutomirski

Is there something like a uprobe test suite? How maintained / actively used is uprobe?

uprobe+bpf is heavily used in production.
selftests/bpf has only one test for it though.

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

Why are you asking?

Because the integration with the x86 entry code is a mess, and I want to know whether to mark it BROKEN or how to make sure the any cleanups actually work.

Any test case to repro the issue you found?
Is it a bug or just messy code?

Just messy code.

Quoted 10 lines

Nowadays a good chunk of popular applications (python, mysql, etc) has USDTs in them.
Issues reported with bcc:
https://github.com/iovisor/bcc/issues?q=is%3Aissue+USDT
Similar thing with bpftrace.
Both standard USDT and semaphore based are used in the wild. uprobe for containers has been a long standing feature request. If you can improve uprobe performance that would be awesome. That's another thing that people report often. We optimized it a bit. More can be done.

Wait... USDT is much easier to implement well. Are we talking just USDT or are we talking about general uprobes in which almost any instruction can get probed? If the only users that care about uprobes are doing USDT, we could vastly simplify the implementation and probably make it faster, too.

#33 lore raw
Quoted 38 lines · Andy Lutomirski
Quoted 17 lines · Alexei Starovoitov
Quoted 12 lines · Andy Lutomirski
Quoted 12 lines
Quoted 3 lines · Alexei Starovoitov
Quoted 3 lines · Andy Lutomirski

Is there something like a uprobe test suite? How maintained / actively used is uprobe?

uprobe+bpf is heavily used in production.
selftests/bpf has only one test for it though.

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

Why are you asking?

Because the integration with the x86 entry code is a mess, and I want to know whether to mark it BROKEN or how to make sure the any cleanups actually work.

Any test case to repro the issue you found?
Is it a bug or just messy code?

Just messy code.

Quoted 10 lines

Nowadays a good chunk of popular applications (python, mysql, etc) has USDTs in them.
Issues reported with bcc:
https://github.com/iovisor/bcc/issues?q=is%3Aissue+USDT
Similar thing with bpftrace.
Both standard USDT and semaphore based are used in the wild. uprobe for containers has been a long standing feature request. If you can improve uprobe performance that would be awesome. That's another thing that people report often. We optimized it a bit. More can be done.

Quoted 1 line

Wait... USDT is much easier to implement well. Are we talking just USDT or are we talking about general uprobes in which almost any instruction can get probed? If the only users that care about uprobes are doing USDT, we could vastly simplify the implementation and probably make it faster, too.

USDTs are driving the majority of uprobe usage.
If they can get faster it will increase their adoption even more. There are certainly cases of normal uprobes.
They are at the start of the function 99% of the time. Like the following:
"uprobe:/lib64/libc.so:malloc(u64 size):size:size,_ret", "uprobe:/lib64/libc.so:free(void *ptr)::ptr",
is common despite its overhead.

Here is the most interesting and practical usage of uprobes:
https://github.com/iovisor/bcc/blob/master/tools/sslsniff.py
and the manpage for the tool:
https://github.com/iovisor/bcc/blob/master/tools/sslsniff_example.txt

uprobe in the middle of the function is very rare.
If the kernel starts rejecting uprobes on some weird instructions I suspect no one will complain.
Especially if such tightening will come with performance boost for uprobe on a nop and unprobe at the start (which is typically push or alu on %sp).
That would be a great step forward.

#34 lore raw
Quoted 5 lines · Alexei Starovoitov

Especially if such tightening will come with performance boost for uprobe on a nop and unprobe at the start (which is typically push or alu on %sp).
That would be a great step forward.

Just in case, nop and push are emulated without additional overhead.

Oleg.

#35 lore raw
In reply to Alexei Starovoitov
Quoted 39 lines · Alexei Starovoitov
Quoted 38 lines · Andy Lutomirski
Quoted 17 lines · Alexei Starovoitov
Quoted 12 lines · Andy Lutomirski
Quoted 12 lines
Quoted 3 lines · Alexei Starovoitov
Quoted 3 lines · Andy Lutomirski

Is there something like a uprobe test suite? How maintained / actively used is uprobe?

uprobe+bpf is heavily used in production.
selftests/bpf has only one test for it though.

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

Why are you asking?

Because the integration with the x86 entry code is a mess, and I want to know whether to mark it BROKEN or how to make sure the any cleanups actually work.

Any test case to repro the issue you found?
Is it a bug or just messy code?

Just messy code.

Quoted 10 lines

Nowadays a good chunk of popular applications (python, mysql, etc) has USDTs in them.
Issues reported with bcc:
https://github.com/iovisor/bcc/issues?q=is%3Aissue+USDT
Similar thing with bpftrace.
Both standard USDT and semaphore based are used in the wild. uprobe for containers has been a long standing feature request. If you can improve uprobe performance that would be awesome. That's another thing that people report often. We optimized it a bit. More can be done.

Quoted 3 lines
Quoted 1 line

Wait... USDT is much easier to implement well. Are we talking just USDT or are we talking about general uprobes in which almost any instruction can get probed? If the only users that care about uprobes are doing USDT, we could vastly simplify the implementation and probably make it faster, too.

USDTs are driving the majority of uprobe usage.

I'd say 50/50 in my experience. Larger userspace applications using bpf for production monitoring tend to use USDT for stability and ABI reasons (hard for bpf to read C++ classes). Bare uprobes (ie not USDT) are used quite often for ad-hoc production debugging.

Quoted 8 lines

If they can get faster it will increase their adoption even more. There are certainly cases of normal uprobes.
They are at the start of the function 99% of the time. Like the following:
"uprobe:/lib64/libc.so:malloc(u64 size):size:size,_ret", "uprobe:/lib64/libc.so:free(void *ptr)::ptr",
is common despite its overhead.

Quoted 8 lines

Here is the most interesting and practical usage of uprobes:
https://github.com/iovisor/bcc/blob/master/tools/sslsniff.py
and the manpage for the tool:
https://github.com/iovisor/bcc/blob/master/tools/sslsniff_example.txt

uprobe in the middle of the function is very rare.
If the kernel starts rejecting uprobes on some weird instructions I suspect no one will complain.

I think it would be great if the kernel could reject mid-instruction uprobes. Unlike with kprobes, you can place uprobes on immediate operands which can cause silent data corruption. See
https://github.com/iovisor/bpftrace/pull/803#issuecomment-507693933
for a funny example.

To prevent accidental (and silent) data corruption, bpftrace uses a disassembler to ensure uprobes are placed on instruction boundaries.

<...>

Daniel

#36 lore raw
Quoted 69 lines · Daniel Xu
Quoted 39 lines · Alexei Starovoitov
Quoted 39 lines · Andy Lutomirski
Quoted 17 lines · Alexei Starovoitov
Quoted 12 lines · Andy Lutomirski
Quoted 12 lines
Quoted 3 lines · Alexei Starovoitov
Quoted 3 lines · Andy Lutomirski

Is there something like a uprobe test suite? How maintained / actively used is uprobe?

uprobe+bpf is heavily used in production.
selftests/bpf has only one test for it though.

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

Why are you asking?

Because the integration with the x86 entry code is a mess, and I want to know whether to mark it BROKEN or how to make sure the any cleanups actually work.

Any test case to repro the issue you found?
Is it a bug or just messy code?

Just messy code.

Quoted 10 lines

Nowadays a good chunk of popular applications (python, mysql, etc) has USDTs in them.
Issues reported with bcc:
https://github.com/iovisor/bcc/issues?q=is%3Aissue+USDT
Similar thing with bpftrace.
Both standard USDT and semaphore based are used in the wild. uprobe for containers has been a long standing feature request. If you can improve uprobe performance that would be awesome. That's another thing that people report often. We optimized it a bit. More can be done.

Quoted 3 lines
Quoted 1 line

Wait... USDT is much easier to implement well. Are we talking just USDT or are we talking about general uprobes in which almost any instruction can get probed? If the only users that care about uprobes are doing USDT, we could vastly simplify the implementation and probably make it faster, too.

USDTs are driving the majority of uprobe usage.

I'd say 50/50 in my experience. Larger userspace applications using bpf for production monitoring tend to use USDT for stability and ABI reasons (hard for bpf to read C++ classes). Bare uprobes (ie not USDT) are used quite often for ad-hoc production debugging.

Quoted 8 lines

If they can get faster it will increase their adoption even more. There are certainly cases of normal uprobes.
They are at the start of the function 99% of the time. Like the following:
"uprobe:/lib64/libc.so:malloc(u64 size):size:size,_ret", "uprobe:/lib64/libc.so:free(void *ptr)::ptr",
is common despite its overhead.

Quoted 8 lines

Here is the most interesting and practical usage of uprobes:
https://github.com/iovisor/bcc/blob/master/tools/sslsniff.py
and the manpage for the tool:
https://github.com/iovisor/bcc/blob/master/tools/sslsniff_example.txt

uprobe in the middle of the function is very rare.
If the kernel starts rejecting uprobes on some weird instructions I suspect no one will complain.

Quoted 5 lines

I think it would be great if the kernel could reject mid-instruction uprobes. Unlike with kprobes, you can place uprobes on immediate operands which can cause silent data corruption. See
https://github.com/iovisor/bpftrace/pull/803#issuecomment-507693933
for a funny example.

This can’t be done in general on x86. One cannot look at code and find the instruction boundaries.

Quoted 7 lines

To prevent accidental (and silent) data corruption, bpftrace uses a disassembler to ensure uprobes are placed on instruction boundaries.

<...>

Daniel

#37 lore raw
In reply to Andy Lutomirski

forgot to add Srikar, sorry for resend...

Quoted 8 lines · Andy Lutomirski
Quoted 3 lines · Oleg Nesterov

But I guess this has nothing to do with uprobes, they do not single-step in kernel mode, right?

They single-step user code, though, and the code that makes this work is quite ugly. Single-stepping on x86 is a mess.

But this doesn't really differ from, say, gdb doing si ? OK, except uprobes have to hook DIE_DEBUG. Nevermind...

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

Uprobes seem to single-step user code for no discernable reason. (They want to trap after executing an out of line instruction, AFAICT. Surely INT3 or even CALL after the out-of-line insn would work as well or better.)

Quoted 7 lines
Quoted 5 lines

Uprobes use single-step from the very beginning, probably because this is the most simple and "standard" way to implement xol.

And please note that CALL/JMP/etc emulation was added much later to fix the problems with non-canonical addresses, and this emulation it still incomplete.

Is there something like a uprobe test suite?

Afaik, no.

Quoted 1 line

How maintained /

Add Srikar who sent the initial implementation. I can only say that I am glad that ./scripts/get_maintainer.pl no longer mentions me ;) I did some changes (including emulation) but a) this was a long ago and b) only because I was forced^W asked to fix the numerous bugs in this code.

Quoted 1 line

actively used is uprobe?

I have no idea, sorry ;)

Oleg.

#38 lore raw
In reply to Oleg Nesterov

Hi Oleg and Andy,

Quoted 17 lines · On Mon, 1 Mar 2021 17:51:31 +0100 Oleg Nesterov <[email protected]> wrote:

Hi Andy,

sorry for delay.

Quoted 11 lines · Andy Lutomirski

A while back, I let myself be convinced that kprobes genuinely need to single-step the kernel on occasion, and I decided that this sucked but I could live with it. it would, however, be Really Really Nice (tm) if we could have a rule that anyone running x86 Linux who single-steps the kernel (e.g. kgdb and nothing else) gets to keep all the pieces when the system falls apart around them. Specifically, if we don't allow kernel single-stepping and if we suitably limit kernel instruction breakpoints (the latter isn't actually a major problem), then we don't really really need to use IRET to return to the kernel, and that means we can avoid some massive NMI nastiness.

Quoted 2 lines

Not sure I understand you correctly, I know almost nothing about low-level x86 magic.

x86 has normal interrupt and NMI. When an NMI occurs the CPU masks NMI (the mask itself is hidden status) and IRET releases the mask. The problem is that if an INT3 is hit in the NMI handler and does a single-stepping, it has to use IRET for atomically setting TF and return.

Quoted 3 lines

But I guess this has nothing to do with uprobes, they do not single-step in kernel mode, right?

Agreed, if the problematic case is IRET from NMI handler, uprobes doesn't hit because it only invoked from user-space.
Andy, what would you think?

Quoted 10 lines
Quoted 4 lines

Uprobes seem to single-step user code for no discernable reason. (They want to trap after executing an out of line instruction, AFAICT. Surely INT3 or even CALL after the out-of-line insn would work as well or better.)

Uprobes use single-step from the very beginning, probably because this is the most simple and "standard" way to implement xol.

And please note that CALL/JMP/etc emulation was added much later to fix the problems with non-canonical addresses, and this emulation it still incomplete.

Yeah, I found another implementation of the emulation afterwards. Of cource since uprobes only treat user-space, it maybe need more care.

Thank you,

Signature · 1 line
Masami Hiramatsu <[email protected]>
#39 lore raw
Quoted 32 lines · Masami Hiramatsu

Hi Oleg and Andy,

Quoted 17 lines · On Mon, 1 Mar 2021 17:51:31 +0100 Oleg Nesterov <[email protected]> wrote:

Hi Andy,

sorry for delay.

Quoted 11 lines · Andy Lutomirski

A while back, I let myself be convinced that kprobes genuinely need to single-step the kernel on occasion, and I decided that this sucked but I could live with it. it would, however, be Really Really Nice (tm) if we could have a rule that anyone running x86 Linux who single-steps the kernel (e.g. kgdb and nothing else) gets to keep all the pieces when the system falls apart around them. Specifically, if we don't allow kernel single-stepping and if we suitably limit kernel instruction breakpoints (the latter isn't actually a major problem), then we don't really really need to use IRET to return to the kernel, and that means we can avoid some massive NMI nastiness.

Quoted 2 lines

Not sure I understand you correctly, I know almost nothing about low-level x86 magic.

x86 has normal interrupt and NMI. When an NMI occurs the CPU masks NMI (the mask itself is hidden status) and IRET releases the mask. The problem is that if an INT3 is hit in the NMI handler and does a single-stepping, it has to use IRET for atomically setting TF and return.

Quoted 1 line
Quoted 6 lines
Quoted 2 lines

But I guess this has nothing to do with uprobes, they do not single-step in kernel mode, right?

Agreed, if the problematic case is IRET from NMI handler, uprobes doesn't hit because it only invoked from user-space.
Andy, what would you think?

Indeed, this isn't a problem for uprobes. The problem for uprobes is that all the notifiers from #DB are kind of messy, and I would like to get rid of them if possible.

#40 lore raw
In reply to Masami Hiramatsu
Quoted 8 lines · Masami Hiramatsu
Quoted 3 lines

Not sure I understand you correctly, I know almost nothing about low-level x86 magic.

x86 has normal interrupt and NMI. When an NMI occurs the CPU masks NMI (the mask itself is hidden status) and IRET releases the mask. The problem is that if an INT3 is hit in the NMI handler and does a single-stepping, it has to use IRET for atomically setting TF and return.

Ah, thanks a lot,

Oleg.