Skip to contentSkip to conversation

riscv: kprobe: Fixup kernel panic when probing an illegal position

PATCH Superseded [email protected] 3 participants 14 messages Jan 26, 2023 – Feb 2, 2023 RISC-V

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 riscv: kprobe: Fixup kernel panic when probing an illegal position

From: Guo Ren <[email protected]>

The kernel would panic when probed for an illegal position. eg:

(CONFIG_RISCV_ISA_C=n)

echo 'p:hello kernel_clone+0x16 a0=%a0' >> kprobe_events echo 1 > events/kprobes/hello/enable
cat trace

Kernel panic - not syncing: stack-protector: Kernel stack
is corrupted in: __do_sys_newfstatat+0xb8/0xb8
CPU: 0 PID: 111 Comm: sh Not tainted
6.2.0-rc1-00027-g2d398fe49a4d #490
Hardware name: riscv-virtio,qemu (DT)
Call Trace:
[<ffffffff80007268>] dump_backtrace+0x38/0x48
[<ffffffff80c5e83c>] show_stack+0x50/0x68
[<ffffffff80c6da28>] dump_stack_lvl+0x60/0x84
[<ffffffff80c6da6c>] dump_stack+0x20/0x30
[<ffffffff80c5ecf4>] panic+0x160/0x374
[<ffffffff80c6db94>] generic_handle_arch_irq+0x0/0xa8
[<ffffffff802deeb0>] sys_newstat+0x0/0x30
[<ffffffff800158c0>] sys_clone+0x20/0x30
[<ffffffff800039e8>] ret_from_syscall+0x0/0x4
---[ end Kernel panic - not syncing: stack-protector:
Kernel stack is corrupted in: __do_sys_newfstatat+0xb8/0xb8 ]---

That is because the kprobe's ebreak instruction broke the kernel's original code. The user should guarantee the correction of the probe position, but it couldn't make the kernel panic.

This patch adds arch_check_kprobe in arch_prepare_kprobe to prevent an illegal position (Such as the middle of an instruction).

Fixes c22b0bcb1dd0 ("riscv: Add kprobes supported")
---
 arch/riscv/kernel/probes/kprobes.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
arch/riscv/kernel/probes/kprobes.c +18 -0
index f21592d20306..475989f06d6d 100644
@@ -48,6 +48,21 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
 	post_kprobe_handler(p, kcb, regs);
 }
 
+static bool __kprobes arch_check_kprobe(struct kprobe *p)
+{
+	unsigned long tmp  = (unsigned long)p->addr - p->offset;
+	unsigned long addr = (unsigned long)p->addr;
+
+	while (tmp <= addr) {
+		if (tmp == addr)
+			return true;
+
+		tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);
+	}
+
+	return false;
+}
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
 	unsigned long probe_addr = (unsigned long)p->addr;
@@ -55,6 +70,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
 	if (probe_addr & 0x1)
 		return -EILSEQ;
 
+	if (!arch_check_kprobe(p))
+		return -EILSEQ;
+
 	/* copy instruction */
 	p->opcode = *p->addr;
 
Signature · 1 line
2.36.1
#1 lore raw
Quoted 54 lines · 在 2023/1/26 21:05, [email protected] 写道:

From: Guo Ren <[email protected]>

The kernel would panic when probed for an illegal position. eg:

(CONFIG_RISCV_ISA_C=n)

echo 'p:hello kernel_clone+0x16 a0=%a0' >> kprobe_events echo 1 > events/kprobes/hello/enable
cat trace

Kernel panic - not syncing: stack-protector: Kernel stack
is corrupted in: __do_sys_newfstatat+0xb8/0xb8
CPU: 0 PID: 111 Comm: sh Not tainted
6.2.0-rc1-00027-g2d398fe49a4d #490
Hardware name: riscv-virtio,qemu (DT)
Call Trace:
[<ffffffff80007268>] dump_backtrace+0x38/0x48
[<ffffffff80c5e83c>] show_stack+0x50/0x68
[<ffffffff80c6da28>] dump_stack_lvl+0x60/0x84
[<ffffffff80c6da6c>] dump_stack+0x20/0x30
[<ffffffff80c5ecf4>] panic+0x160/0x374
[<ffffffff80c6db94>] generic_handle_arch_irq+0x0/0xa8
[<ffffffff802deeb0>] sys_newstat+0x0/0x30
[<ffffffff800158c0>] sys_clone+0x20/0x30
[<ffffffff800039e8>] ret_from_syscall+0x0/0x4
---[ end Kernel panic - not syncing: stack-protector:
Kernel stack is corrupted in: __do_sys_newfstatat+0xb8/0xb8 ]---

That is because the kprobe's ebreak instruction broke the kernel's original code. The user should guarantee the correction of the probe position, but it couldn't make the kernel panic.

This patch adds arch_check_kprobe in arch_prepare_kprobe to prevent an illegal position (Such as the middle of an instruction).

Fixes c22b0bcb1dd0 ("riscv: Add kprobes supported")
---
 arch/riscv/kernel/probes/kprobes.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
arch/riscv/kernel/probes/kprobes.c +4 -0
index f21592d20306..475989f06d6d 100644
@@ -48,6 +48,21 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
 	post_kprobe_handler(p, kcb, regs);
 }
 
+static bool __kprobes arch_check_kprobe(struct kprobe *p)
+{
+	unsigned long tmp  = (unsigned long)p->addr - p->offset;
+	unsigned long addr = (unsigned long)p->addr;

arch/riscv/kernel/probes/kprobes.c static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs) · line 64

Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +10 -0
+
+	while (tmp <= addr) {
+		if (tmp == addr)
+			return true;
+
+		tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);
+	}
+
+	return false;
+}

LGTM.

I have submit a patch to fix the same problem, found at:

https://lore.kernel.org/lkml/[email protected]/

So this boundary check is necessary no matter CONFIG_RISCV_ISA_C is enable or not, right?

Quoted 14 lines
arch/riscv/kernel/probes/kprobes.c +4 -0
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
 	unsigned long probe_addr = (unsigned long)p->addr;
@@ -55,6 +70,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
 	if (probe_addr & 0x1)
 		return -EILSEQ;
 
+	if (!arch_check_kprobe(p))
+		return -EILSEQ;
+
 	/* copy instruction */
 	p->opcode = *p->addr;
 
