diff options
author | Oleg Nesterov <oleg@tv-sign.ru> | 2005-10-23 12:25:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-10-24 11:12:35 -0400 |
commit | 108150ea78003044e41150c75259447b2c0953b6 (patch) | |
tree | ffe0b7e59e6ca1c8a4dad18110e485e5c72872bc /kernel/posix-cpu-timers.c | |
parent | ba9e358fd04190a59e605c2963a15e014139a707 (diff) |
[PATCH] posix-timers: fix cleanup_timers() and run_posix_cpu_timers() races
1. cleanup_timers() sets timer->task = NULL under tasklist + ->sighand locks.
That means that this code in posix_cpu_timer_del() and posix_cpu_timer_set()
lock_timer(timer);
if (timer->task == NULL)
return;
read_lock(tasklist);
put_task_struct(timer->task)
is racy. With this patch timer->task modified and accounted only under
timer->it_lock. Sadly, this means that dead task_struct won't be freed
until timer deleted or armed.
2. run_posix_cpu_timers() collects expired timers into local list under
tasklist + ->sighand again. That means that posix_cpu_timer_del()
should check timer->it.cpu.firing under these locks too.
Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel/posix-cpu-timers.c')
-rw-r--r-- | kernel/posix-cpu-timers.c | 29 |
1 files changed, 10 insertions, 19 deletions
diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index d30b304a3384..30ab39a27736 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c | |||
@@ -380,14 +380,9 @@ int posix_cpu_timer_create(struct k_itimer *new_timer) | |||
380 | int posix_cpu_timer_del(struct k_itimer *timer) | 380 | int posix_cpu_timer_del(struct k_itimer *timer) |
381 | { | 381 | { |
382 | struct task_struct *p = timer->it.cpu.task; | 382 | struct task_struct *p = timer->it.cpu.task; |
383 | int ret = 0; | ||
383 | 384 | ||
384 | if (timer->it.cpu.firing) | 385 | if (likely(p != NULL)) { |
385 | return TIMER_RETRY; | ||
386 | |||
387 | if (unlikely(p == NULL)) | ||
388 | return 0; | ||
389 | |||
390 | if (!list_empty(&timer->it.cpu.entry)) { | ||
391 | read_lock(&tasklist_lock); | 386 | read_lock(&tasklist_lock); |
392 | if (unlikely(p->signal == NULL)) { | 387 | if (unlikely(p->signal == NULL)) { |
393 | /* | 388 | /* |
@@ -396,18 +391,20 @@ int posix_cpu_timer_del(struct k_itimer *timer) | |||
396 | */ | 391 | */ |
397 | BUG_ON(!list_empty(&timer->it.cpu.entry)); | 392 | BUG_ON(!list_empty(&timer->it.cpu.entry)); |
398 | } else { | 393 | } else { |
399 | /* | ||
400 | * Take us off the task's timer list. | ||
401 | */ | ||
402 | spin_lock(&p->sighand->siglock); | 394 | spin_lock(&p->sighand->siglock); |
403 | list_del(&timer->it.cpu.entry); | 395 | if (timer->it.cpu.firing) |
396 | ret = TIMER_RETRY; | ||
397 | else | ||
398 | list_del(&timer->it.cpu.entry); | ||
404 | spin_unlock(&p->sighand->siglock); | 399 | spin_unlock(&p->sighand->siglock); |
405 | } | 400 | } |
406 | read_unlock(&tasklist_lock); | 401 | read_unlock(&tasklist_lock); |
402 | |||
403 | if (!ret) | ||
404 | put_task_struct(p); | ||
407 | } | 405 | } |
408 | put_task_struct(p); | ||
409 | 406 | ||
410 | return 0; | 407 | return ret; |
411 | } | 408 | } |
412 | 409 | ||
413 | /* | 410 | /* |
@@ -424,8 +421,6 @@ static void cleanup_timers(struct list_head *head, | |||
424 | cputime_t ptime = cputime_add(utime, stime); | 421 | cputime_t ptime = cputime_add(utime, stime); |
425 | 422 | ||
426 | list_for_each_entry_safe(timer, next, head, entry) { | 423 | list_for_each_entry_safe(timer, next, head, entry) { |
427 | put_task_struct(timer->task); | ||
428 | timer->task = NULL; | ||
429 | list_del_init(&timer->entry); | 424 | list_del_init(&timer->entry); |
430 | if (cputime_lt(timer->expires.cpu, ptime)) { | 425 | if (cputime_lt(timer->expires.cpu, ptime)) { |
431 | timer->expires.cpu = cputime_zero; | 426 | timer->expires.cpu = cputime_zero; |
@@ -437,8 +432,6 @@ static void cleanup_timers(struct list_head *head, | |||
437 | 432 | ||
438 | ++head; | 433 | ++head; |
439 | list_for_each_entry_safe(timer, next, head, entry) { | 434 | list_for_each_entry_safe(timer, next, head, entry) { |
440 | put_task_struct(timer->task); | ||
441 | timer->task = NULL; | ||
442 | list_del_init(&timer->entry); | 435 | list_del_init(&timer->entry); |
443 | if (cputime_lt(timer->expires.cpu, utime)) { | 436 | if (cputime_lt(timer->expires.cpu, utime)) { |
444 | timer->expires.cpu = cputime_zero; | 437 | timer->expires.cpu = cputime_zero; |
@@ -450,8 +443,6 @@ static void cleanup_timers(struct list_head *head, | |||
450 | 443 | ||
451 | ++head; | 444 | ++head; |
452 | list_for_each_entry_safe(timer, next, head, entry) { | 445 | list_for_each_entry_safe(timer, next, head, entry) { |
453 | put_task_struct(timer->task); | ||
454 | timer->task = NULL; | ||
455 | list_del_init(&timer->entry); | 446 | list_del_init(&timer->entry); |
456 | if (timer->expires.sched < sched_time) { | 447 | if (timer->expires.sched < sched_time) { |
457 | timer->expires.sched = 0; | 448 | timer->expires.sched = 0; |