Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1457623 > unrolled thread
| Started by | Miroslav Benes <mbenes@suse.cz> |
|---|---|
| First post | 2016-08-08 11:00 +0200 |
| Last post | 2016-08-09 14:20 +0200 |
| Articles | 4 — 2 participants |
Back to article view | Back to linux.kernel
A bug in ftrace - dynamic fops Miroslav Benes <mbenes@suse.cz> - 2016-08-08 11:00 +0200
Re: A bug in ftrace - dynamic fops Steven Rostedt <rostedt@goodmis.org> - 2016-08-08 17:00 +0200
Re: A bug in ftrace - dynamic fops Miroslav Benes <mbenes@suse.cz> - 2016-08-09 10:20 +0200
Re: A bug in ftrace - dynamic fops Steven Rostedt <rostedt@goodmis.org> - 2016-08-09 14:20 +0200
| From | Miroslav Benes <mbenes@suse.cz> |
|---|---|
| Date | 2016-08-08 11:00 +0200 |
| Subject | A bug in ftrace - dynamic fops |
| Message-ID | <s3OzD-7xb-11@gated-at.bofh.it> |
Hi Steven,
I am afraid there is a bug in the current mainline's ftrace when dynamic
fops are involved.
ftrace_shutdown() relies on schedule_on_each_cpu() which should ensure
that no task stays in a ftrace handler. This was sufficient for a long
time because every handler was called with the preemption disabled thanks
to ftrace_ops_list_func (or ftrace_ops_assist_func). Dynamic trampolines
did not change the behaviour because !PREEMPT was required (commit
12cce594fa8f ("ftrace/x86: Allow !CONFIG_PREEMPT dynamic ops to use
allocated trampolines")).
Situation changed with the commit 00ccbf2f5b75 ("ftrace/x86: Let dynamic
trampolines call ops->func even for dynamic fops"). The purpose of the
patch is clear - to call ops->func whenever possible and thus gain an
advantage of dynamic trampolines. But it also allows the handler (that
very ops->func) to sleep because no atomic context is enforced. This
breaks the assumption for schedule_on_each_cpu() in ftrace_shutdown() and
one can crash the kernel quite easily.
It suffices to register dynamic fops with FTRACE_OPS_FL_RECURSION_SAFE set
(because otherwise ftrace_ops_assist_func() is used which also disables
preemption), sleep in the handler and meanwhile remove it.
I can imagine two reasonable solutions...
1. introduce something similar to ftrace_ops_assist_func() which would
just disable preemption before calling ops->func and enable it afterwards.
or
2. implement the whole thing through RCU_TASKS. This would also enable
dynamic trampolines for PREEMPT kernels.
Revert of 00ccbf2f5b75 commit would be solution as well but there is a
drawback.
Regards,
Miroslav
[toc] | [next] | [standalone]
| From | Steven Rostedt <rostedt@goodmis.org> |
|---|---|
| Date | 2016-08-08 17:00 +0200 |
| Message-ID | <s3Uc1-2G2-15@gated-at.bofh.it> |
| In reply to | #1457623 |
On Mon, 8 Aug 2016 10:57:45 +0200 (CEST)
Miroslav Benes <mbenes@suse.cz> wrote:
> Hi Steven,
>
> I am afraid there is a bug in the current mainline's ftrace when dynamic
> fops are involved.
I'm sorry but I don't see it.
>
> ftrace_shutdown() relies on schedule_on_each_cpu() which should ensure
> that no task stays in a ftrace handler. This was sufficient for a long
> time because every handler was called with the preemption disabled thanks
> to ftrace_ops_list_func (or ftrace_ops_assist_func). Dynamic trampolines
> did not change the behaviour because !PREEMPT was required (commit
> 12cce594fa8f ("ftrace/x86: Allow !CONFIG_PREEMPT dynamic ops to use
> allocated trampolines")).
>
> Situation changed with the commit 00ccbf2f5b75 ("ftrace/x86: Let dynamic
> trampolines call ops->func even for dynamic fops"). The purpose of the
> patch is clear - to call ops->func whenever possible and thus gain an
> advantage of dynamic trampolines. But it also allows the handler (that
> very ops->func) to sleep because no atomic context is enforced. This
> breaks the assumption for schedule_on_each_cpu() in ftrace_shutdown() and
> one can crash the kernel quite easily.
Not when CONFIG_PREEMPT is not enabled. This is because the kernel does
not preempt unless it specifically asks to be preempted. If ops->func()
calls a mutex, or sleeps, then *THAT* is a bug! ops->func() is more
sensitive than interrupt handlers, and all ops->func()s must use extra
care. This sounds like a "doctor it hurts me when I do this" bug (where
the doctor replies "well, don't do that").
If something registers an ops->func() that can sleep, then it MUST limit
the functions that it can be registered to, and also must handle any
synchronization of the ops itself being freed. This isn't the ftrace
infrastructure's responsibility.
>
> It suffices to register dynamic fops with FTRACE_OPS_FL_RECURSION_SAFE set
> (because otherwise ftrace_ops_assist_func() is used which also disables
> preemption), sleep in the handler and meanwhile remove it.
>
> I can imagine two reasonable solutions...
>
> 1. introduce something similar to ftrace_ops_assist_func() which would
> just disable preemption before calling ops->func and enable it afterwards.
With CONFIG_PREEMPT disabled, how can ops->func() sleep? It can't,
unless it specifically calls a function that can. I don't see the bug
you mention here.
>
> or
>
> 2. implement the whole thing through RCU_TASKS. This would also enable
> dynamic trampolines for PREEMPT kernels.
That said, this has been on my todo list for too long, and will soon be
implemented. I want CONFIG_PREEMPT to allow dynamic trampolines for
dynamic ops too. Not to mention, RCU_TASKS was specifically written for
me to do this. I dropped the ball on this one. :-p
-- Steve
>
> Revert of 00ccbf2f5b75 commit would be solution as well but there is a
> drawback.
>
> Regards,
> Miroslav
[toc] | [prev] | [next] | [standalone]
| From | Miroslav Benes <mbenes@suse.cz> |
|---|---|
| Date | 2016-08-09 10:20 +0200 |
| Message-ID | <s4aqt-51O-15@gated-at.bofh.it> |
| In reply to | #1457852 |
On Mon, 8 Aug 2016, Steven Rostedt wrote:
> On Mon, 8 Aug 2016 10:57:45 +0200 (CEST)
> Miroslav Benes <mbenes@suse.cz> wrote:
>
> > Hi Steven,
> >
> > I am afraid there is a bug in the current mainline's ftrace when dynamic
> > fops are involved.
>
> I'm sorry but I don't see it.
>
> >
> > ftrace_shutdown() relies on schedule_on_each_cpu() which should ensure
> > that no task stays in a ftrace handler. This was sufficient for a long
> > time because every handler was called with the preemption disabled thanks
> > to ftrace_ops_list_func (or ftrace_ops_assist_func). Dynamic trampolines
> > did not change the behaviour because !PREEMPT was required (commit
> > 12cce594fa8f ("ftrace/x86: Allow !CONFIG_PREEMPT dynamic ops to use
> > allocated trampolines")).
> >
> > Situation changed with the commit 00ccbf2f5b75 ("ftrace/x86: Let dynamic
> > trampolines call ops->func even for dynamic fops"). The purpose of the
> > patch is clear - to call ops->func whenever possible and thus gain an
> > advantage of dynamic trampolines. But it also allows the handler (that
> > very ops->func) to sleep because no atomic context is enforced. This
> > breaks the assumption for schedule_on_each_cpu() in ftrace_shutdown() and
> > one can crash the kernel quite easily.
>
> Not when CONFIG_PREEMPT is not enabled. This is because the kernel does
> not preempt unless it specifically asks to be preempted. If ops->func()
> calls a mutex, or sleeps, then *THAT* is a bug! ops->func() is more
> sensitive than interrupt handlers, and all ops->func()s must use extra
> care. This sounds like a "doctor it hurts me when I do this" bug (where
> the doctor replies "well, don't do that").
I agree it is kind of shooting oneself in the foot bug, because explicit
call to a sleeping function may not be the brightest thing to do. However
I see two (closely related) issues with this.
1. It is a change in behaviour. Ftrace silently relies on an atomicity of
ops->func(). I don't see it documented anywhere, but it did not matter
because the atomicity was always guaranteed as described above. Now there
is a possibility to achieve a situation which breaks the assumption. It
makes me worried.
2. Previously if someone called a function which could sleep he was
immediately warned not to do so via "sleeping in atomic context" BUG. Now
he wouldn't know. That's because in_atomic() and might_sleep()
infrastructure does not work in ops->func(). in_atomic() gives 0 even if
it is an atomic context in fact. But well, the comment for in_atomic() in
linux/preempt.h warns about exactly this situation I guess.
Anyway, it is your call.
> If something registers an ops->func() that can sleep, then it MUST limit
> the functions that it can be registered to, and also must handle any
> synchronization of the ops itself being freed. This isn't the ftrace
> infrastructure's responsibility.
>
> >
> > It suffices to register dynamic fops with FTRACE_OPS_FL_RECURSION_SAFE set
> > (because otherwise ftrace_ops_assist_func() is used which also disables
> > preemption), sleep in the handler and meanwhile remove it.
> >
> > I can imagine two reasonable solutions...
> >
> > 1. introduce something similar to ftrace_ops_assist_func() which would
> > just disable preemption before calling ops->func and enable it afterwards.
>
> With CONFIG_PREEMPT disabled, how can ops->func() sleep? It can't,
> unless it specifically calls a function that can. I don't see the bug
> you mention here.
>
> >
> > or
> >
> > 2. implement the whole thing through RCU_TASKS. This would also enable
> > dynamic trampolines for PREEMPT kernels.
>
> That said, this has been on my todo list for too long, and will soon be
> implemented. I want CONFIG_PREEMPT to allow dynamic trampolines for
> dynamic ops too. Not to mention, RCU_TASKS was specifically written for
> me to do this. I dropped the ball on this one. :-p
That is great to hear. Looking forward to seeing that.
Thanks,
Miroslav
[toc] | [prev] | [next] | [standalone]
| From | Steven Rostedt <rostedt@goodmis.org> |
|---|---|
| Date | 2016-08-09 14:20 +0200 |
| Message-ID | <s4eaJ-7rr-17@gated-at.bofh.it> |
| In reply to | #1458489 |
On Tue, 9 Aug 2016 10:16:00 +0200 (CEST) Miroslav Benes <mbenes@suse.cz> wrote: > I agree it is kind of shooting oneself in the foot bug, because explicit > call to a sleeping function may not be the brightest thing to do. However > I see two (closely related) issues with this. > > 1. It is a change in behaviour. Ftrace silently relies on an atomicity of > ops->func(). I don't see it documented anywhere, but it did not matter > because the atomicity was always guaranteed as described above. Now there > is a possibility to achieve a situation which breaks the assumption. It > makes me worried. Why? It's something that a kernel developer should be aware of. I mean, that ops->func can easily be called from *any* context, like irq, softirq, or even an NMI. One who hooks into any function of the kernel should understand that it has special requirements, just like we don't document that you can't sleep in an NMI. And if you only hook to functions that can sleep, then great! You are allowed to do that too. Just like calling a module function that can sleep. You need to make sure nothing is calling your function when you unload the module. I don't see anything that is deceptive here. > > 2. Previously if someone called a function which could sleep he was > immediately warned not to do so via "sleeping in atomic context" BUG. Now > he wouldn't know. That's because in_atomic() and might_sleep() > infrastructure does not work in ops->func(). in_atomic() gives 0 even if > it is an atomic context in fact. But well, the comment for in_atomic() in > linux/preempt.h warns about exactly this situation I guess. It will warn if you hook to a function that can sleep. And if you never do, then there's nothing wrong. If the only functions you hook to can sleep, then it is fine for you to sleep in your code too. But if you do, you must synchronize that logic. You must make sure all functions are out of the sleep when you unresgister. Just like you must make sure all functions are out of a sleeping function in a module. This is kernel programming 101. I never saw a need to have sleeping functions being called by ops->func() and I don't know of a case that would. If there is a legitimate case (not hypothetical) and then I could add a way to postpone freeing of an ops if need be. Because note, that TASK_RCU will only be called when CONFIG_PREEMPT is enabled. It would be overkill to do it for !CONFIG_PREEMPT, thus it will not solve what you want here. -- Steve
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web