Signature · 2 lines
BR,
Liao, Chang
author
#2 lore raw
Quoted 71 lines · liaochang (A)
Quoted 54 lines · 在 2023/1/26 21:05, [email protected] 写道:

From: Guo Ren <[email protected]>

The kernel would panic when probed for an illegal position. eg:

(CONFIG_RISCV_ISA_C=n)

echo 'p:hello kernel_clone+0x16 a0=%a0' >> kprobe_events echo 1 > events/kprobes/hello/enable
cat trace

Kernel panic - not syncing: stack-protector: Kernel stack
is corrupted in: __do_sys_newfstatat+0xb8/0xb8
CPU: 0 PID: 111 Comm: sh Not tainted
6.2.0-rc1-00027-g2d398fe49a4d #490
Hardware name: riscv-virtio,qemu (DT)
Call Trace:
[<ffffffff80007268>] dump_backtrace+0x38/0x48
[<ffffffff80c5e83c>] show_stack+0x50/0x68
[<ffffffff80c6da28>] dump_stack_lvl+0x60/0x84
[<ffffffff80c6da6c>] dump_stack+0x20/0x30
[<ffffffff80c5ecf4>] panic+0x160/0x374
[<ffffffff80c6db94>] generic_handle_arch_irq+0x0/0xa8
[<ffffffff802deeb0>] sys_newstat+0x0/0x30
[<ffffffff800158c0>] sys_clone+0x20/0x30
[<ffffffff800039e8>] ret_from_syscall+0x0/0x4
---[ end Kernel panic - not syncing: stack-protector:
Kernel stack is corrupted in: __do_sys_newfstatat+0xb8/0xb8 ]---

That is because the kprobe's ebreak instruction broke the kernel's original code. The user should guarantee the correction of the probe position, but it couldn't make the kernel panic.

This patch adds arch_check_kprobe in arch_prepare_kprobe to prevent an illegal position (Such as the middle of an instruction).

Fixes c22b0bcb1dd0 ("riscv: Add kprobes supported")
---
 arch/riscv/kernel/probes/kprobes.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
arch/riscv/kernel/probes/kprobes.c +4 -0
index f21592d20306..475989f06d6d 100644
@@ -48,6 +48,21 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
      post_kprobe_handler(p, kcb, regs);
 }
 
+static bool __kprobes arch_check_kprobe(struct kprobe *p)
+{
+     unsigned long tmp  = (unsigned long)p->addr - p->offset;
+     unsigned long addr = (unsigned long)p->addr;

arch/riscv/kernel/probes/kprobes.c static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs) · line 64

Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +10 -0
+
+     while (tmp <= addr) {
+             if (tmp == addr)
+                     return true;
+
+             tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);
+     }
+
+     return false;
+}

LGTM.

Quoted 1 line

I have submit a patch to fix the same problem, found at:

Oh, I missed that patch. Our goal is the same.

But it would be best if you reused p->offset, not kallsyms_lookup_size_offset, the p->addr added by _kprobe_addr (kernel/kprobes.c), not just kallsyms_lookup_size_offset. Sure, it works around for the current riscv, but that's not correct.

Quoted 4 lines

https://lore.kernel.org/lkml/[email protected]/

So this boundary check is necessary no matter CONFIG_RISCV_ISA_C is enable or not, right?

Yes, my panic example in the commit log is based on the !CONFIG_RISCV_ISA_C, you couldn't miss that.

Quoted 20 lines
Quoted 7 lines
arch/riscv/kernel/probes/kprobes.c +1 -0
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
      unsigned long probe_addr = (unsigned long)p->addr;

arch/riscv/kernel/probes/kprobes.c int __kprobes arch_prepare_kprobe(struct kprobe *p) · line 78

Quoted 9 lines
arch/riscv/kernel/probes/kprobes.c +3 -0
@@ -55,6 +70,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
      if (probe_addr & 0x1)
              return -EILSEQ;
 
+     if (!arch_check_kprobe(p))
+             return -EILSEQ;
+
      /* copy instruction */
      p->opcode = *p->addr;
 

--
BR,
Liao, Chang

Signature · 2 lines
Best Regards
 Guo Ren
author
#3 lore raw
Quoted 73 lines · Guo Ren
Quoted 71 lines · liaochang (A)
Quoted 54 lines · 在 2023/1/26 21:05, [email protected] 写道:

From: Guo Ren <[email protected]>

The kernel would panic when probed for an illegal position. eg:

(CONFIG_RISCV_ISA_C=n)

echo 'p:hello kernel_clone+0x16 a0=%a0' >> kprobe_events echo 1 > events/kprobes/hello/enable
cat trace

Kernel panic - not syncing: stack-protector: Kernel stack
is corrupted in: __do_sys_newfstatat+0xb8/0xb8
CPU: 0 PID: 111 Comm: sh Not tainted
6.2.0-rc1-00027-g2d398fe49a4d #490
Hardware name: riscv-virtio,qemu (DT)
Call Trace:
[<ffffffff80007268>] dump_backtrace+0x38/0x48
[<ffffffff80c5e83c>] show_stack+0x50/0x68
[<ffffffff80c6da28>] dump_stack_lvl+0x60/0x84
[<ffffffff80c6da6c>] dump_stack+0x20/0x30
[<ffffffff80c5ecf4>] panic+0x160/0x374
[<ffffffff80c6db94>] generic_handle_arch_irq+0x0/0xa8
[<ffffffff802deeb0>] sys_newstat+0x0/0x30
[<ffffffff800158c0>] sys_clone+0x20/0x30
[<ffffffff800039e8>] ret_from_syscall+0x0/0x4
---[ end Kernel panic - not syncing: stack-protector:
Kernel stack is corrupted in: __do_sys_newfstatat+0xb8/0xb8 ]---

That is because the kprobe's ebreak instruction broke the kernel's original code. The user should guarantee the correction of the probe position, but it couldn't make the kernel panic.

This patch adds arch_check_kprobe in arch_prepare_kprobe to prevent an illegal position (Such as the middle of an instruction).

Fixes c22b0bcb1dd0 ("riscv: Add kprobes supported")
---
 arch/riscv/kernel/probes/kprobes.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
arch/riscv/kernel/probes/kprobes.c +4 -0
index f21592d20306..475989f06d6d 100644
@@ -48,6 +48,21 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
      post_kprobe_handler(p, kcb, regs);
 }
 
