diff mbox series

[RFC] powerpc/uprobes: Fixup si_addr if we took an exception while single stepping

Message ID 20170913200330.15606-1-naveen.n.rao@linux.vnet.ibm.com (mailing list archive)
State RFC
Headers show
Series [RFC] powerpc/uprobes: Fixup si_addr if we took an exception while single stepping | expand

Commit Message

Naveen N. Rao Sept. 13, 2017, 8:03 p.m. UTC
If the single-stepped instruction causes an exception, we may end up
setting siginfo.si_addr to the address of the uprobe xol area. This is
not desirable since the address won't make sense for the process if it
wants to handle the exception. Fixup the si_addr field in such cases.

Reported-by: Anton Blanchard <anton@samba.org>
Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/uprobes.h |  7 +++++++
 arch/powerpc/kernel/traps.c        |  4 ++++
 arch/powerpc/kernel/uprobes.c      | 17 +++++++++++++++++
 3 files changed, 28 insertions(+)

Comments

Christophe Leroy March 11, 2022, 4:49 p.m. UTC | #1
Le 13/09/2017 à 22:03, Naveen N. Rao a écrit :
> If the single-stepped instruction causes an exception, we may end up
> setting siginfo.si_addr to the address of the uprobe xol area. This is
> not desirable since the address won't make sense for the process if it
> wants to handle the exception. Fixup the si_addr field in such cases.
> 
> Reported-by: Anton Blanchard <anton@samba.org>
> Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>


Nowadays _exception() calls force_sig_fault_to_task() so if the change 
is still required I guess it must be done there ?

Christophe


> ---
>   arch/powerpc/include/asm/uprobes.h |  7 +++++++
>   arch/powerpc/kernel/traps.c        |  4 ++++
>   arch/powerpc/kernel/uprobes.c      | 17 +++++++++++++++++
>   3 files changed, 28 insertions(+)
> 
> diff --git a/arch/powerpc/include/asm/uprobes.h b/arch/powerpc/include/asm/uprobes.h
> index 7422a999a39a..13fc6af3c1fd 100644
> --- a/arch/powerpc/include/asm/uprobes.h
> +++ b/arch/powerpc/include/asm/uprobes.h
> @@ -23,6 +23,7 @@
>    */
>   
>   #include <linux/notifier.h>
> +#include <asm/siginfo.h>
>   #include <asm/probes.h>
>   
>   typedef ppc_opcode_t uprobe_opcode_t;
> @@ -45,4 +46,10 @@ struct arch_uprobe_task {
>   	unsigned long	saved_trap_nr;
>   };
>   
> +#ifdef CONFIG_UPROBES
> +extern void uprobe_fixup_exception(struct pt_regs *regs, siginfo_t *info);
> +#else
> +static inline void uprobe_fixup_exception(struct pt_regs *regs, siginfo_t *info) { }
> +#endif
> +
>   #endif	/* _ASM_UPROBES_H */
> diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
> index ec74e203ee04..1bb858a37029 100644
> --- a/arch/powerpc/kernel/traps.c
> +++ b/arch/powerpc/kernel/traps.c
> @@ -66,6 +66,7 @@
>   #include <asm/hmi.h>
>   #include <sysdev/fsl_pci.h>
>   #include <asm/kprobes.h>
> +#include <asm/uprobes.h>
>   
>   #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC_CORE)
>   int (*__debugger)(struct pt_regs *regs) __read_mostly;
> @@ -292,6 +293,9 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
>   	info.si_signo = signr;
>   	info.si_code = code;
>   	info.si_addr = (void __user *) addr;
> +
> +	uprobe_fixup_exception(regs, &info);
> +
>   	force_sig_info(signr, &info, current);
>   }
>   
> diff --git a/arch/powerpc/kernel/uprobes.c b/arch/powerpc/kernel/uprobes.c
> index 5d105b8eeece..a361a56e6210 100644
> --- a/arch/powerpc/kernel/uprobes.c
> +++ b/arch/powerpc/kernel/uprobes.c
> @@ -25,6 +25,7 @@
>   #include <linux/uprobes.h>
>   #include <linux/uaccess.h>
>   #include <linux/kdebug.h>
> +#include <linux/signal.h>
>   
>   #include <asm/sstep.h>
>   
> @@ -214,3 +215,19 @@ bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx,
>   	else
>   		return regs->gpr[1] < ret->stack;
>   }
> +
> +void uprobe_fixup_exception(struct pt_regs *regs, siginfo_t *info)
> +{
> +	struct task_struct *t = current;
> +	struct uprobe_task *utask = t->utask;
> +
> +	if (likely(!utask || !utask->active_uprobe))
> +		return;
> +
> +	/*
> +	 * We reset si_addr here.
> +	 * regs->nip is reset during our way back through uprobe_deny_signal()
> +	 */
> +	if (info->si_addr == (void __user *) utask->xol_vaddr)
> +		info->si_addr = (void __user *) utask->vaddr;
> +}
diff mbox series

