2019-09-10 12:32:28

by Ravi Bangoria

[permalink] [raw]
Subject: [PATCH 0/2] powerpc/watchpoint: Disable watchpoint hit by larx/stcx instruction

I've prepared my patch on top of Christophe's patch as it's easy
to change stepping_handler() rather than hw_breakpoint_handler().
2nd patch is the actual fix.

Christophe Leroy (1):
powerpc/hw_breakpoint: move instruction stepping out of
hw_breakpoint_handler()

Ravi Bangoria (1):
powerpc/watchpoint: Disable watchpoint hit by larx/stcx instructions

arch/powerpc/kernel/hw_breakpoint.c | 77 +++++++++++++++++++----------
1 file changed, 50 insertions(+), 27 deletions(-)

--
2.21.0


2019-09-10 12:38:53

by Christophe Leroy

[permalink] [raw]
Subject: Re: [PATCH 0/2] powerpc/watchpoint: Disable watchpoint hit by larx/stcx instruction



Le 10/09/2019 à 12:24, Ravi Bangoria a écrit :
> I've prepared my patch on top of Christophe's patch as it's easy
> to change stepping_handler() rather than hw_breakpoint_handler().
> 2nd patch is the actual fix.

Anyway, my patch is already commited on powerpc/next

https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/?h=next-20190904&id=658d029df0bce6472c94b724ca54d74bc6659c2e

Christophe

>
> Christophe Leroy (1):
> powerpc/hw_breakpoint: move instruction stepping out of
> hw_breakpoint_handler()
>
> Ravi Bangoria (1):
> powerpc/watchpoint: Disable watchpoint hit by larx/stcx instructions
>
> arch/powerpc/kernel/hw_breakpoint.c | 77 +++++++++++++++++++----------
> 1 file changed, 50 insertions(+), 27 deletions(-)
>

2019-09-10 18:55:07

by Ravi Bangoria

[permalink] [raw]
Subject: [PATCH 2/2] powerpc/watchpoint: Disable watchpoint hit by larx/stcx instructions

If watchpoint exception is generated by larx/stcx instructions, the
reservation created by larx gets lost while handling exception, and
thus stcx instruction always fails. Generally these instructions are
used in a while(1) loop, for example spinlocks. And because stcx
never succeeds, it loops forever and ultimately hangs the system.

Note that ptrace anyway works in one-shot mode and thus for ptrace
we don't change the behaviour. It's up to ptrace user to take care
of this.

Signed-off-by: Ravi Bangoria <[email protected]>
---
arch/powerpc/kernel/hw_breakpoint.c | 49 +++++++++++++++++++----------
1 file changed, 33 insertions(+), 16 deletions(-)

diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c
index 28ad3171bb82..9fa496a598ce 100644
--- a/arch/powerpc/kernel/hw_breakpoint.c
+++ b/arch/powerpc/kernel/hw_breakpoint.c
@@ -195,14 +195,32 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs)
tsk->thread.last_hit_ubp = NULL;
}

