perf/hw_breakpoint: Fix breakpoint modify
Subsystems (3)
Jiri Olsa
reviewerv2 cover perf/hw_breakpoint: Fix breakpoint modify
hi,
Milind reported that modify_user_hw_breakpoint wouldn't allow the breakpoint changing if the new attr had 'disabled' set to true.
I found a case where it actualy prevents ptrace user interface to change the breakpoint. It's described in patch 1 as perf test, patch 2 is the breakpoint code fix.
I ran strace tests, nothing (new) broken there..
v2 changes:
- added Oleg's ack for patch 2
- added new changes based on Oleg's questions
plus new test code
thanks,
jirka
---
Jiri Olsa (5):
perf tests: Add breakpoint modify tests
perf/hw_breakpoint: Modify breakpoint even if the new attr has disabled set perf/hw_breakpoint: Remove superfluous bp->attr.disabled = 0 perf/hw_breakpoint: Set breakpoint as disabled in modify_user_hw_breakpoint error path perf/hw_breakpoint: Add fallback code for ptrace_set_breakpoint_addr
arch/x86/kernel/ptrace.c | 5 +++ kernel/events/hw_breakpoint.c | 15 +++++---- tools/perf/arch/x86/include/arch-tests.h | 1 + tools/perf/arch/x86/tests/Build | 1 + tools/perf/arch/x86/tests/arch-tests.c | 6 ++++ tools/perf/arch/x86/tests/bp-modify.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 235 insertions(+), 6 deletions(-) create mode 100644 tools/perf/arch/x86/tests/bp-modify.c
Jiri Olsa
reviewerPATCH v2 1/5 perf tests: Add breakpoint modify tests
Adding to tests that aims on kernel breakpoint
modification bugs.
First test creates HW breakpoint, tries to change
it and checks it was properly changed. It aims
on kernel issue that prevents HW breakpoint to
be changed via ptrace interface.
The first test forks, the child sets itself as ptrace tracee and waits in signal for parent to trace it, then it calls bp_1 and quits.
The parent does following steps:
- creates a new breakpoint (id 0) for bp_2 function
- changes that breakpoint to bp_1 function
- waits for the breakpoint to hit and checks it has proper rip of bp_1 function
This test aims on an issue in kernel preventing to change disabled breakpoints
Second test mimics the first one except for few steps in the parent:
- creates a new breakpoint (id 0) for bp_1 function
- changes that breakpoint to bogus (-1) address
- waits for the breakpoint to hit and checks it has proper rip of bp_1 function
This test aims on an issue in kernel disabling enabled breakpoint after unsuccesful change.
--- tools/perf/arch/x86/include/arch-tests.h | 1 + tools/perf/arch/x86/tests/Build | 1 + tools/perf/arch/x86/tests/arch-tests.c | 6 + tools/perf/arch/x86/tests/bp-modify.c | 213 +++++++++++++++++++++++ 4 files changed, 221 insertions(+) create mode 100644 tools/perf/arch/x86/tests/bp-modify.c
tools/perf/arch/x86/include/arch-tests.h +1 -0
@@ -9,6 +9,7 @@ struct test;int test__rdpmc (struct test *test__maybe_unused ,int subtest);int test__perf_time_to_tsc (struct test *test__maybe_unused ,int subtest);int test__insn_x86 (struct test *test__maybe_unused ,int subtest); +int test__bp_modify (struct test *test,int subtest);#ifdef HAVE_DWARF_UNWIND_SUPPORT struct thread ;
tools/perf/arch/x86/tests/Build +1 -0
@@ -5,3 +5,4 @@ libperf-y += arch-tests.o libperf-y += rdpmc.o libperf-y += perf-time-to-tsc.o libperf-$(CONFIG_AUXTRACE) += insn-x86.o +libperf-$(CONFIG_X86_64) += bp-modify.o
tools/perf/arch/x86/tests/arch-tests.c +6 -0
@@ -23,6 +23,12 @@ struct test arch_tests[] = { .desc = "x86 instruction decoder - new instructions", .func = test__insn_x86, }, +#endif +#if defined(__x86_64__) + { + .desc = "x86 bp modify", + .func = test__bp_modify, + }, #endif { .func = NULL,
tools/perf/arch/x86/tests/bp-modify.c new file +213 -0
213 lines not on this page · raw message
Signature · 1 line
2.17.1
Jiri Olsa
reviewerPATCH v2 2/5 perf/hw_breakpoint: Modify breakpoint even if the new attr has disabled set
In reply to Jiri OlsaWe need to change the breakpoint even if the attr with new fields has disabled set to true.
Current code prevents following user code to change the breakpoint address:
ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[0]), addr_1) ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[0]), addr_2) ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[7]), dr7)
The first PTRACE_POKEUSER creates the breakpoint with attr.disabled set to true:
ptrace_set_breakpoint_addr(nr = 0)
struct perf_event *bp = t->ptrace_bps[nr]; ptrace_register_breakpoint(..., disabled = true)
ptrace_fill_bp_fields(..., disabled) register_user_hw_breakpoint
So the second PTRACE_POKEUSER will be omitted:
ptrace_set_breakpoint_addr(nr = 0)
struct perf_event *bp = t->ptrace_bps[nr];
struct perf_event_attr attr = bp->attr; modify_user_hw_breakpoint(bp, &attr)
if (!attr->disabled)
modify_user_hw_breakpoint_check--- kernel/events/hw_breakpoint.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
kernel/events/hw_breakpoint.c +6 -4
@@ -509,6 +509,8 @@ modify_user_hw_breakpoint_check(struct perf_event *bp, struct perf_event_attr *a*/ int modify_user_hw_breakpoint (struct perf_event *bp,struct perf_event_attr *attr) { +int err; +/* * modify_user_hw_breakpoint can be invoked with IRQs disabled and hence it * will not be possible to raise IPIs that invoke __perf_event_disable.
@@ -520,11 +522,11 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attelse perf_event_disable (bp); -if (!attr->disabled) { -int err =modify_user_hw_breakpoint_check (bp, attr, false); + err =modify_user_hw_breakpoint_check (bp, attr, false); +if (err) +return err; -if (err) -return err; +if (!attr->disabled) {perf_event_enable (bp); bp->attr.disabled =0 ; }
Signature · 1 line
2.17.1
Jiri Olsa
reviewerPATCH v2 3/5 perf/hw_breakpoint: Remove superfluous bp->attr.disabled = 0
In reply to Jiri OlsaOnce the breakpoint was succesfully modified, the attr->disabled value is in bp->attr.disabled. So there's no reason to set it again, removing that.
--- kernel/events/hw_breakpoint.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
kernel/events/hw_breakpoint.c +2 -3
@@ -526,10 +526,9 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attif (err)return err; -if (!attr->disabled) { +if (!attr->disabled)perf_event_enable (bp); - bp->attr.disabled =0 ; - } +return 0 ; }EXPORT_SYMBOL_GPL (modify_user_hw_breakpoint);
Signature · 1 line
2.17.1
Re: [PATCH 3/5] perf/hw_breakpoint: Remove superfluous bp->attr.disabled = 0
kernel/events/hw_breakpoint.c int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att · line 532
Quoted 10 lines · Jiri Olsa
kernel/events/hw_breakpoint.c +1
-3@@ -526,10 +526,9 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attif (err)return err; -if (!attr->disabled) { +if (!attr->disabled)perf_event_enable (bp); - bp->attr.disabled =0 ; - }
Jiri Olsa
reviewerPATCH v2 4/5 perf/hw_breakpoint: Set breakpoint as disabled in modify_user_hw_breakpoint error path
In reply to Jiri OlsaWhen we failed to modify breakpoint, we ended up with disabled event. Mirror that in its 'attr.disabled', because some parts of the ptrace code depends on it.
--- kernel/events/hw_breakpoint.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
kernel/events/hw_breakpoint.c +3 -1
@@ -523,8 +523,10 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attperf_event_disable (bp); err =modify_user_hw_breakpoint_check (bp, attr, false); -if (err) +if (err) { + bp->attr.disabled =1 ;return err; + }if (!attr->disabled)perf_event_enable (bp);
Signature · 1 line
2.17.1
Oleg Nesterov
maintainerRe: [PATCH 4/5] perf/hw_breakpoint: Set breakpoint as disabled in modify_user_hw_breakpoint error path
kernel/events/hw_breakpoint.c int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att · line 528
Quoted 9 lines · Jiri Olsa
kernel/events/hw_breakpoint.c +2
-1@@ -523,8 +523,10 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attperf_event_disable (bp); err =modify_user_hw_breakpoint_check (bp, attr, false); -if (err) +if (err) { + bp->attr.disabled =1 ;return err;
Yes, but on the second thought... Can't we simply do
int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr)
{
int err;
/*
* modify_user_hw_breakpoint can be invoked with IRQs disabled and hence it
* will not be possible to raise IPIs that invoke __perf_event_disable.
* So call the function directly after making sure we are targeting the
* current task.
*/
if (irqs_disabled() && bp->ctx && bp->ctx->task == current)
perf_event_disable_local(bp);
else
perf_event_disable(bp);
err = modify_user_hw_breakpoint_check(bp, attr, false);
if (!bp.attr->disabled)
perf_event_enable(bp);
return err;
}instead of this and the next patch?
We can do this because (as you pointed out) validate_hw_breakpoint() has already gone in -tip tree, and (afaics) modify_user_hw_breakpoint_check() never changes perf_event's state on failure, so we can safely "restart" this bp if it was enabled before.
1. This is what we had before the recent f67b15037a7a50c57f72e69a6d59941ad90a0f0f "perf/hwbp: Simplify the perf-hwbp code, fix documentation".
Note that this commit was actually the bug fix which introduced another problem fixed by your 2/5.
But see above, perf_event_enable() is no longer unsafe after modify_user_hw_breakpoint_check(), we can restore the previous semantics.
2. This matches perf_event_modify_breakpoint(). Which btw can be simplified a bit, it too can simply do
if (!bp->attr.disabled) _perf_event_enable(bp); return err;
Oleg.
Re: [PATCH 4/5] perf/hw_breakpoint: Set breakpoint as disabled in modify_user_hw_breakpoint error path
Quoted 52 lines · Oleg Nesterov
kernel/events/hw_breakpoint.c int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att · line 528
Quoted 9 lines · Jiri Olsa
kernel/events/hw_breakpoint.c +2
-1@@ -523,8 +523,10 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attperf_event_disable (bp); err =modify_user_hw_breakpoint_check (bp, attr, false); -if (err) +if (err) { + bp->attr.disabled =1 ;return err;Yes, but on the second thought... Can't we simply do
int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr) { int err; /* * modify_user_hw_breakpoint can be invoked with IRQs disabled and hence it * will not be possible to raise IPIs that invoke __perf_event_disable. * So call the function directly after making sure we are targeting the * current task. */ if (irqs_disabled() && bp->ctx && bp->ctx->task == current) perf_event_disable_local(bp); else perf_event_disable(bp); err = modify_user_hw_breakpoint_check(bp, attr, false); if (!bp.attr->disabled) perf_event_enable(bp); return err; }instead of this and the next patch?
We can do this because (as you pointed out)
validate_hw_breakpoint()has already gone in -tip tree, and (afaics)modify_user_hw_breakpoint_check()never changes perf_event's state on failure, so we can safely "restart" this bp if it was enabled before.1. This is what we had before the recent
f67b15037a7a50c57f72e69a6d59941ad90a0f0f"perf/hwbp: Simplify the perf-hwbp code, fix documentation".Note that this commit was actually the bug fix which introduced another problem fixed by your 2/5.
But see above,
perf_event_enable()is no longer unsafe aftermodify_user_hw_breakpoint_check(), we can restore the previous semantics.
Quoted 7 lines
2. This matches
perf_event_modify_breakpoint(). Which btw can be simplified a bit, it too can simply doif (!bp->attr.disabled) _perf_event_enable(bp); return err;
yep, seems good.. will send new version
thanks,
jirka
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 4/5] perf/hw_breakpoint: Set breakpoint as disabled in modify_user_hw_breakpoint error path
Em Thu, Aug 09, 2018 at 06:30:15PM +0200, Jiri Olsa escreveu:
Quoted 8 lines · Oleg Nesterov
Quoted 7 lines · Jiri Olsa
kernel/events/hw_breakpoint.c int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att · line 528
Quoted 4 lines
kernel/events/hw_breakpoint.c +2
-1-if (err) +if (err) { + bp->attr.disabled =1 ;return err;Yes, but on the second thought... Can't we simply do
<SNIP>
Quoted 1 line
Quoted 1 line
instead of this and the next patch?
<SNIP>
Quoted 1 line
yep, seems good.. will send new version
Ok, but this is just for 4/5 and 5/5, right?
- Arnaldo
Re: [PATCH 4/5] perf/hw_breakpoint: Set breakpoint as disabled in modify_user_hw_breakpoint error path
Quoted 12 lines · Arnaldo Carvalho de Melo
Em Thu, Aug 09, 2018 at 06:30:15PM +0200, Jiri Olsa escreveu:
Quoted 8 lines · Oleg Nesterov
Quoted 7 lines · Jiri Olsa
kernel/events/hw_breakpoint.c int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att · line 528
Quoted 4 lines
kernel/events/hw_breakpoint.c +2
-1-if (err) +if (err) { + bp->attr.disabled =1 ;return err;Yes, but on the second thought... Can't we simply do
<SNIP>
Quoted 7 lines
Quoted 1 line
Quoted 1 line
instead of this and the next patch?
<SNIP>
Quoted 1 line
yep, seems good.. will send new version
Ok, but this is just for 4/5 and 5/5, right?
I resent v4 yesterday with final changes/acks, in here:
7186 r Aug 27 Jiri Olsa (1.6K) [PATCHv4 0/5] perf/hw_breakpoint: Fix breakpoint modify
thanks,
jirka
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 4/5] perf/hw_breakpoint: Set breakpoint as disabled in modify_user_hw_breakpoint error path
Em Tue, Aug 28, 2018 at 04:38:23PM +0200, Jiri Olsa escreveu:
Quoted 17 lines · Arnaldo Carvalho de Melo
Quoted 16 lines
Em Thu, Aug 09, 2018 at 06:30:15PM +0200, Jiri Olsa escreveu:
Quoted 8 lines · Oleg Nesterov
Quoted 7 lines · Jiri Olsa
kernel/events/hw_breakpoint.c int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att · line 528
Quoted 4 lines
kernel/events/hw_breakpoint.c +2
-1-if (err) +if (err) { + bp->attr.disabled =1 ;return err;Yes, but on the second thought... Can't we simply do
<SNIP>
Quoted 1 line
Quoted 1 line
instead of this and the next patch?
<SNIP>
Quoted 7 lines
Quoted 3 lines
Quoted 1 line
yep, seems good.. will send new version
Ok, but this is just for 4/5 and 5/5, right?
I resent v4 yesterday with final changes/acks, in here:
7186 r Aug 27 Jiri Olsa (1.6K) [PATCHv4 0/5] perf/hw_breakpoint: Fix breakpoint modify
Ok, will search that, sorry for the noise :-)
- Arnaldo
Jiri Olsa
reviewerPATCH v2 5/5 perf/hw_breakpoint: Add fallback code for ptrace_set_breakpoint_addr
In reply to Jiri OlsaRestoring the breakpoint after unsuccesfull address change, so following user code no longer produces disabled breakpoint.
ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[0]), addr_1) ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[7]), dr7) ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[0]), -1)
The first 2 ptrace calls set breakpoint on addr_1. The 3rd ptrace call tries to set it to bogus address (-1). This would normaly end up with disabled breakpoint. This patch adds the code that restores the breakpoint to its original state.
--- arch/x86/kernel/ptrace.c | 5 +++++ 1 file changed, 5 insertions(+)
arch/x86/kernel/ptrace.c +5 -0
@@ -693,9 +693,14 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, t->ptrace_bps[nr] = bp; }else {struct perf_event_attr attr = bp->attr; +bool disabled = attr.disabled; attr.bp_addr = addr; err =modify_user_hw_breakpoint (bp, &attr); +if (err && !disabled) { + bp->attr.disabled = false; +WARN_ON (modify_user_hw_breakpoint (bp, &bp->attr)); + } }return err;
Signature · 1 line
2.17.1
Arnaldo Carvalho de Melo
maintainerEm Thu, Aug 09, 2018 at 02:03:00PM +0200, Jiri Olsa escreveu:
Quoted 8 lines
hi,
Milind reported thatmodify_user_hw_breakpointwouldn't allow the breakpoint changing if the new attr had 'disabled' set to true.I found a case where it actualy prevents ptrace user interface to change the breakpoint. It's described in patch 1 as perf test, patch 2 is the breakpoint code fix.
Going over these patches now, applying the test, running it, then the kernel bits, reboot, retest, etc.
- Arnaldo