diff options
author | Oleg Nesterov <oleg@redhat.com> | 2014-04-13 14:58:54 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2014-06-21 00:15:12 -0400 |
commit | 4af4206be2bd1933cae20c2b6fb2058dbc887f7c (patch) | |
tree | a11266fc7f0eb38a6185698a899a914dda13ed57 | |
parent | 7171511eaec5bf23fb06078f59784a3a0626b38f (diff) |
tracing: Fix syscall_*regfunc() vs copy_process() race
syscall_regfunc() and syscall_unregfunc() should set/clear
TIF_SYSCALL_TRACEPOINT system-wide, but do_each_thread() can race
with copy_process() and miss the new child which was not added to
the process/thread lists yet.
Change copy_process() to update the child's TIF_SYSCALL_TRACEPOINT
under tasklist.
Link: http://lkml.kernel.org/p/20140413185854.GB20668@redhat.com
Cc: stable@vger.kernel.org # 2.6.33
Fixes: a871bd33a6c0 "tracing: Add syscall tracepoints"
Acked-by: Frederic Weisbecker <fweisbec@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | include/trace/syscall.h | 15 | ||||
-rw-r--r-- | kernel/fork.c | 2 |
2 files changed, 17 insertions, 0 deletions
diff --git a/include/trace/syscall.h b/include/trace/syscall.h index fed853f3d7aa..9674145e2f6a 100644 --- a/include/trace/syscall.h +++ b/include/trace/syscall.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <linux/tracepoint.h> | 4 | #include <linux/tracepoint.h> |
5 | #include <linux/unistd.h> | 5 | #include <linux/unistd.h> |
6 | #include <linux/ftrace_event.h> | 6 | #include <linux/ftrace_event.h> |
7 | #include <linux/thread_info.h> | ||
7 | 8 | ||
8 | #include <asm/ptrace.h> | 9 | #include <asm/ptrace.h> |
9 | 10 | ||
@@ -32,4 +33,18 @@ struct syscall_metadata { | |||
32 | struct ftrace_event_call *exit_event; | 33 | struct ftrace_event_call *exit_event; |
33 | }; | 34 | }; |
34 | 35 | ||
36 | #if defined(CONFIG_TRACEPOINTS) && defined(CONFIG_HAVE_SYSCALL_TRACEPOINTS) | ||
37 | static inline void syscall_tracepoint_update(struct task_struct *p) | ||
38 | { | ||
39 | if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) | ||
40 | set_tsk_thread_flag(p, TIF_SYSCALL_TRACEPOINT); | ||
41 | else | ||
42 | clear_tsk_thread_flag(p, TIF_SYSCALL_TRACEPOINT); | ||
43 | } | ||
44 | #else | ||
45 | static inline void syscall_tracepoint_update(struct task_struct *p) | ||
46 | { | ||
47 | } | ||
48 | #endif | ||
49 | |||
35 | #endif /* _TRACE_SYSCALL_H */ | 50 | #endif /* _TRACE_SYSCALL_H */ |
diff --git a/kernel/fork.c b/kernel/fork.c index d2799d1fc952..6a13c46cd87d 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1487,7 +1487,9 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1487 | 1487 | ||
1488 | total_forks++; | 1488 | total_forks++; |
1489 | spin_unlock(¤t->sighand->siglock); | 1489 | spin_unlock(¤t->sighand->siglock); |
1490 | syscall_tracepoint_update(p); | ||
1490 | write_unlock_irq(&tasklist_lock); | 1491 | write_unlock_irq(&tasklist_lock); |
1492 | |||
1491 | proc_fork_connector(p); | 1493 | proc_fork_connector(p); |
1492 | cgroup_post_fork(p); | 1494 | cgroup_post_fork(p); |
1493 | if (clone_flags & CLONE_THREAD) | 1495 | if (clone_flags & CLONE_THREAD) |