Skip to content

Commit 2dc4dbf

Browse files
committed
posix-timers: Dont iterate /proc/$PID/timers with sighand:: Siglock held
The readout of /proc/$PID/timers holds sighand::siglock with interrupts disabled. That is required to protect against concurrent modifications of the task::signal::posix_timers list because the list is not RCU safe. With the conversion of the timer storage to a RCU protected hlist, this is not longer required. The only requirement is to protect the returned entry against a concurrent free, which is trivial as the timers are RCU protected. Removing the trylock of sighand::siglock is benign because the life time of task_struct::signal is bound to the life time of the task_struct itself. There are two scenarios where this matters: 1) The process is life and not about to be checkpointed 2) The process is stopped via ptrace for checkpointing #1 is a racy snapshot of the armed timers and nothing can rely on it. It's not more than debug information and it has been that way before because sighand lock is dropped when the buffer is full and the restart of the iteration might find a completely different set of timers. The task and therefore task::signal cannot be freed as timers_start() acquired a reference count via get_pid_task(). #2 the process is stopped for checkpointing so nothing can delete or create timers at this point. Neither can the process exit during the traversal. If CRIU fails to observe an exit in progress prior to the dissimination of the timers, then there are more severe problems to solve in the CRIU mechanics as they can't rely on posix timers being enabled in the first place. Therefore replace the lock acquisition with rcu_read_lock() and switch the timer storage traversal over to seq_hlist_*_rcu(). Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-by: Frederic Weisbecker <[email protected]> Link: https://lore.kernel.org/all/[email protected]
1 parent 451898e commit 2dc4dbf

File tree

1 file changed

+20
-28
lines changed

1 file changed

+20
-28
lines changed

fs/proc/base.c

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2497,11 +2497,9 @@ static const struct file_operations proc_map_files_operations = {
24972497

24982498
#if defined(CONFIG_CHECKPOINT_RESTORE) && defined(CONFIG_POSIX_TIMERS)
24992499
struct timers_private {
2500-
struct pid *pid;
2501-
struct task_struct *task;
2502-
struct sighand_struct *sighand;
2503-
struct pid_namespace *ns;
2504-
unsigned long flags;
2500+
struct pid *pid;
2501+
struct task_struct *task;
2502+
struct pid_namespace *ns;
25052503
};
25062504

25072505
static void *timers_start(struct seq_file *m, loff_t *pos)
@@ -2512,54 +2510,48 @@ static void *timers_start(struct seq_file *m, loff_t *pos)
25122510
if (!tp->task)
25132511
return ERR_PTR(-ESRCH);
25142512

2515-
tp->sighand = lock_task_sighand(tp->task, &tp->flags);
2516-
if (!tp->sighand)
2517-
return ERR_PTR(-ESRCH);
2518-
2519-
return seq_hlist_start(&tp->task->signal->posix_timers, *pos);
2513+
rcu_read_lock();
2514+
return seq_hlist_start_rcu(&tp->task->signal->posix_timers, *pos);
25202515
}
25212516

25222517
static void *timers_next(struct seq_file *m, void *v, loff_t *pos)
25232518
{
25242519
struct timers_private *tp = m->private;
2525-
return seq_hlist_next(v, &tp->task->signal->posix_timers, pos);
2520+
2521+
return seq_hlist_next_rcu(v, &tp->task->signal->posix_timers, pos);
25262522
}
25272523

25282524
static void timers_stop(struct seq_file *m, void *v)
25292525
{
25302526
struct timers_private *tp = m->private;
25312527

2532-
if (tp->sighand) {
2533-
unlock_task_sighand(tp->task, &tp->flags);
2534-
tp->sighand = NULL;
2535-
}
2536-
25372528
if (tp->task) {
25382529
put_task_struct(tp->task);
25392530
tp->task = NULL;
2531+
rcu_read_unlock();
25402532
}
25412533
}
25422534

25432535
static int show_timer(struct seq_file *m, void *v)
25442536
{
2545-
struct k_itimer *timer;
2546-
struct timers_private *tp = m->private;
2547-
int notify;
25482537
static const char * const nstr[] = {
2549-
[SIGEV_SIGNAL] = "signal",
2550-
[SIGEV_NONE] = "none",
2551-
[SIGEV_THREAD] = "thread",
2538+
[SIGEV_SIGNAL] = "signal",
2539+
[SIGEV_NONE] = "none",
2540+
[SIGEV_THREAD] = "thread",
25522541
};
25532542

2554-
timer = hlist_entry((struct hlist_node *)v, struct k_itimer, list);
2555-
notify = timer->it_sigev_notify;
2543+
struct k_itimer *timer = hlist_entry((struct hlist_node *)v, struct k_itimer, list);
2544+
struct timers_private *tp = m->private;
2545+
int notify = timer->it_sigev_notify;
2546+
2547+
guard(spinlock_irq)(&timer->it_lock);
2548+
if (!posixtimer_valid(timer))
2549+
return 0;
25562550

25572551
seq_printf(m, "ID: %d\n", timer->it_id);
2558-
seq_printf(m, "signal: %d/%px\n",
2559-
timer->sigq.info.si_signo,
2552+
seq_printf(m, "signal: %d/%px\n", timer->sigq.info.si_signo,
25602553
timer->sigq.info.si_value.sival_ptr);
2561-
seq_printf(m, "notify: %s/%s.%d\n",
2562-
nstr[notify & ~SIGEV_THREAD_ID],
2554+
seq_printf(m, "notify: %s/%s.%d\n", nstr[notify & ~SIGEV_THREAD_ID],
25632555
(notify & SIGEV_THREAD_ID) ? "tid" : "pid",
25642556
pid_nr_ns(timer->it_pid, tp->ns));
25652557
seq_printf(m, "ClockID: %d\n", timer->it_clock);

0 commit comments

Comments
 (0)