Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

[PATCH] Fix cpu timers exit deadlock and races

Oleg Nesterov reported an SMP deadlock. If there is a running timer
tracking a different process's CPU time clock when the process owning
the timer exits, we deadlock on tasklist_lock in posix_cpu_timer_del via
exit_itimers.

That code was using tasklist_lock to check for a race with __exit_signal
being called on the timer-target task and clearing its ->signal.
However, there is actually no such race. __exit_signal will have called
posix_cpu_timers_exit and posix_cpu_timers_exit_group before it does
that. Those will clear those k_itimer's association with the dying
task, so posix_cpu_timer_del will return early and never reach the code
in question.

In addition, posix_cpu_timer_del called from exit_itimers during execve
or directly from timer_delete in the process owning the timer can race
with an exiting timer-target task to cause a double put on timer-target
task struct. Make sure we always access cpu_timers lists with sighand
lock held.

Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Chris Wright <chrisw@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Roland McGrath and committed by
Linus Torvalds
e03d13e9 3359b54c

+11 -17
+11 -17
kernel/posix-cpu-timers.c
··· 387 387 if (unlikely(p == NULL)) 388 388 return 0; 389 389 390 + spin_lock(&p->sighand->siglock); 390 391 if (!list_empty(&timer->it.cpu.entry)) { 391 - read_lock(&tasklist_lock); 392 - if (unlikely(p->signal == NULL)) { 393 - /* 394 - * We raced with the reaping of the task. 395 - * The deletion should have cleared us off the list. 396 - */ 397 - BUG_ON(!list_empty(&timer->it.cpu.entry)); 398 - } else { 399 - /* 400 - * Take us off the task's timer list. 401 - */ 402 - spin_lock(&p->sighand->siglock); 403 - list_del(&timer->it.cpu.entry); 404 - spin_unlock(&p->sighand->siglock); 405 - } 406 - read_unlock(&tasklist_lock); 392 + /* 393 + * Take us off the task's timer list. We don't need to 394 + * take tasklist_lock and check for the task being reaped. 395 + * If it was reaped, it already called posix_cpu_timers_exit 396 + * and posix_cpu_timers_exit_group to clear all the timers 397 + * that pointed to it. 398 + */ 399 + list_del(&timer->it.cpu.entry); 400 + put_task_struct(p); 407 401 } 408 - put_task_struct(p); 402 + spin_unlock(&p->sighand->siglock); 409 403 410 404 return 0; 411 405 }