+static bool is_larx_stcx_instr(struct pt_regs *regs, unsigned int instr)
+{
+ int ret, type;
+ struct instruction_op op;
+
+ ret = analyse_instr(&op, regs, instr);
+ type = GETTYPE(op.type);
+ return (!ret && (type == LARX || type == STCX));
+}
+
/*
* Handle debug exception notifications.
*/
static bool stepping_handler(struct pt_regs *regs, struct perf_event *bp,
unsigned long addr)
{
- int stepped;
- unsigned int instr;
+ unsigned int instr = 0;
+
+ if (__get_user_inatomic(instr, (unsigned int *)regs->nip))
+ goto fail;
+
+ if (is_larx_stcx_instr(regs, instr)) {
+ printk_ratelimited("Watchpoint: Can't emulate/single-step larx/"
+ "stcx instructions. Disabling watchpoint.\n");
+ goto disable;
+ }

/* Do not emulate user-space instructions, instead single-step them */
if (user_mode(regs)) {
@@ -211,23 +229,22 @@ static bool stepping_handler(struct pt_regs *regs, struct perf_event *bp,
return false;
}

- stepped = 0;
- instr = 0;
- if (!__get_user_inatomic(instr, (unsigned int *)regs->nip))
- stepped = emulate_step(regs, instr);
+ if (!emulate_step(regs, instr))
+ goto fail;

+ return true;
+
+fail:
/*
- * emulate_step() could not execute it. We've failed in reliably
- * handling the hw-breakpoint. Unregister it and throw a warning
- * message to let the user know about it.
+ * We've failed in reliably handling the hw-breakpoint. Unregister
+ * it and throw a warning message to let the user know about it.
*/
- if (!stepped) {
- WARN(1, "Unable to handle hardware breakpoint. Breakpoint at "
- "0x%lx will be disabled.", addr);
- perf_event_disable_inatomic(bp);
- return false;
- }
- return true;
+ WARN(1, "Unable to handle hardware breakpoint. Breakpoint at "
+ "0x%lx will be disabled.", addr);
+
+disable:
+ perf_event_disable_inatomic(bp);
+ return false;
}

int hw_breakpoint_handler(struct die_args *args)
--
2.21.0

2019-09-10 18:55:08

by Ravi Bangoria

[permalink] [raw]
Subject: [PATCH 1/2] powerpc/hw_breakpoint: move instruction stepping out of hw_breakpoint_handler()

From: Christophe Leroy <[email protected]>

On 8xx, breakpoints stop after executing the instruction, so
stepping/emulation is not needed. Move it into a sub-function and
remove the #ifdefs.

Signed-off-by: Christophe Leroy <[email protected]>
Reviewed-by: Ravi Bangoria <[email protected]>
---
arch/powerpc/kernel/hw_breakpoint.c | 60 ++++++++++++++++-------------
1 file changed, 33 insertions(+), 27 deletions(-)

diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c
index c8d1fa2e9d53..28ad3171bb82 100644
--- a/arch/powerpc/kernel/hw_breakpoint.c
+++ b/arch/powerpc/kernel/hw_breakpoint.c
@@ -198,15 +198,43 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs)
/*
* Handle debug exception notifications.
*/
+static bool stepping_handler(struct pt_regs *regs, struct perf_event *bp,
+ unsigned long addr)
+{
+ int stepped;
+ unsigned int instr;
+
+ /* Do not emulate user-space instructions, instead single-step them */
+ if (user_mode(regs)) {
+ current->thread.last_hit_ubp = bp;
+ regs->msr |= MSR_SE;
+ return false;
+ }
+
+ stepped = 0;
+ instr = 0;
+ if (!__get_user_inatomic(instr, (unsigned int *)regs->nip))
+ stepped = emulate_step(regs, instr);
+
+ /*
+ * emulate_step() could not execute it. We've failed in reliably
+ * handling the hw-breakpoint. Unregister it and throw a warning
+ * message to let the user know about it.
+ */
+ if (!stepped) {
+ WARN(1, "Unable to handle hardware breakpoint. Breakpoint at "
+ "0x%lx will be disabled.", addr);
+ perf_event_disable_inatomic(bp);
+ return false;
+ }
+ return true;
+}
+
int hw_breakpoint_handler(struct die_args *args)
{
int rc = NOTIFY_STOP;
struct perf_event *bp;
struct pt_regs *regs = args->regs;
-#ifndef CONFIG_PPC_8xx
- int stepped = 1;
- unsigned int instr;
-#endif
struct arch_hw_breakpoint *info;
unsigned long dar = regs->dar;

@@ -251,31 +279,9 @@ int hw_breakpoint_handler(struct die_args *args)
(dar - bp->attr.bp_addr < bp->attr.bp_len)))
info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;

-#ifndef CONFIG_PPC_8xx
- /* Do not emulate user-space instructions, instead single-step them */
- if (user_mode(regs)) {
- current->thread.last_hit_ubp = bp;
- regs->msr |= MSR_SE;
+ if (!IS_ENABLED(CONFIG_PPC_8xx) && !stepping_handler(regs, bp, info->address))
goto out;
- }
-
- stepped = 0;
- instr = 0;
- if (!__get_user_inatomic(instr, (unsigned int *) regs->nip))
- stepped = emulate_step(regs, instr);