Patch

diff --git a/arch/powerpc/include/asm/uprobes.h b/arch/powerpc/include/asm/uprobes.h
index 7422a999a39a..13fc6af3c1fd 100644
--- a/arch/powerpc/include/asm/uprobes.h
+++ b/arch/powerpc/include/asm/uprobes.h
@@ -23,6 +23,7 @@ 
  */
 
 #include <linux/notifier.h>
+#include <asm/siginfo.h>
 #include <asm/probes.h>
 
 typedef ppc_opcode_t uprobe_opcode_t;
@@ -45,4 +46,10 @@  struct arch_uprobe_task {
 	unsigned long	saved_trap_nr;
 };
 
+#ifdef CONFIG_UPROBES
+extern void uprobe_fixup_exception(struct pt_regs *regs, siginfo_t *info);
+#else
+static inline void uprobe_fixup_exception(struct pt_regs *regs, siginfo_t *info) { }
+#endif
+
 #endif	/* _ASM_UPROBES_H */
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index ec74e203ee04..1bb858a37029 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -66,6 +66,7 @@ 
 #include <asm/hmi.h>
 #include <sysdev/fsl_pci.h>
 #include <asm/kprobes.h>
+#include <asm/uprobes.h>
 
 #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC_CORE)
 int (*__debugger)(struct pt_regs *regs) __read_mostly;
@@ -292,6 +293,9 @@  void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
 	info.si_signo = signr;
 	info.si_code = code;
 	info.si_addr = (void __user *) addr;
+
+	uprobe_fixup_exception(regs, &info);
+
 	force_sig_info(signr, &info, current);
 }
 
diff --git a/arch/powerpc/kernel/uprobes.c b/arch/powerpc/kernel/uprobes.c
index 5d105b8eeece..a361a56e6210 100644
--- a/arch/powerpc/kernel/uprobes.c
+++ b/arch/powerpc/kernel/uprobes.c
@@ -25,6 +25,7 @@ 
 #include <linux/uprobes.h>
 #include <linux/uaccess.h>
 #include <linux/kdebug.h>
+#include <linux/signal.h>
 
 #include <asm/sstep.h>
 
@@ -214,3 +215,19 @@  bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx,
 	else
 		return regs->gpr[1] < ret->stack;
 }
+
+void uprobe_fixup_exception(struct pt_regs *regs, siginfo_t *info)
+{
+	struct task_struct *t = current;
+	struct uprobe_task *utask = t->utask;
+
+	if (likely(!utask || !utask->active_uprobe))
+		return;
+
+	/*
+	 * We reset si_addr here.
+	 * regs->nip is reset during our way back through uprobe_deny_signal()
+	 */
+	if (info->si_addr == (void __user *) utask->xol_vaddr)
+		info->si_addr = (void __user *) utask->vaddr;
+}