+static bool __kprobes arch_check_kprobe(struct kprobe *p)
+{
+     unsigned long tmp  = (unsigned long)p->addr - p->offset;
+     unsigned long addr = (unsigned long)p->addr;

arch/riscv/kernel/probes/kprobes.c static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs) · line 64

Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +10 -0
+
+     while (tmp <= addr) {
+             if (tmp == addr)
+                     return true;
+
+             tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);
+     }
+
+     return false;
+}

LGTM.

Quoted 7 lines
Quoted 1 line

I have submit a patch to fix the same problem, found at:

Oh, I missed that patch. Our goal is the same.

But it would be best if you reused p->offset, not kallsyms_lookup_size_offset, the p->addr added by _kprobe_addr (kernel/kprobes.c), not just kallsyms_lookup_size_offset. Sure, it works around for the current riscv, but that's not correct.

Sorry, the above description is a little bit confusing. What I mean is that:
The p->addr = func_entry + p->offset. Not kallsyms_lookup_size_offset. Your patch could get the wrong func_entry.

Quoted 34 lines
Quoted 5 lines

https://lore.kernel.org/lkml/[email protected]/

So this boundary check is necessary no matter CONFIG_RISCV_ISA_C is enable or not, right?

Yes, my panic example in the commit log is based on the !CONFIG_RISCV_ISA_C, you couldn't miss that.

Quoted 10 lines
Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +1 -0
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
      unsigned long probe_addr = (unsigned long)p->addr;
@@ -55,6 +70,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
      if (probe_addr & 0x1)
              return -EILSEQ;
 
Quoted 10 lines

arch/riscv/kernel/probes/kprobes.c int __kprobes arch_prepare_kprobe(struct kprobe *p) · line 78

Quoted 6 lines
arch/riscv/kernel/probes/kprobes.c +3 -0
+     if (!arch_check_kprobe(p))
+             return -EILSEQ;
+
      /* copy instruction */
      p->opcode = *p->addr;
 

--
BR,
Liao, Chang

--
Best Regards
Guo Ren

Signature · 2 lines
Best Regards
 Guo Ren
#4 lore raw
Quoted 77 lines · Guo Ren
Quoted 76 lines · Guo Ren
Quoted 71 lines · liaochang (A)
Quoted 54 lines · 在 2023/1/26 21:05, [email protected] 写道:

From: Guo Ren <[email protected]>

The kernel would panic when probed for an illegal position. eg:

(CONFIG_RISCV_ISA_C=n)

echo 'p:hello kernel_clone+0x16 a0=%a0' >> kprobe_events echo 1 > events/kprobes/hello/enable
cat trace

Kernel panic - not syncing: stack-protector: Kernel stack
is corrupted in: __do_sys_newfstatat+0xb8/0xb8
CPU: 0 PID: 111 Comm: sh Not tainted
6.2.0-rc1-00027-g2d398fe49a4d #490
Hardware name: riscv-virtio,qemu (DT)
Call Trace:
[<ffffffff80007268>] dump_backtrace+0x38/0x48
[<ffffffff80c5e83c>] show_stack+0x50/0x68
[<ffffffff80c6da28>] dump_stack_lvl+0x60/0x84
[<ffffffff80c6da6c>] dump_stack+0x20/0x30
[<ffffffff80c5ecf4>] panic+0x160/0x374
[<ffffffff80c6db94>] generic_handle_arch_irq+0x0/0xa8
[<ffffffff802deeb0>] sys_newstat+0x0/0x30
[<ffffffff800158c0>] sys_clone+0x20/0x30
[<ffffffff800039e8>] ret_from_syscall+0x0/0x4
---[ end Kernel panic - not syncing: stack-protector:
Kernel stack is corrupted in: __do_sys_newfstatat+0xb8/0xb8 ]---

That is because the kprobe's ebreak instruction broke the kernel's original code. The user should guarantee the correction of the probe position, but it couldn't make the kernel panic.

This patch adds arch_check_kprobe in arch_prepare_kprobe to prevent an illegal position (Such as the middle of an instruction).

Fixes c22b0bcb1dd0 ("riscv: Add kprobes supported")
---
 arch/riscv/kernel/probes/kprobes.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
arch/riscv/kernel/probes/kprobes.c +4 -0
index f21592d20306..475989f06d6d 100644
@@ -48,6 +48,21 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
      post_kprobe_handler(p, kcb, regs);
 }
 
+static bool __kprobes arch_check_kprobe(struct kprobe *p)
+{
+     unsigned long tmp  = (unsigned long)p->addr - p->offset;
+     unsigned long addr = (unsigned long)p->addr;

arch/riscv/kernel/probes/kprobes.c static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs) · line 64

Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +10 -0
+
+     while (tmp <= addr) {
+             if (tmp == addr)
+                     return true;
+
+             tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);
+     }
+
+     return false;
+}

LGTM.

Quoted 1 line

I have submit a patch to fix the same problem, found at:

Oh, I missed that patch. Our goal is the same.

Quoted 7 lines
Quoted 4 lines

But it would be best if you reused p->offset, not kallsyms_lookup_size_offset, the p->addr added by _kprobe_addr (kernel/kprobes.c), not just kallsyms_lookup_size_offset. Sure, it works around for the current riscv, but that's not correct.

Sorry, the above description is a little bit confusing. What I mean is that:
The p->addr = func_entry + p->offset. Not kallsyms_lookup_size_offset. Your patch could get the wrong func_entry.

According to my testing and debuggin on QEMU, I think both your patch and mine is able
to find the correct func_entry, because _kprobe_addr also uses kallsyms_lookup_size_offset
to get the first instruction address of given symbol. Of course, i prefere to your patch because it reuse p->offset.

Thanks.

Quoted 38 lines
Quoted 35 lines
Quoted 6 lines

https://lore.kernel.org/lkml/[email protected]/

So this boundary check is necessary no matter CONFIG_RISCV_ISA_C is enable or not, right?

Yes, my panic example in the commit log is based on the !CONFIG_RISCV_ISA_C, you couldn't miss that.

Quoted 10 lines
Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +1 -0
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
      unsigned long probe_addr = (unsigned long)p->addr;
@@ -55,6 +70,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
      if (probe_addr & 0x1)
              return -EILSEQ;
 
Quoted 10 lines

arch/riscv/kernel/probes/kprobes.c int __kprobes arch_prepare_kprobe(struct kprobe *p) · line 78

