diff options
author | Oleg Nesterov <oleg@redhat.com> | 2008-11-17 09:39:47 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-11-17 10:49:35 -0500 |
commit | ad133ba3dc283300e5b62b5b7211d2f39fbf6ee7 (patch) | |
tree | 4762492d6c564e0ac1ec47122a0d0d49b0d41e90 /kernel/sched_stats.h | |
parent | 29d7b90c15035741d15421b36000509212b3e135 (diff) |
sched, signals: fix the racy usage of ->signal in account_group_xxx/run_posix_cpu_timers
Impact: fix potential NULL dereference
Contrary to ad474caca3e2a0550b7ce0706527ad5ab389a4d4 changelog, other
acct_group_xxx() helpers can be called after exit_notify() by timer tick.
Thanks to Roland for pointing out this. Somehow I missed this simple fact
when I read the original patch, and I am afraid I confused Frank during
the discussion. Sorry.
Fortunately, these helpers work with current, we can check ->exit_state
to ensure that ->signal can't go away under us.
Also, add the comment and compiler barrier to account_group_exec_runtime(),
to make sure we load ->signal only once.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/sched_stats.h')
-rw-r--r-- | kernel/sched_stats.h | 15 |
1 files changed, 11 insertions, 4 deletions
diff --git a/kernel/sched_stats.h b/kernel/sched_stats.h index ee71bec1da66..7dbf72a2b02c 100644 --- a/kernel/sched_stats.h +++ b/kernel/sched_stats.h | |||
@@ -298,9 +298,11 @@ static inline void account_group_user_time(struct task_struct *tsk, | |||
298 | { | 298 | { |
299 | struct signal_struct *sig; | 299 | struct signal_struct *sig; |
300 | 300 | ||
301 | sig = tsk->signal; | 301 | /* tsk == current, ensure it is safe to use ->signal */ |
302 | if (unlikely(!sig)) | 302 | if (unlikely(tsk->exit_state)) |
303 | return; | 303 | return; |
304 | |||
305 | sig = tsk->signal; | ||
304 | if (sig->cputime.totals) { | 306 | if (sig->cputime.totals) { |
305 | struct task_cputime *times; | 307 | struct task_cputime *times; |
306 | 308 | ||
@@ -325,9 +327,11 @@ static inline void account_group_system_time(struct task_struct *tsk, | |||
325 | { | 327 | { |
326 | struct signal_struct *sig; | 328 | struct signal_struct *sig; |
327 | 329 | ||
328 | sig = tsk->signal; | 330 | /* tsk == current, ensure it is safe to use ->signal */ |
329 | if (unlikely(!sig)) | 331 | if (unlikely(tsk->exit_state)) |
330 | return; | 332 | return; |
333 | |||
334 | sig = tsk->signal; | ||
331 | if (sig->cputime.totals) { | 335 | if (sig->cputime.totals) { |
332 | struct task_cputime *times; | 336 | struct task_cputime *times; |
333 | 337 | ||
@@ -353,8 +357,11 @@ static inline void account_group_exec_runtime(struct task_struct *tsk, | |||
353 | struct signal_struct *sig; | 357 | struct signal_struct *sig; |
354 | 358 | ||
355 | sig = tsk->signal; | 359 | sig = tsk->signal; |
360 | /* see __exit_signal()->task_rq_unlock_wait() */ | ||
361 | barrier(); | ||
356 | if (unlikely(!sig)) | 362 | if (unlikely(!sig)) |
357 | return; | 363 | return; |
364 | |||
358 | if (sig->cputime.totals) { | 365 | if (sig->cputime.totals) { |
359 | struct task_cputime *times; | 366 | struct task_cputime *times; |
360 | 367 | ||