diff options
author | Oleg Nesterov <oleg@tv-sign.ru> | 2005-06-28 20:44:47 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-28 21:20:30 -0700 |
commit | f01b1b0baa454825ed95c28d2a6a71bbf4510836 (patch) | |
tree | 6a7dda617cdb79933780b841429e67ec7c908d52 /kernel/itimer.c | |
parent | b36bbb6c3d5244eaf52241ec69f79494137f2db0 (diff) | |
download | linux-f01b1b0baa454825ed95c28d2a6a71bbf4510836.tar.gz linux-f01b1b0baa454825ed95c28d2a6a71bbf4510836.tar.bz2 linux-f01b1b0baa454825ed95c28d2a6a71bbf4510836.zip |
[PATCH] ITIMER_REAL: fix possible deadlock and race
As Steven Rostedt pointed out, there are 2 problems with ITIMER_REAL
timers.
1. do_setitimer() does not call del_timer_sync() in case
when the timer is not pending (it_real_value() returns 0).
This is wrong, the timer may still be running, and it can
rearm itself.
2. It calls del_timer_sync() with tsk->sighand->siglock held.
This is deadlockable, because timer's handler needs this
lock too.
Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>
Acked-by: Steven Rostedt <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel/itimer.c')
-rw-r--r-- | kernel/itimer.c | 8 |
1 files changed, 6 insertions, 2 deletions
diff --git a/kernel/itimer.c b/kernel/itimer.c index 1dc988e0d2c7..a72cb0e5aa4b 100644 --- a/kernel/itimer.c +++ b/kernel/itimer.c @@ -153,11 +153,15 @@ int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue) switch (which) { case ITIMER_REAL: +again: spin_lock_irq(&tsk->sighand->siglock); interval = tsk->signal->it_real_incr; val = it_real_value(tsk->signal); - if (val) - del_timer_sync(&tsk->signal->real_timer); + /* We are sharing ->siglock with it_real_fn() */ + if (try_to_del_timer_sync(&tsk->signal->real_timer) < 0) { + spin_unlock_irq(&tsk->sighand->siglock); + goto again; + } tsk->signal->it_real_incr = timeval_to_jiffies(&value->it_interval); it_real_arm(tsk, timeval_to_jiffies(&value->it_value)); |