Quoted 6 lines
arch/riscv/kernel/probes/kprobes.c +3 -0
+     if (!arch_check_kprobe(p))
+             return -EILSEQ;
+
      /* copy instruction */
      p->opcode = *p->addr;
 

--
BR,
Liao, Chang

--
Best Regards
Guo Ren

Signature · 2 lines
BR,
Liao, Chang
author
#5 lore raw
In reply to liaochang (A)
Quoted 71 lines · liaochang (A)
Quoted 54 lines · 在 2023/1/26 21:05, [email protected] 写道:

From: Guo Ren <[email protected]>

The kernel would panic when probed for an illegal position. eg:

(CONFIG_RISCV_ISA_C=n)

echo 'p:hello kernel_clone+0x16 a0=%a0' >> kprobe_events echo 1 > events/kprobes/hello/enable
cat trace

Kernel panic - not syncing: stack-protector: Kernel stack
is corrupted in: __do_sys_newfstatat+0xb8/0xb8
CPU: 0 PID: 111 Comm: sh Not tainted
6.2.0-rc1-00027-g2d398fe49a4d #490
Hardware name: riscv-virtio,qemu (DT)
Call Trace:
[<ffffffff80007268>] dump_backtrace+0x38/0x48
[<ffffffff80c5e83c>] show_stack+0x50/0x68
[<ffffffff80c6da28>] dump_stack_lvl+0x60/0x84
[<ffffffff80c6da6c>] dump_stack+0x20/0x30
[<ffffffff80c5ecf4>] panic+0x160/0x374
[<ffffffff80c6db94>] generic_handle_arch_irq+0x0/0xa8
[<ffffffff802deeb0>] sys_newstat+0x0/0x30
[<ffffffff800158c0>] sys_clone+0x20/0x30
[<ffffffff800039e8>] ret_from_syscall+0x0/0x4
---[ end Kernel panic - not syncing: stack-protector:
Kernel stack is corrupted in: __do_sys_newfstatat+0xb8/0xb8 ]---

That is because the kprobe's ebreak instruction broke the kernel's original code. The user should guarantee the correction of the probe position, but it couldn't make the kernel panic.

This patch adds arch_check_kprobe in arch_prepare_kprobe to prevent an illegal position (Such as the middle of an instruction).

Fixes c22b0bcb1dd0 ("riscv: Add kprobes supported")
---
 arch/riscv/kernel/probes/kprobes.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
arch/riscv/kernel/probes/kprobes.c +4 -0
index f21592d20306..475989f06d6d 100644
@@ -48,6 +48,21 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
      post_kprobe_handler(p, kcb, regs);
 }
 
+static bool __kprobes arch_check_kprobe(struct kprobe *p)
+{
+     unsigned long tmp  = (unsigned long)p->addr - p->offset;
+     unsigned long addr = (unsigned long)p->addr;

arch/riscv/kernel/probes/kprobes.c static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs) · line 64

Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +10 -0
+
+     while (tmp <= addr) {
+             if (tmp == addr)
+                     return true;
+
+             tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);
+     }
+
+     return false;
+}

LGTM.

Quoted 3 lines

I have submit a patch to fix the same problem, found at:

https://lore.kernel.org/lkml/[email protected]/

I have a question on your OPTKPROBE patch:
https://lore.kernel.org/lkml/[email protected]/

@@ -490,7 +573,14 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op,
* to detour buffer, ra is used to form JR jumping back from detour
* buffer.
*/
- find_free_registers(orig, op, &rd, &ra);
+ if (used_reg == ALL_REG_OCCUPIED) {
+ find_free_registers(orig, op, &rd, &ra);
+ } else {
+ /* Choose one unused caller-saved register. */
+ rd = ffz(used_reg);
+ ra = rd;
+ }
+
if (rd == 0 || ra == 0) {
^^^^^^^^^^^^^^^^^^
After no opt_used_dst_reg & no free caller-saved register (Of cause, it's very rare for no available tmp regs):

Why not try:

     0: REG_S  ra, -SZREG(sp)
     4: auipc  ra, ?
     8: jalr   ?(ra)
    12: REG_L  ra, -SZREG(sp)

Besides taking up more instruction slots, does it have other problems?

Quoted 22 lines

So this boundary check is necessary no matter CONFIG_RISCV_ISA_C is enable or not, right?

Quoted 5 lines
arch/riscv/kernel/probes/kprobes.c +1 -0
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
      unsigned long probe_addr = (unsigned long)p->addr;

arch/riscv/kernel/probes/kprobes.c int __kprobes arch_prepare_kprobe(struct kprobe *p) · line 78

Quoted 9 lines
arch/riscv/kernel/probes/kprobes.c +3 -0
@@ -55,6 +70,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
      if (probe_addr & 0x1)
              return -EILSEQ;
 
+     if (!arch_check_kprobe(p))
+             return -EILSEQ;
+
      /* copy instruction */
      p->opcode = *p->addr;
 

--
BR,
Liao, Chang

Signature · 7 lines
Best Regards
 Guo Ren

_______________________________________________
linux-riscv mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-riscv
#6 lore raw
Quoted 94 lines · Guo Ren
Quoted 71 lines · liaochang (A)
Quoted 54 lines · 在 2023/1/26 21:05, [email protected] 写道:

From: Guo Ren <[email protected]>

The kernel would panic when probed for an illegal position. eg:

(CONFIG_RISCV_ISA_C=n)

echo 'p:hello kernel_clone+0x16 a0=%a0' >> kprobe_events echo 1 > events/kprobes/hello/enable
cat trace

Kernel panic - not syncing: stack-protector: Kernel stack
is corrupted in: __do_sys_newfstatat+0xb8/0xb8
CPU: 0 PID: 111 Comm: sh Not tainted
6.2.0-rc1-00027-g2d398fe49a4d #490
Hardware name: riscv-virtio,qemu (DT)
Call Trace:
[<ffffffff80007268>] dump_backtrace+0x38/0x48
[<ffffffff80c5e83c>] show_stack+0x50/0x68
[<ffffffff80c6da28>] dump_stack_lvl+0x60/0x84
[<ffffffff80c6da6c>] dump_stack+0x20/0x30
[<ffffffff80c5ecf4>] panic+0x160/0x374
[<ffffffff80c6db94>] generic_handle_arch_irq+0x0/0xa8
[<ffffffff802deeb0>] sys_newstat+0x0/0x30
[<ffffffff800158c0>] sys_clone+0x20/0x30
[<ffffffff800039e8>] ret_from_syscall+0x0/0x4
---[ end Kernel panic - not syncing: stack-protector:
Kernel stack is corrupted in: __do_sys_newfstatat+0xb8/0xb8 ]---

That is because the kprobe's ebreak instruction broke the kernel's original code. The user should guarantee the correction of the probe position, but it couldn't make the kernel panic.

This patch adds arch_check_kprobe in arch_prepare_kprobe to prevent an illegal position (Such as the middle of an instruction).

Fixes c22b0bcb1dd0 ("riscv: Add kprobes supported")
---
 arch/riscv/kernel/probes/kprobes.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
arch/riscv/kernel/probes/kprobes.c +4 -0
index f21592d20306..475989f06d6d 100644
@@ -48,6 +48,21 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
      post_kprobe_handler(p, kcb, regs);
 }
 
+static bool __kprobes arch_check_kprobe(struct kprobe *p)
+{
+     unsigned long tmp  = (unsigned long)p->addr - p->offset;
+     unsigned long addr = (unsigned long)p->addr;

arch/riscv/kernel/probes/kprobes.c static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs) · line 64

Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +10 -0
+
+     while (tmp <= addr) {
+             if (tmp == addr)
+                     return true;
+
+             tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);
+     }
+
+     return false;
+}

