The testing show that perf_ftrace_function_call() are using
smp_processor_id() with preemption enabled, all the checking
on CPU could be wrong after preemption, PATCH 1/2 will fix
that.
Besides, as Peter point out, the testing of recursion within
the section between ftrace_test_recursion_trylock()/_unlock()
pair also need the preemption disabled as the documentation
explained, PATCH 2/2 will make sure on that.
Michael Wang (2):
ftrace: disable preemption on the testing of recursion
ftrace: prevent preemption in perf_ftrace_function_call()
arch/csky/kernel/probes/ftrace.c | 2 --
arch/parisc/kernel/ftrace.c | 2 --
arch/powerpc/kernel/kprobes-ftrace.c | 2 --
arch/riscv/kernel/probes/ftrace.c | 2 --
arch/x86/kernel/kprobes/ftrace.c | 2 --
include/linux/trace_recursion.h | 10 +++++++++-
kernel/livepatch/patch.c | 6 ------
kernel/trace/trace_event_perf.c | 17 +++++++++++++----
kernel/trace/trace_functions.c | 5 -----
9 files changed, 22 insertions(+), 26 deletions(-)
--
1.8.3.1
As the documentation explained, ftrace_test_recursion_trylock()
and ftrace_test_recursion_unlock() were supposed to disable and
enable preemption properly, however currently this work is done
outside of the function, which could be missing by mistake.
This path will make sure the preemption will be disabled when
trylock() succeed, and the unlock() will enable preemption when
the the testing of recursion are finished.
Reported-by: Abaci <redacted>
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Michael Wang <redacted>
---
arch/csky/kernel/probes/ftrace.c | 2 --
arch/parisc/kernel/ftrace.c | 2 --
arch/powerpc/kernel/kprobes-ftrace.c | 2 --
arch/riscv/kernel/probes/ftrace.c | 2 --
arch/x86/kernel/kprobes/ftrace.c | 2 --
include/linux/trace_recursion.h | 10 +++++++++-
kernel/livepatch/patch.c | 6 ------
kernel/trace/trace_functions.c | 5 -----
8 files changed, 9 insertions(+), 22 deletions(-)
With CONFIG_DEBUG_PREEMPT we observed reports like:
BUG: using smp_processor_id() in preemptible
caller is perf_ftrace_function_call+0x6f/0x2e0
CPU: 1 PID: 680 Comm: a.out Not tainted
Call Trace:
<TASK>
dump_stack_lvl+0x8d/0xcf
check_preemption_disabled+0x104/0x110
? optimize_nops.isra.7+0x230/0x230
? text_poke_bp_batch+0x9f/0x310
perf_ftrace_function_call+0x6f/0x2e0
...
__text_poke+0x5/0x620
text_poke_bp_batch+0x9f/0x310
This telling us the CPU could be changed after task is preempted, and
the checking on CPU before preemption will be invalid.
This patch just turn off preemption in perf_ftrace_function_call()
to prevent CPU changing.
CC: Steven Rostedt <rostedt@goodmis.org>
Reported-by: Abaci <redacted>
Signed-off-by: Michael Wang <redacted>
---
kernel/trace/trace_event_perf.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
The testing show that perf_ftrace_function_call() are using
smp_processor_id() with preemption enabled, all the checking
on CPU could be wrong after preemption, PATCH 1/2 will fix
that.
2/2 actually.
Besides, as Peter point out, the testing of recursion within
the section between ftrace_test_recursion_trylock()/_unlock()
pair also need the preemption disabled as the documentation
explained, PATCH 2/2 will make sure on that.
1/2 actually...
Regards,
Michael Wang
Michael Wang (2):
ftrace: disable preemption on the testing of recursion
ftrace: prevent preemption in perf_ftrace_function_call()
arch/csky/kernel/probes/ftrace.c | 2 --
arch/parisc/kernel/ftrace.c | 2 --
arch/powerpc/kernel/kprobes-ftrace.c | 2 --
arch/riscv/kernel/probes/ftrace.c | 2 --
arch/x86/kernel/kprobes/ftrace.c | 2 --
include/linux/trace_recursion.h | 10 +++++++++-
kernel/livepatch/patch.c | 6 ------
kernel/trace/trace_event_perf.c | 17 +++++++++++++----
kernel/trace/trace_functions.c | 5 -----
9 files changed, 22 insertions(+), 26 deletions(-)
From: Steven Rostedt <rostedt@goodmis.org> Date: 2021-10-12 12:17:37
On Tue, 12 Oct 2021 13:40:08 +0800
王贇 [off-list ref] wrote:
quoted hunk
@@ -52,11 +52,6 @@ static void notrace klp_ftrace_handler(unsigned long ip, bit = ftrace_test_recursion_trylock(ip, parent_ip); if (WARN_ON_ONCE(bit < 0)) return;- /*- * A variant of synchronize_rcu() is used to allow patching functions- * where RCU is not watching, see klp_synchronize_transition().- */
I have to take a deeper look at this patch set, but do not remove this
comment, as it explains the protection here, that is not obvious with the
changes you made.
-- Steve
@@ -52,11 +52,6 @@ static void notrace klp_ftrace_handler(unsigned long ip,bit=ftrace_test_recursion_trylock(ip,parent_ip);if(WARN_ON_ONCE(bit<0))return;-/*-*Avariantofsynchronize_rcu()isusedtoallowpatchingfunctions-*whereRCUisnotwatching,seeklp_synchronize_transition().-*/-preempt_disable_notrace();func=list_first_or_null_rcu(&ops->func_stack,structklp_func,stack_node);
@@ -120,7 +115,6 @@ static void notrace klp_ftrace_handler(unsigned long ip,klp_arch_set_pc(fregs,(unsignedlong)func->new_func);unlock:-preempt_enable_notrace();ftrace_test_recursion_unlock(bit);}
I don't like this change much. We have preempt_disable there not because
of ftrace_test_recursion, but because of RCU. ftrace_test_recursion was
added later. Yes, it would work with the change, but it would also hide
things which should not be hidden in my opinion.
Miroslav
From: Steven Rostedt <rostedt@goodmis.org> Date: 2021-10-12 12:29:26
On Tue, 12 Oct 2021 14:24:43 +0200 (CEST)
Miroslav Benes [off-list ref] wrote:
quoted
+++ b/kernel/livepatch/patch.c
@@ -52,11 +52,6 @@ static void notrace klp_ftrace_handler(unsigned long ip,bit=ftrace_test_recursion_trylock(ip,parent_ip);if(WARN_ON_ONCE(bit<0))return;-/*-*Avariantofsynchronize_rcu()isusedtoallowpatchingfunctions-*whereRCUisnotwatching,seeklp_synchronize_transition().-*/-preempt_disable_notrace();func=list_first_or_null_rcu(&ops->func_stack,structklp_func,stack_node);
@@ -120,7 +115,6 @@ static void notrace klp_ftrace_handler(unsigned long ip,klp_arch_set_pc(fregs,(unsignedlong)func->new_func);unlock:-preempt_enable_notrace();ftrace_test_recursion_unlock(bit);}
I don't like this change much. We have preempt_disable there not because
of ftrace_test_recursion, but because of RCU. ftrace_test_recursion was
added later. Yes, it would work with the change, but it would also hide
things which should not be hidden in my opinion.
Agreed, but I believe the change is fine, but requires a nice comment to
explain what you said above.
Thus, before the "ftrace_test_recursion_trylock()" we need:
/*
* The ftrace_test_recursion_trylock() will disable preemption,
* which is required for the variant of synchronize_rcu() that is
* used to allow patching functions where RCU is not watching.
* See klp_synchronize_transition() for more details.
*/
-- Steve
The recursion test does not require preemption disabled, it uses the task
struct, not per_cpu variables, so you should not disable it before the test.
bit = trace_test_and_set_recursion(ip, parent_ip, TRACE_FTRACE_START, TRACE_FTRACE_MAX);
if (bit >= 0)
preempt_disable_notrace();
And if the bit is zero, it means a recursion check was already done by
another caller (ftrace handler does the check, followed by calling perf),
and you really don't even need to disable preemption in that case.
if (bit > 0)
preempt_disable_notrace();
And on the unlock, have:
static __always_inline void ftrace_test_recursion_unlock(int bit)
{
if (bit)
preempt_enable_notrace();
trace_clear_recursion(bit);
}
But maybe that's over optimizing ;-)
-- Steve
+ bit = trace_test_and_set_recursion(ip, parent_ip, TRACE_FTRACE_START, TRACE_FTRACE_MAX);
+ if (bit < 0)
+ preempt_enable_notrace();
+
+ return bit;
}
This seems rather daft, wouldn't it be easier to just put that check
under the recursion thing?
In case if the condition matched, extra lock/unlock will be introduced,
but I guess that's acceptable since this seems unlikely to happen :-P
Will move the check in v2.
Regards,
Michael Wang
On Tue, 12 Oct 2021 13:40:08 +0800
王贇 [off-list ref] wrote:
quoted
@@ -52,11 +52,6 @@ static void notrace klp_ftrace_handler(unsigned long ip, bit = ftrace_test_recursion_trylock(ip, parent_ip); if (WARN_ON_ONCE(bit < 0)) return;- /*- * A variant of synchronize_rcu() is used to allow patching functions- * where RCU is not watching, see klp_synchronize_transition().- */
I have to take a deeper look at this patch set, but do not remove this
comment, as it explains the protection here, that is not obvious with the
changes you made.
I don't like this change much. We have preempt_disable there not because
of ftrace_test_recursion, but because of RCU. ftrace_test_recursion was
added later. Yes, it would work with the change, but it would also hide
things which should not be hidden in my opinion.
Not very sure about the backgroup stories, but just found this in
'Documentation/trace/ftrace-uses.rst':
Note, on success,
ftrace_test_recursion_trylock() will disable preemption, and the
ftrace_test_recursion_unlock() will enable it again (if it was previously
enabled).
Seems like this lock pair was supposed to take care the preemtion itself?
Regards,
Michael Wang
On Tue, 12 Oct 2021 14:24:43 +0200 (CEST)
Miroslav Benes [off-list ref] wrote:
quoted
quoted
+++ b/kernel/livepatch/patch.c
@@ -52,11 +52,6 @@ static void notrace klp_ftrace_handler(unsigned long ip,bit=ftrace_test_recursion_trylock(ip,parent_ip);if(WARN_ON_ONCE(bit<0))return;-/*-*Avariantofsynchronize_rcu()isusedtoallowpatchingfunctions-*whereRCUisnotwatching,seeklp_synchronize_transition().-*/-preempt_disable_notrace();func=list_first_or_null_rcu(&ops->func_stack,structklp_func,stack_node);
@@ -120,7 +115,6 @@ static void notrace klp_ftrace_handler(unsigned long ip,klp_arch_set_pc(fregs,(unsignedlong)func->new_func);unlock:-preempt_enable_notrace();ftrace_test_recursion_unlock(bit);}
I don't like this change much. We have preempt_disable there not because
of ftrace_test_recursion, but because of RCU. ftrace_test_recursion was
added later. Yes, it would work with the change, but it would also hide
things which should not be hidden in my opinion.
Agreed, but I believe the change is fine, but requires a nice comment to
explain what you said above.
Thus, before the "ftrace_test_recursion_trylock()" we need:
/*
* The ftrace_test_recursion_trylock() will disable preemption,
* which is required for the variant of synchronize_rcu() that is
* used to allow patching functions where RCU is not watching.
* See klp_synchronize_transition() for more details.
*/
The recursion test does not require preemption disabled, it uses the task
struct, not per_cpu variables, so you should not disable it before the test.
bit = trace_test_and_set_recursion(ip, parent_ip, TRACE_FTRACE_START, TRACE_FTRACE_MAX);
if (bit >= 0)
preempt_disable_notrace();
And if the bit is zero, it means a recursion check was already done by
another caller (ftrace handler does the check, followed by calling perf),
and you really don't even need to disable preemption in that case.
if (bit > 0)
preempt_disable_notrace();
And on the unlock, have:
static __always_inline void ftrace_test_recursion_unlock(int bit)
{
if (bit)
preempt_enable_notrace();
trace_clear_recursion(bit);
}
But maybe that's over optimizing ;-)
I see, while the user can still check smp_processor_id() after trylock
return bit 0...
I guess Peter's point at very beginning is to prevent such cases, since
kernel for production will not have preemption debug on, and such issue
won't get report but could cause trouble which really hard to trace down
, way to eliminate such issue once for all sounds attractive, isn't it?
Regards,
Michael Wang
-- Steve
quoted
+ bit = trace_test_and_set_recursion(ip, parent_ip, TRACE_FTRACE_START, TRACE_FTRACE_MAX);
+ if (bit < 0)
+ preempt_enable_notrace();
+
+ return bit;
}
I don't like this change much. We have preempt_disable there not because
of ftrace_test_recursion, but because of RCU. ftrace_test_recursion was
added later. Yes, it would work with the change, but it would also hide
things which should not be hidden in my opinion.
Not very sure about the backgroup stories, but just found this in
'Documentation/trace/ftrace-uses.rst':
Note, on success,
ftrace_test_recursion_trylock() will disable preemption, and the
ftrace_test_recursion_unlock() will enable it again (if it was previously
enabled).
Right that part is to be fixed by what you are adding here.
The point that Miroslav is complaining about is that the preemption
disabling is special in this case, and not just from the recursion
point of view, which is why the comment is still required.
-- Steve
Seems like this lock pair was supposed to take care the preemtion itself?
From: Steven Rostedt <rostedt@goodmis.org> Date: 2021-10-13 02:30:46
On Wed, 13 Oct 2021 10:04:52 +0800
王贇 [off-list ref] wrote:
I see, while the user can still check smp_processor_id() after trylock
return bit 0...
But preemption would have already been disabled. That's because a bit 0
means that a recursion check has already been made by a previous
caller and this one is nested, thus preemption is already disabled.
If bit is 0, then preemption had better be disabled as well.
-- Steve
I don't like this change much. We have preempt_disable there not because
of ftrace_test_recursion, but because of RCU. ftrace_test_recursion was
added later. Yes, it would work with the change, but it would also hide
things which should not be hidden in my opinion.
Not very sure about the backgroup stories, but just found this in
'Documentation/trace/ftrace-uses.rst':
Note, on success,
ftrace_test_recursion_trylock() will disable preemption, and the
ftrace_test_recursion_unlock() will enable it again (if it was previously
enabled).
Right that part is to be fixed by what you are adding here.
The point that Miroslav is complaining about is that the preemption
disabling is special in this case, and not just from the recursion
point of view, which is why the comment is still required.
My bad... the title do confusing people, will rewrite it.
Regards,
Michael Wang
-- Steve
quoted
Seems like this lock pair was supposed to take care the preemtion itself?
On Wed, 13 Oct 2021 10:04:52 +0800
王贇 [off-list ref] wrote:
quoted
I see, while the user can still check smp_processor_id() after trylock
return bit 0...
But preemption would have already been disabled. That's because a bit 0
means that a recursion check has already been made by a previous
caller and this one is nested, thus preemption is already disabled.
If bit is 0, then preemption had better be disabled as well.
Thanks for the explain, now I get your point :-)
Let's make bit 0 an exemption then.
Regards,
Michael Wang
The testing show that perf_ftrace_function_call() are using smp_processor_id()
with preemption enabled, all the checking on CPU could be wrong after preemption.
As Peter point out, the section between ftrace_test_recursion_trylock/unlock()
pair require the preemption to be disabled as 'Documentation/trace/ftrace-uses.rst'
explained, but currently the work is done outside of the helpers.
Patch 1/2 will make sure preemption disabled after trylock() succeed,
patch 2/2 will do smp_processor_id() checking after trylock to address the
issue.
Michael Wang (2):
ftrace: disable preemption between ftrace_test_recursion_trylock/unlock()
ftrace: do CPU checking after preemption disabled
arch/csky/kernel/probes/ftrace.c | 2 --
arch/parisc/kernel/ftrace.c | 2 --
arch/powerpc/kernel/kprobes-ftrace.c | 2 --
arch/riscv/kernel/probes/ftrace.c | 2 --
arch/x86/kernel/kprobes/ftrace.c | 2 --
include/linux/trace_recursion.h | 22 +++++++++++++++++++++-
kernel/livepatch/patch.c | 6 ------
kernel/trace/trace_event_perf.c | 6 +++---
kernel/trace/trace_functions.c | 5 -----
9 files changed, 24 insertions(+), 25 deletions(-)
--
1.8.3.1
As the documentation explained, ftrace_test_recursion_trylock()
and ftrace_test_recursion_unlock() were supposed to disable and
enable preemption properly, however currently this work is done
outside of the function, which could be missing by mistake.
This path will make sure the preemption was disabled when trylock()
succeed, and the unlock() will enable the preemption if previously
enabled.
CC: Steven Rostedt <rostedt@goodmis.org>
CC: Miroslav Benes <mbenes@suse.cz>
Reported-by: Abaci <redacted>
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Michael Wang <redacted>
---
arch/csky/kernel/probes/ftrace.c | 2 --
arch/parisc/kernel/ftrace.c | 2 --
arch/powerpc/kernel/kprobes-ftrace.c | 2 --
arch/riscv/kernel/probes/ftrace.c | 2 --
arch/x86/kernel/kprobes/ftrace.c | 2 --
include/linux/trace_recursion.h | 22 +++++++++++++++++++++-
kernel/livepatch/patch.c | 6 ------
kernel/trace/trace_functions.c | 5 -----
8 files changed, 21 insertions(+), 22 deletions(-)
@@ -222,9 +238,13 @@ static __always_inline int ftrace_test_recursion_trylock(unsigned long ip,*@bit:Thereturnofasuccessfulftrace_test_recursion_trylock()**Thisisusedattheendofaftracecallback.+*+*Preemptionwillbeenabled(ifitwaspreviouslyenabled).*/static__always_inlinevoidftrace_test_recursion_unlock(intbit){+if(bit)+preempt_enable_notrace();trace_clear_recursion(bit);}
With CONFIG_DEBUG_PREEMPT we observed reports like:
BUG: using smp_processor_id() in preemptible
caller is perf_ftrace_function_call+0x6f/0x2e0
CPU: 1 PID: 680 Comm: a.out Not tainted
Call Trace:
<TASK>
dump_stack_lvl+0x8d/0xcf
check_preemption_disabled+0x104/0x110
? optimize_nops.isra.7+0x230/0x230
? text_poke_bp_batch+0x9f/0x310
perf_ftrace_function_call+0x6f/0x2e0
...
__text_poke+0x5/0x620
text_poke_bp_batch+0x9f/0x310
This telling us the CPU could be changed after task is preempted, and
the checking on CPU before preemption will be invalid.
Since now ftrace_test_recursion_trylock() will help to disable the
preemption, this patch just do the checking after trylock() to address
the issue.
CC: Steven Rostedt <rostedt@goodmis.org>
Reported-by: Abaci <redacted>
Signed-off-by: Michael Wang <redacted>
---
kernel/trace/trace_event_perf.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
The testing show that perf_ftrace_function_call() are using smp_processor_id()
with preemption enabled, all the checking on CPU could be wrong after preemption.
As Peter point out, the section between ftrace_test_recursion_trylock/unlock()
pair require the preemption to be disabled as 'Documentation/trace/ftrace-uses.rst'
explained, but currently the work is done outside of the helpers.
Patch 1/2 will make sure preemption disabled after trylock() succeed,
patch 2/2 will do smp_processor_id() checking after trylock to address the
issue.
Michael Wang (2):
ftrace: disable preemption between ftrace_test_recursion_trylock/unlock()
ftrace: do CPU checking after preemption disabled
arch/csky/kernel/probes/ftrace.c | 2 --
arch/parisc/kernel/ftrace.c | 2 --
arch/powerpc/kernel/kprobes-ftrace.c | 2 --
arch/riscv/kernel/probes/ftrace.c | 2 --
arch/x86/kernel/kprobes/ftrace.c | 2 --
include/linux/trace_recursion.h | 22 +++++++++++++++++++++-
kernel/livepatch/patch.c | 6 ------
kernel/trace/trace_event_perf.c | 6 +++---
kernel/trace/trace_functions.c | 5 -----
9 files changed, 24 insertions(+), 25 deletions(-)
Ok, I'll resend it with link then.
Regards,
Michael Wang
-- Steve
On Wed, 13 Oct 2021 11:16:56 +0800
王贇 [off-list ref] wrote:
quoted
The testing show that perf_ftrace_function_call() are using smp_processor_id()
with preemption enabled, all the checking on CPU could be wrong after preemption.
As Peter point out, the section between ftrace_test_recursion_trylock/unlock()
pair require the preemption to be disabled as 'Documentation/trace/ftrace-uses.rst'
explained, but currently the work is done outside of the helpers.
Patch 1/2 will make sure preemption disabled after trylock() succeed,
patch 2/2 will do smp_processor_id() checking after trylock to address the
issue.
Michael Wang (2):
ftrace: disable preemption between ftrace_test_recursion_trylock/unlock()
ftrace: do CPU checking after preemption disabled
arch/csky/kernel/probes/ftrace.c | 2 --
arch/parisc/kernel/ftrace.c | 2 --
arch/powerpc/kernel/kprobes-ftrace.c | 2 --
arch/riscv/kernel/probes/ftrace.c | 2 --
arch/x86/kernel/kprobes/ftrace.c | 2 --
include/linux/trace_recursion.h | 22 +++++++++++++++++++++-
kernel/livepatch/patch.c | 6 ------
kernel/trace/trace_event_perf.c | 6 +++---
kernel/trace/trace_functions.c | 5 -----
9 files changed, 24 insertions(+), 25 deletions(-)
On Mon, 11 Oct 2021 22:39:16 PDT (-0700), yun.wang@linux.alibaba.com wrote:
The testing show that perf_ftrace_function_call() are using
smp_processor_id() with preemption enabled, all the checking
on CPU could be wrong after preemption, PATCH 1/2 will fix
that.
Besides, as Peter point out, the testing of recursion within
the section between ftrace_test_recursion_trylock()/_unlock()
pair also need the preemption disabled as the documentation
explained, PATCH 2/2 will make sure on that.
Michael Wang (2):
ftrace: disable preemption on the testing of recursion
ftrace: prevent preemption in perf_ftrace_function_call()
arch/csky/kernel/probes/ftrace.c | 2 --
arch/parisc/kernel/ftrace.c | 2 --
arch/powerpc/kernel/kprobes-ftrace.c | 2 --
arch/riscv/kernel/probes/ftrace.c | 2 --
arch/x86/kernel/kprobes/ftrace.c | 2 --
include/linux/trace_recursion.h | 10 +++++++++-
kernel/livepatch/patch.c | 6 ------
kernel/trace/trace_event_perf.c | 17 +++++++++++++----
kernel/trace/trace_functions.c | 5 -----
9 files changed, 22 insertions(+), 26 deletions(-)