- /*
- * emulate_step() could not execute it. We've failed in reliably
- * handling the hw-breakpoint. Unregister it and throw a warning
- * message to let the user know about it.
- */
- if (!stepped) {
- WARN(1, "Unable to handle hardware breakpoint. Breakpoint at "
- "0x%lx will be disabled.", info->address);
- perf_event_disable_inatomic(bp);
- goto out;
- }
-#endif
/*
* As a policy, the callback is invoked in a 'trigger-after-execute'
* fashion
--
2.21.0

2019-09-10 18:55:54

by Naveen N. Rao

[permalink] [raw]
Subject: Re: [PATCH 2/2] powerpc/watchpoint: Disable watchpoint hit by larx/stcx instructions

Ravi Bangoria wrote:
> If watchpoint exception is generated by larx/stcx instructions, the
> reservation created by larx gets lost while handling exception, and
> thus stcx instruction always fails. Generally these instructions are
> used in a while(1) loop, for example spinlocks. And because stcx
> never succeeds, it loops forever and ultimately hangs the system.
>
> Note that ptrace anyway works in one-shot mode and thus for ptrace
> we don't change the behaviour. It's up to ptrace user to take care
> of this.
>
> Signed-off-by: Ravi Bangoria <[email protected]>
> ---
> arch/powerpc/kernel/hw_breakpoint.c | 49 +++++++++++++++++++----------
> 1 file changed, 33 insertions(+), 16 deletions(-)
>
> diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c
> index 28ad3171bb82..9fa496a598ce 100644
> --- a/arch/powerpc/kernel/hw_breakpoint.c
> +++ b/arch/powerpc/kernel/hw_breakpoint.c
> @@ -195,14 +195,32 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs)
> tsk->thread.last_hit_ubp = NULL;
> }
>
> +static bool is_larx_stcx_instr(struct pt_regs *regs, unsigned int instr)
> +{
> + int ret, type;
> + struct instruction_op op;
> +
> + ret = analyse_instr(&op, regs, instr);
> + type = GETTYPE(op.type);
> + return (!ret && (type == LARX || type == STCX));
> +}
> +
> /*
> * Handle debug exception notifications.
> */
> static bool stepping_handler(struct pt_regs *regs, struct perf_event *bp,
> unsigned long addr)
> {
> - int stepped;
> - unsigned int instr;
> + unsigned int instr = 0;
> +
> + if (__get_user_inatomic(instr, (unsigned int *)regs->nip))
> + goto fail;
> +
> + if (is_larx_stcx_instr(regs, instr)) {
> + printk_ratelimited("Watchpoint: Can't emulate/single-step larx/"
> + "stcx instructions. Disabling watchpoint.\n");

The below WARN() uses the term 'breakpoint'. Better to use consistent
terminology. I would rewrite the above as:
printk_ratelimited("Breakpoint hit on instruction that can't be emulated. "
"Breakpoint at 0x%lx will be disabled.\n", addr);

Otherwise:
Acked-by: Naveen N. Rao <[email protected]>

- Naveen

> + goto disable;
> + }
>
> /* Do not emulate user-space instructions, instead single-step them */
> if (user_mode(regs)) {
> @@ -211,23 +229,22 @@ static bool stepping_handler(struct pt_regs *regs, struct perf_event *bp,
> return false;
> }
>
> - stepped = 0;
> - instr = 0;
> - if (!__get_user_inatomic(instr, (unsigned int *)regs->nip))
> - stepped = emulate_step(regs, instr);
> + if (!emulate_step(regs, instr))
> + goto fail;
>
> + return true;
> +
> +fail:
> /*
> - * emulate_step() could not execute it. We've failed in reliably
> - * handling the hw-breakpoint. Unregister it and throw a warning
> - * message to let the user know about it.
> + * We've failed in reliably handling the hw-breakpoint. Unregister
> + * it and throw a warning message to let the user know about it.
> */
> - if (!stepped) {
> - WARN(1, "Unable to handle hardware breakpoint. Breakpoint at "
> - "0x%lx will be disabled.", addr);
> - perf_event_disable_inatomic(bp);
> - return false;
> - }
> - return true;
> + WARN(1, "Unable to handle hardware breakpoint. Breakpoint at "
> + "0x%lx will be disabled.", addr);
> +
> +disable:
> + perf_event_disable_inatomic(bp);
> + return false;
> }
>
> int hw_breakpoint_handler(struct die_args *args)
> --
> 2.21.0
>
>