LGTM.

Quoted 3 lines

I have submit a patch to fix the same problem, found at:

https://lore.kernel.org/lkml/[email protected]/

I have a question on your OPTKPROBE patch:
https://lore.kernel.org/lkml/[email protected]/

(quoted diff) +8 -1
@@ -490,7 +573,14 @@ int arch_prepare_optimized_kprobe(struct
 optimized_kprobe *op,
  * to detour buffer, ra is used to form JR jumping back from detour
  * buffer.
  */
- find_free_registers(orig, op, &rd, &ra);
+ if (used_reg == ALL_REG_OCCUPIED) {
+ find_free_registers(orig, op, &rd, &ra);
+ } else {
+ /* Choose one unused caller-saved register. */
+ rd = ffz(used_reg);
+ ra = rd;
+ }
+
  if (rd == 0 || ra == 0) {
  ^^^^^^^^^^^^^^^^^^
Quoted 2 lines

After no opt_used_dst_reg & no free caller-saved register (Of cause, it's very rare for no available tmp regs):

Thanks for reviewing.

If this case occurs, it means all 32 integer registers are used as the source in opcode, I have no deep knowledge about the code generation and register allocation of riscv compiler, but guess it is not possible to generate such style of instructions sequence for one really meaningful kernel function.

Quoted 9 lines

Why not try:

     0: REG_S  ra, -SZREG(sp)
     4: auipc  ra, ?
     8: jalr   ?(ra)
    12: REG_L  ra, -SZREG(sp)

Besides taking up more instruction slots, does it have other problems?

I think it okay in some term, two points need to pay attention:
1. It needs to update the 'ra' entry in stack if ra is modified in detour buffer.
2. If sp is modified in detour buffer, #12 might return a illegal value, it is a common case when set kprobe at function prologue and epilogue.

Quoted 26 lines
Quoted 23 lines

So this boundary check is necessary no matter CONFIG_RISCV_ISA_C is enable or not, right?

Quoted 5 lines
arch/riscv/kernel/probes/kprobes.c +1 -0
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
      unsigned long probe_addr = (unsigned long)p->addr;

arch/riscv/kernel/probes/kprobes.c int __kprobes arch_prepare_kprobe(struct kprobe *p) · line 78

Quoted 9 lines
arch/riscv/kernel/probes/kprobes.c +3 -0
@@ -55,6 +70,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
      if (probe_addr & 0x1)
              return -EILSEQ;
 
+     if (!arch_check_kprobe(p))
+             return -EILSEQ;
+
      /* copy instruction */
      p->opcode = *p->addr;
 

--
BR,
Liao, Chang

Signature · 2 lines
BR,
Liao, Chang
#7 lore raw
In reply to [email protected]
Quoted 28 lines · [email protected] writes:

From: Guo Ren <[email protected]>

The kernel would panic when probed for an illegal position. eg:

(CONFIG_RISCV_ISA_C=n)

echo 'p:hello kernel_clone+0x16 a0=%a0' >> kprobe_events echo 1 > events/kprobes/hello/enable
cat trace

Kernel panic - not syncing: stack-protector: Kernel stack
is corrupted in: __do_sys_newfstatat+0xb8/0xb8
CPU: 0 PID: 111 Comm: sh Not tainted
6.2.0-rc1-00027-g2d398fe49a4d #490
Hardware name: riscv-virtio,qemu (DT)
Call Trace:
[<ffffffff80007268>] dump_backtrace+0x38/0x48
[<ffffffff80c5e83c>] show_stack+0x50/0x68
[<ffffffff80c6da28>] dump_stack_lvl+0x60/0x84
[<ffffffff80c6da6c>] dump_stack+0x20/0x30
[<ffffffff80c5ecf4>] panic+0x160/0x374
[<ffffffff80c6db94>] generic_handle_arch_irq+0x0/0xa8
[<ffffffff802deeb0>] sys_newstat+0x0/0x30
[<ffffffff800158c0>] sys_clone+0x20/0x30
[<ffffffff800039e8>] ret_from_syscall+0x0/0x4
---[ end Kernel panic - not syncing: stack-protector:
Kernel stack is corrupted in: __do_sys_newfstatat+0xb8/0xb8 ]---
Quoted 6 lines

That is because the kprobe's ebreak instruction broke the kernel's original code. The user should guarantee the correction of the probe position, but it couldn't make the kernel panic.

This patch adds arch_check_kprobe in arch_prepare_kprobe to prevent an illegal position (Such as the middle of an instruction).

Nice!

@liaochang Will you remove your patch from the OPTPROBE series?

Quoted 15 lines
Fixes c22b0bcb1dd0 ("riscv: Add kprobes supported")
---
 arch/riscv/kernel/probes/kprobes.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
arch/riscv/kernel/probes/kprobes.c +0 -0
index f21592d20306..475989f06d6d 100644
@@ -48,6 +48,21 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
 	post_kprobe_handler(p, kcb, regs);
 }
 

arch/riscv/kernel/probes/kprobes.c static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs) · line 60

Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +10 -0
+static bool __kprobes arch_check_kprobe(struct kprobe *p)
+{
+	unsigned long tmp  = (unsigned long)p->addr - p->offset;
+	unsigned long addr = (unsigned long)p->addr;
+
+	while (tmp <= addr) {
+		if (tmp == addr)
+			return true;
+
+		tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);

kprobe_opcode_t is u32; This can trigger a misaligned load, right?

Quoted 9 lines
arch/riscv/kernel/probes/kprobes.c +5 -0
+	}
+
+	return false;
+}
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
 	unsigned long probe_addr = (unsigned long)p->addr;

