From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ACF881F0E28 for ; Sun, 2 Mar 2025 19:37:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=193.142.43.55 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740944232; cv=none; b=hP2HCKH+6JiAO1+mw9WLwURwEXazr2cPRAxlKSmcDNUBy5fIqeASLGTJwk9+zVSfO/8T2sBNUXqPk222rQUW3npegaOVbbTyHWGuAoqlV/dPRsmRgy3XcPjxNPy9nvV7sseIiUwj804Xm2AMatAlN+7eOrLHKUfi62PoNGtIkaw= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740944232; c=relaxed/simple; bh=d3Y3ODPrIY1jp+7CJvmqQz5BLSj4jQPlqtFgLsgPGqQ=; h=Message-ID:From:To:Cc:Subject:References:MIME-Version: Content-Type:Date; b=ZbTyCbXVWtTTGv1xWoJCTAtSS4TlMpa2DrqcJ1kqc5ylCefvyggaCwK4+UNoDORwWlCthnbUpN4uLEACuIBWKS3/cdQiLD2dzEM/BnInxtQjzqdrhXyy22rP/H4kYYGcv+baU48dqFV3ojNSnMTKrzgjmapsr12gnJrytSdd2Js= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de; spf=pass smtp.mailfrom=linutronix.de; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=QJUIbIcC; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=1Jf0Rqto; arc=none smtp.client-ip=193.142.43.55 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linutronix.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="QJUIbIcC"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="1Jf0Rqto" Message-ID: <20250302193627.854190870@linutronix.de> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1740944229; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: references:references; bh=jGU5P5nY7M5LQXxbj27aqdU32TjBwj9UsBnUCn2J8+Q=; b=QJUIbIcCkcYFCcNorkv/sWN5FfYXIsyRIqh2ZyxKMXhA0mRCo7l0PnDBraNj306nMcve2J WKVX130BUKNyWF5Y4HlM70CPFXxIuLLqN4nCOvuuaPbZAH+WRD4JjUjGRRoPuO5O3z4Ont /MOSClOKtyKl3D/RdwI4Dxm8NnOQ2XPhuATEgsFS2FrIi6ZLFFqHaIstd/KE/Wh1HyG8pv I81+F+RwRt9kS59zjqlV305uNxq0B4coVrHEwbLJmSZVqSuYdwJDutS+KtgA3ui7bjK649 1ZbvUEIwlfJWiZ89Pu9gQonH/orcHfXqVlZ/KAbLGxZJjGfVh5cV6VracIcOwA== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1740944229; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: references:references; bh=jGU5P5nY7M5LQXxbj27aqdU32TjBwj9UsBnUCn2J8+Q=; b=1Jf0RqtoBNFwCyKFDKX7BWHxZI7bZm4jIk987KzLFOK3Vyi15lYXM3gecK5zayGn/LHlrP gogYOnsruUF5k9Cg== From: Thomas Gleixner To: LKML Cc: Anna-Maria Behnsen , Frederic Weisbecker , Benjamin Segall , Eric Dumazet , Andrey Vagin , Pavel Tikhomirov , Peter Zijlstra Subject: [patch V2 15/17] posix-timers: Dont iterate /proc/$PID/timers with sighand::siglock held References: <20250302185753.311903554@linutronix.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Date: Sun, 2 Mar 2025 20:37:08 +0100 (CET) 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 --- fs/proc/base.c | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2497,11 +2497,9 @@ static const struct file_operations proc #if defined(CONFIG_CHECKPOINT_RESTORE) && defined(CONFIG_POSIX_TIMERS) struct timers_private { - struct pid *pid; - struct task_struct *task; - struct sighand_struct *sighand; - struct pid_namespace *ns; - unsigned long flags; + struct pid *pid; + struct task_struct *task; + struct pid_namespace *ns; }; static void *timers_start(struct seq_file *m, loff_t *pos) @@ -2512,54 +2510,48 @@ static void *timers_start(struct seq_fil if (!tp->task) return ERR_PTR(-ESRCH); - tp->sighand = lock_task_sighand(tp->task, &tp->flags); - if (!tp->sighand) - return ERR_PTR(-ESRCH); - - return seq_hlist_start(&tp->task->signal->posix_timers, *pos); + rcu_read_lock(); + return seq_hlist_start_rcu(&tp->task->signal->posix_timers, *pos); } static void *timers_next(struct seq_file *m, void *v, loff_t *pos) { struct timers_private *tp = m->private; - return seq_hlist_next(v, &tp->task->signal->posix_timers, pos); + + return seq_hlist_next_rcu(v, &tp->task->signal->posix_timers, pos); } static void timers_stop(struct seq_file *m, void *v) { struct timers_private *tp = m->private; - if (tp->sighand) { - unlock_task_sighand(tp->task, &tp->flags); - tp->sighand = NULL; - } - if (tp->task) { put_task_struct(tp->task); tp->task = NULL; + rcu_read_unlock(); } } static int show_timer(struct seq_file *m, void *v) { - struct k_itimer *timer; - struct timers_private *tp = m->private; - int notify; static const char * const nstr[] = { - [SIGEV_SIGNAL] = "signal", - [SIGEV_NONE] = "none", - [SIGEV_THREAD] = "thread", + [SIGEV_SIGNAL] = "signal", + [SIGEV_NONE] = "none", + [SIGEV_THREAD] = "thread", }; - timer = hlist_entry((struct hlist_node *)v, struct k_itimer, list); - notify = timer->it_sigev_notify; + struct k_itimer *timer = hlist_entry((struct hlist_node *)v, struct k_itimer, list); + struct timers_private *tp = m->private; + int notify = timer->it_sigev_notify; + + guard(spinlock_irq)(&timer->it_lock); + if (!posixtimer_valid(timer)) + return 0; seq_printf(m, "ID: %d\n", timer->it_id); - seq_printf(m, "signal: %d/%px\n", - timer->sigq.info.si_signo, + seq_printf(m, "signal: %d/%px\n", timer->sigq.info.si_signo, timer->sigq.info.si_value.sival_ptr); - seq_printf(m, "notify: %s/%s.%d\n", - nstr[notify & ~SIGEV_THREAD_ID], + seq_printf(m, "notify: %s/%s.%d\n", nstr[notify & ~SIGEV_THREAD_ID], (notify & SIGEV_THREAD_ID) ? "tid" : "pid", pid_nr_ns(timer->it_pid, tp->ns)); seq_printf(m, "ClockID: %d\n", timer->it_clock);