diff options
author | Steven Rostedt <srostedt@redhat.com> | 2008-12-03 11:04:51 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-12-03 11:15:03 -0500 |
commit | e8e1abe92fd7ea9d823a3aaf81d10e2cba593b6b (patch) | |
tree | 9ddeaf43558b2ad13c6b4758b17b84781f0217a7 /kernel/fork.c | |
parent | 0a37119d963e876ca86912497346ec50dea2541b (diff) |
ftrace: fix race in function graph during fork
Impact: graph tracer race/crash fix
There is a nasy race in startup of a new process running the
function graph tracer. In fork.c:
total_forks++;
spin_unlock(¤t->sighand->siglock);
write_unlock_irq(&tasklist_lock);
ftrace_graph_init_task(p);
proc_fork_connector(p);
cgroup_post_fork(p);
return p;
The new task is free to run as soon as the tasklist_lock is released.
This is before the ftrace_graph_init_task. If the task does run
it will be using the same ret_stack and curr_ret_stack as the parent.
This will cause crashes that are difficult to debug.
This patch moves the ftrace_graph_init_task to just after the alloc_pid
code. This fixes the above race.
Signed-off-by: Steven Rostedt <srostedt@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/fork.c')
-rw-r--r-- | kernel/fork.c | 9 |
1 files changed, 6 insertions, 3 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index 5f82a999c032..7407ab319875 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1137,6 +1137,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1137 | } | 1137 | } |
1138 | } | 1138 | } |
1139 | 1139 | ||
1140 | ftrace_graph_init_task(p); | ||
1141 | |||
1140 | p->pid = pid_nr(pid); | 1142 | p->pid = pid_nr(pid); |
1141 | p->tgid = p->pid; | 1143 | p->tgid = p->pid; |
1142 | if (clone_flags & CLONE_THREAD) | 1144 | if (clone_flags & CLONE_THREAD) |
@@ -1145,7 +1147,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1145 | if (current->nsproxy != p->nsproxy) { | 1147 | if (current->nsproxy != p->nsproxy) { |
1146 | retval = ns_cgroup_clone(p, pid); | 1148 | retval = ns_cgroup_clone(p, pid); |
1147 | if (retval) | 1149 | if (retval) |
1148 | goto bad_fork_free_pid; | 1150 | goto bad_fork_free_graph; |
1149 | } | 1151 | } |
1150 | 1152 | ||
1151 | p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; | 1153 | p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; |
@@ -1238,7 +1240,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1238 | spin_unlock(¤t->sighand->siglock); | 1240 | spin_unlock(¤t->sighand->siglock); |
1239 | write_unlock_irq(&tasklist_lock); | 1241 | write_unlock_irq(&tasklist_lock); |
1240 | retval = -ERESTARTNOINTR; | 1242 | retval = -ERESTARTNOINTR; |
1241 | goto bad_fork_free_pid; | 1243 | goto bad_fork_free_graph; |
1242 | } | 1244 | } |
1243 | 1245 | ||
1244 | if (clone_flags & CLONE_THREAD) { | 1246 | if (clone_flags & CLONE_THREAD) { |
@@ -1271,11 +1273,12 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1271 | total_forks++; | 1273 | total_forks++; |
1272 | spin_unlock(¤t->sighand->siglock); | 1274 | spin_unlock(¤t->sighand->siglock); |
1273 | write_unlock_irq(&tasklist_lock); | 1275 | write_unlock_irq(&tasklist_lock); |
1274 | ftrace_graph_init_task(p); | ||
1275 | proc_fork_connector(p); | 1276 | proc_fork_connector(p); |
1276 | cgroup_post_fork(p); | 1277 | cgroup_post_fork(p); |
1277 | return p; | 1278 | return p; |
1278 | 1279 | ||
1280 | bad_fork_free_graph: | ||
1281 | ftrace_graph_exit_task(p); | ||
1279 | bad_fork_free_pid: | 1282 | bad_fork_free_pid: |
1280 | if (pid != &init_struct_pid) | 1283 | if (pid != &init_struct_pid) |
1281 | free_pid(pid); | 1284 | free_pid(pid); |