arch/riscv/kernel/probes/kprobes.c int __kprobes arch_prepare_kprobe(struct kprobe *p) · line 77

Quoted 8 lines
arch/riscv/kernel/probes/kprobes.c +3 -0
@@ -55,6 +70,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
 	if (probe_addr & 0x1)
 		return -EILSEQ;
 
+	if (!arch_check_kprobe(p))
+		return -EILSEQ;
+
 	/* copy instruction */
 	p->opcode = *p->addr;

Not related to your patch, but this can also trigger a misaligned load.

Björn

author
#8 lore raw
Quoted 82 lines · Björn Töpel
Quoted 28 lines · [email protected] writes:

From: Guo Ren <[email protected]>

The kernel would panic when probed for an illegal position. eg:

(CONFIG_RISCV_ISA_C=n)

echo 'p:hello kernel_clone+0x16 a0=%a0' >> kprobe_events echo 1 > events/kprobes/hello/enable
cat trace

Kernel panic - not syncing: stack-protector: Kernel stack
is corrupted in: __do_sys_newfstatat+0xb8/0xb8
CPU: 0 PID: 111 Comm: sh Not tainted
6.2.0-rc1-00027-g2d398fe49a4d #490
Hardware name: riscv-virtio,qemu (DT)
Call Trace:
[<ffffffff80007268>] dump_backtrace+0x38/0x48
[<ffffffff80c5e83c>] show_stack+0x50/0x68
[<ffffffff80c6da28>] dump_stack_lvl+0x60/0x84
[<ffffffff80c6da6c>] dump_stack+0x20/0x30
[<ffffffff80c5ecf4>] panic+0x160/0x374
[<ffffffff80c6db94>] generic_handle_arch_irq+0x0/0xa8
[<ffffffff802deeb0>] sys_newstat+0x0/0x30
[<ffffffff800158c0>] sys_clone+0x20/0x30
[<ffffffff800039e8>] ret_from_syscall+0x0/0x4
---[ end Kernel panic - not syncing: stack-protector:
Kernel stack is corrupted in: __do_sys_newfstatat+0xb8/0xb8 ]---
Quoted 6 lines

That is because the kprobe's ebreak instruction broke the kernel's original code. The user should guarantee the correction of the probe position, but it couldn't make the kernel panic.

This patch adds arch_check_kprobe in arch_prepare_kprobe to prevent an illegal position (Such as the middle of an instruction).

Nice!

@liaochang Will you remove your patch from the OPTPROBE series?

Quoted 15 lines
Fixes c22b0bcb1dd0 ("riscv: Add kprobes supported")
---
 arch/riscv/kernel/probes/kprobes.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
arch/riscv/kernel/probes/kprobes.c +0 -0
index f21592d20306..475989f06d6d 100644
@@ -48,6 +48,21 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
      post_kprobe_handler(p, kcb, regs);
 }
 

arch/riscv/kernel/probes/kprobes.c static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs) · line 60

Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +10 -0
+static bool __kprobes arch_check_kprobe(struct kprobe *p)
+{
+     unsigned long tmp  = (unsigned long)p->addr - p->offset;
+     unsigned long addr = (unsigned long)p->addr;
+
+     while (tmp <= addr) {
+             if (tmp == addr)
+                     return true;
+
+             tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);

kprobe_opcode_t is u32; This can trigger a misaligned load, right?

Quoted 12 lines
arch/riscv/kernel/probes/kprobes.c +5 -0
+     }
+
+     return false;
+}
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
      unsigned long probe_addr = (unsigned long)p->addr;
@@ -55,6 +70,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
      if (probe_addr & 0x1)
              return -EILSEQ;
 
Quoted 7 lines

arch/riscv/kernel/probes/kprobes.c int __kprobes arch_prepare_kprobe(struct kprobe *p) · line 77

Quoted 5 lines
arch/riscv/kernel/probes/kprobes.c +3 -0
+     if (!arch_check_kprobe(p))
+             return -EILSEQ;
+
      /* copy instruction */
      p->opcode = *p->addr;

Not related to your patch, but this can also trigger a misaligned load.

Yes, it would trigger a misaligned load.

What's the problem of that?

Quoted 3 lines

Björn

Signature · 2 lines
Best Regards
 Guo Ren
author
#9 lore raw
In reply to Björn Töpel
Quoted 82 lines · Björn Töpel
Quoted 28 lines · [email protected] writes:

From: Guo Ren <[email protected]>

The kernel would panic when probed for an illegal position. eg:

(CONFIG_RISCV_ISA_C=n)

echo 'p:hello kernel_clone+0x16 a0=%a0' >> kprobe_events echo 1 > events/kprobes/hello/enable
cat trace

Kernel panic - not syncing: stack-protector: Kernel stack
is corrupted in: __do_sys_newfstatat+0xb8/0xb8
CPU: 0 PID: 111 Comm: sh Not tainted
6.2.0-rc1-00027-g2d398fe49a4d #490
Hardware name: riscv-virtio,qemu (DT)
Call Trace:
[<ffffffff80007268>] dump_backtrace+0x38/0x48
[<ffffffff80c5e83c>] show_stack+0x50/0x68
[<ffffffff80c6da28>] dump_stack_lvl+0x60/0x84
[<ffffffff80c6da6c>] dump_stack+0x20/0x30
[<ffffffff80c5ecf4>] panic+0x160/0x374
[<ffffffff80c6db94>] generic_handle_arch_irq+0x0/0xa8
[<ffffffff802deeb0>] sys_newstat+0x0/0x30
[<ffffffff800158c0>] sys_clone+0x20/0x30
[<ffffffff800039e8>] ret_from_syscall+0x0/0x4
---[ end Kernel panic - not syncing: stack-protector:
Kernel stack is corrupted in: __do_sys_newfstatat+0xb8/0xb8 ]---
Quoted 6 lines

That is because the kprobe's ebreak instruction broke the kernel's original code. The user should guarantee the correction of the probe position, but it couldn't make the kernel panic.

This patch adds arch_check_kprobe in arch_prepare_kprobe to prevent an illegal position (Such as the middle of an instruction).

Nice!

@liaochang Will you remove your patch from the OPTPROBE series?

Quoted 15 lines
Fixes c22b0bcb1dd0 ("riscv: Add kprobes supported")
---
 arch/riscv/kernel/probes/kprobes.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
arch/riscv/kernel/probes/kprobes.c +0 -0
index f21592d20306..475989f06d6d 100644
@@ -48,6 +48,21 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
      post_kprobe_handler(p, kcb, regs);
 }
 

arch/riscv/kernel/probes/kprobes.c static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs) · line 60

Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +10 -0
+static bool __kprobes arch_check_kprobe(struct kprobe *p)
+{
+     unsigned long tmp  = (unsigned long)p->addr - p->offset;
+     unsigned long addr = (unsigned long)p->addr;
+
+     while (tmp <= addr) {
+             if (tmp == addr)
+                     return true;
+
+             tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);

kprobe_opcode_t is u32; This can trigger a misaligned load, right?

Quoted 12 lines
arch/riscv/kernel/probes/kprobes.c +5 -0
+     }
+
+     return false;
+}
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
      unsigned long probe_addr = (unsigned long)p->addr;
@@ -55,6 +70,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
      if (probe_addr & 0x1)
              return -EILSEQ;
 
Quoted 7 lines

arch/riscv/kernel/probes/kprobes.c int __kprobes arch_prepare_kprobe(struct kprobe *p) · line 77

Quoted 5 lines
arch/riscv/kernel/probes/kprobes.c +3 -0
+     if (!arch_check_kprobe(p))
+             return -EILSEQ;
+
      /* copy instruction */
      p->opcode = *p->addr;

Not related to your patch, but this can also trigger a misaligned load.

After rereading the spec, misaligned load/store is not mandatory supported. (Although my machines and qemu are correct)

So I need fixup:

(quoted diff) +0 -0

b/arch/riscv/kernel/probes/kprobes.c
index 27f8960c321c..0c016d496746 100644

arch/riscv/kernel/probes/kprobes.c +4 -2
@@ -58,12 +58,14 @@ static bool __kprobes arch_check_kprobe(struct kprobe *p)
 {
        unsigned long tmp  = (unsigned long)p->addr - p->offset;
        unsigned long addr = (unsigned long)p->addr;
+       kprobe_opcode_t opcode;
 
        while (tmp <= addr) {
                if (tmp == addr)
                        return true;
 
-               tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);
+               memcpy(&opcode, (void *)tmp, sizeof(kprobe_opcode_t));
+               tmp += GET_INSN_LENGTH(opcode);
        }
 
        return false;
@@ -80,7 +82,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
                return -EILSEQ;
 
        /* copy instruction */
-       p->opcode = *p->addr;
+       memcpy(&p->opcode, p->addr, sizeof(kprobe_opcode_t));
 
        /* decode instruction */
        switch (riscv_probe_decode_insn(p->addr, &p->ainsn.api)) {
Quoted 3 lines

Björn

Signature · 2 lines
Best Regards
 Guo Ren
#10 lore raw
Quoted 9 lines · Guo Ren
Quoted 7 lines

Review

Quoted 5 lines
(quoted diff) +3 -0
+     if (!arch_check_kprobe(p))
+             return -EILSEQ;
+
      /* copy instruction */
      p->opcode = *p->addr;

Not related to your patch, but this can also trigger a misaligned load.

After rereading the spec, misaligned load/store is not mandatory supported. (Although my machines and qemu are correct)

Yes. It would be great if it could be turned on by QEMU (you can with Spike).

Quoted 10 lines

So I need fixup:

arch/riscv/kernel/probes/kprobes.c +0 -0
index 27f8960c321c..0c016d496746 100644
@@ -58,12 +58,14 @@ static bool __kprobes arch_check_kprobe(struct kprobe *p)
 {
        unsigned long tmp  = (unsigned long)p->addr - p->offset;

arch/riscv/kernel/probes/kprobes.c static bool __kprobes arch_check_kprobe(struct kprobe *p) · line 68

Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +3 -1
        unsigned long addr = (unsigned long)p->addr;
+       kprobe_opcode_t opcode;
 
        while (tmp <= addr) {
                if (tmp == addr)
                        return true;
 
-               tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);
+               memcpy(&opcode, (void *)tmp, sizeof(kprobe_opcode_t));
+               tmp += GET_INSN_LENGTH(opcode);

I'd prefer sizeof(opcode).

arch/riscv/kernel/probes/kprobes.c int __kprobes arch_prepare_kprobe(struct kprobe *p) · line 85

Quoted 9 lines
arch/riscv/kernel/probes/kprobes.c +1 -1
        }
 
        return false;
@@ -80,7 +82,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
                return -EILSEQ;
 
        /* copy instruction */
-       p->opcode = *p->addr;
+       memcpy(&p->opcode, p->addr, sizeof(kprobe_opcode_t));

Same comment as above, and this would ideally be a separate patch.

Björn

author
#11 lore raw
Quoted 33 lines · Björn Töpel
Quoted 9 lines · Guo Ren
Quoted 7 lines

Review

Quoted 5 lines
(quoted diff) +3 -0
+     if (!arch_check_kprobe(p))
+             return -EILSEQ;
+
      /* copy instruction */
      p->opcode = *p->addr;

Not related to your patch, but this can also trigger a misaligned load.

After rereading the spec, misaligned load/store is not mandatory supported. (Although my machines and qemu are correct)

Yes. It would be great if it could be turned on by QEMU (you can with Spike).

Quoted 17 lines

So I need fixup:

arch/riscv/kernel/probes/kprobes.c +1 -0
index 27f8960c321c..0c016d496746 100644
@@ -58,12 +58,14 @@ static bool __kprobes arch_check_kprobe(struct kprobe *p)
 {
        unsigned long tmp  = (unsigned long)p->addr - p->offset;
        unsigned long addr = (unsigned long)p->addr;
+       kprobe_opcode_t opcode;
 
        while (tmp <= addr) {
                if (tmp == addr)
                        return true;
 
Quoted 5 lines

arch/riscv/kernel/probes/kprobes.c static bool __kprobes arch_check_kprobe(struct kprobe *p) · line 68

Quoted 3 lines
arch/riscv/kernel/probes/kprobes.c +2 -1
-               tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);
+               memcpy(&opcode, (void *)tmp, sizeof(kprobe_opcode_t));
+               tmp += GET_INSN_LENGTH(opcode);

I'd prefer sizeof(opcode).

prefer sizeof(opcode) = 4

GET_INSN_LENGTH(opcode) returns 2 or 4;

Quoted 7 lines
Quoted 7 lines
arch/riscv/kernel/probes/kprobes.c +0 -0
        }
 
        return false;
@@ -80,7 +82,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
                return -EILSEQ;
 
Quoted 5 lines

arch/riscv/kernel/probes/kprobes.c int __kprobes arch_prepare_kprobe(struct kprobe *p) · line 85

Quoted 3 lines
arch/riscv/kernel/probes/kprobes.c +1 -1
        /* copy instruction */
-       p->opcode = *p->addr;
+       memcpy(&p->opcode, p->addr, sizeof(kprobe_opcode_t));

Same comment as above, and this would ideally be a separate patch.

Yes

Quoted 3 lines

Björn

Signature · 2 lines
Best Regards
 Guo Ren
#12 lore raw
Quoted 8 lines · Guo Ren
Quoted 5 lines

arch/riscv/kernel/probes/kprobes.c static bool __kprobes arch_check_kprobe(struct kprobe *p) · line 68

Quoted 3 lines
arch/riscv/kernel/probes/kprobes.c +2 -1
-               tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);
+               memcpy(&opcode, (void *)tmp, sizeof(kprobe_opcode_t));
+               tmp += GET_INSN_LENGTH(opcode);

I'd prefer sizeof(opcode).

prefer sizeof(opcode) = 4

GET_INSN_LENGTH(opcode) returns 2 or 4;

Sorry for being unclear. I was referring to the sizeof in the memcpy.

#13 lore raw
In reply to Björn Töpel
Quoted 34 lines · Björn Töpel
Quoted 32 lines · [email protected] writes:

From: Guo Ren <[email protected]>

The kernel would panic when probed for an illegal position. eg:

(CONFIG_RISCV_ISA_C=n)

echo 'p:hello kernel_clone+0x16 a0=%a0' >> kprobe_events echo 1 > events/kprobes/hello/enable
cat trace

Kernel panic - not syncing: stack-protector: Kernel stack
is corrupted in: __do_sys_newfstatat+0xb8/0xb8
CPU: 0 PID: 111 Comm: sh Not tainted
6.2.0-rc1-00027-g2d398fe49a4d #490
Hardware name: riscv-virtio,qemu (DT)
Call Trace:
[<ffffffff80007268>] dump_backtrace+0x38/0x48
[<ffffffff80c5e83c>] show_stack+0x50/0x68
[<ffffffff80c6da28>] dump_stack_lvl+0x60/0x84
[<ffffffff80c6da6c>] dump_stack+0x20/0x30
[<ffffffff80c5ecf4>] panic+0x160/0x374
[<ffffffff80c6db94>] generic_handle_arch_irq+0x0/0xa8
[<ffffffff802deeb0>] sys_newstat+0x0/0x30
[<ffffffff800158c0>] sys_clone+0x20/0x30
[<ffffffff800039e8>] ret_from_syscall+0x0/0x4
---[ end Kernel panic - not syncing: stack-protector:
Kernel stack is corrupted in: __do_sys_newfstatat+0xb8/0xb8 ]---

That is because the kprobe's ebreak instruction broke the kernel's original code. The user should guarantee the correction of the probe position, but it couldn't make the kernel panic.

Quoted 6 lines
Quoted 2 lines

This patch adds arch_check_kprobe in arch_prepare_kprobe to prevent an illegal position (Such as the middle of an instruction).

Nice!

@liaochang Will you remove your patch from the OPTPROBE series?

Sure, i will remove it.

Quoted 16 lines
Quoted 16 lines
Fixes c22b0bcb1dd0 ("riscv: Add kprobes supported")
---
 arch/riscv/kernel/probes/kprobes.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
arch/riscv/kernel/probes/kprobes.c +0 -0
index f21592d20306..475989f06d6d 100644
@@ -48,6 +48,21 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
 	post_kprobe_handler(p, kcb, regs);
 }
 
Quoted 12 lines

arch/riscv/kernel/probes/kprobes.c static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs) · line 60

Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +10 -0
+static bool __kprobes arch_check_kprobe(struct kprobe *p)
+{
+	unsigned long tmp  = (unsigned long)p->addr - p->offset;
+	unsigned long addr = (unsigned long)p->addr;
+
+	while (tmp <= addr) {
+		if (tmp == addr)
+			return true;
+
+		tmp += GET_INSN_LENGTH(*(kprobe_opcode_t *)tmp);

kprobe_opcode_t is u32; This can trigger a misaligned load, right?

I think it depends on the hardware implementation, event an EEI may guarantee that misaligned loads and stores are fully supported, hardware will requires additional sychronizatin to ensure atomicity, so it is better to use a load instruction whose effective address is naturally aligned.

From "Volum I: RISC-V Unprivileged ISA 2.6":

"..., Loads and stores whose effective address is not naturally aligned to the referenced datatype (i.e, the effective address is not divisible by the size of the access in bytes) have behavior dependenet on the EEI."

Quoted 23 lines
Quoted 10 lines
arch/riscv/kernel/probes/kprobes.c +5 -0
+	}
+
+	return false;
+}
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
 	unsigned long probe_addr = (unsigned long)p->addr;

arch/riscv/kernel/probes/kprobes.c int __kprobes arch_prepare_kprobe(struct kprobe *p) · line 77

Quoted 8 lines
arch/riscv/kernel/probes/kprobes.c +3 -0
@@ -55,6 +70,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
 	if (probe_addr & 0x1)
 		return -EILSEQ;
 
+	if (!arch_check_kprobe(p))
+		return -EILSEQ;
+
 	/* copy instruction */
 	p->opcode = *p->addr;

Not related to your patch, but this can also trigger a misaligned load.

Björn

Signature · 2 lines
BR,
Liao, Chang