public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Thomas Gleixner <tglx@linutronix.de>
To: LKML <linux-kernel@vger.kernel.org>
Cc: Anna-Maria Behnsen <anna-maria@linutronix.de>,
	Frederic Weisbecker <frederic@kernel.org>,
	John Stultz <jstultz@google.com>,
	Peter Zijlstra <peterz@infradead.org>,
	Ingo Molnar <mingo@kernel.org>, Stephen Boyd <sboyd@kernel.org>,
	Eric Biederman <ebiederm@xmission.com>,
	Oleg Nesterov <oleg@redhat.com>
Subject: [patch V3 42/51] posix-timers: Embed sigqueue in struct k_itimer
Date: Mon, 10 Jun 2024 18:42:57 +0200 (CEST)	[thread overview]
Message-ID: <20240610164028.109697550@linutronix.de> (raw)
In-Reply-To: 20240610163452.591699700@linutronix.de

To cure the SIG_IGN handling for posix interval timers, the preallocated
sigqueue needs to be embedded into struct k_itimer to prevent life time
races of all sorts.

Now that the prerequisites are in place, embed the sigqueue into struct
k_itimer and fixup the relevant usage sites.

Aside of preparing for proper SIG_IGN handling, this spares an extra
allocation.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 fs/proc/base.c               |    4 +-
 include/linux/posix-timers.h |   23 +++++++++++++++-
 kernel/signal.c              |   12 +++++++-
 kernel/time/posix-timers.c   |   59 ++++++++++++++++++++++++++-----------------
 4 files changed, 69 insertions(+), 29 deletions(-)

--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2496,8 +2496,8 @@ static int show_timer(struct seq_file *m
 
 	seq_printf(m, "ID: %d\n", timer->it_id);
 	seq_printf(m, "signal: %d/%px\n",
-		   timer->sigq->info.si_signo,
-		   timer->sigq->info.si_value.sival_ptr);
+		   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],
 		   (notify & SIGEV_THREAD_ID) ? "tid" : "pid",
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -39,6 +39,8 @@ static inline int clockid_to_fd(const cl
 
 #ifdef CONFIG_POSIX_TIMERS
 
+#include <linux/signal_types.h>
+
 /**
  * cpu_timer - Posix CPU timer representation for k_itimer
  * @node:	timerqueue node to queue in the task/sig
@@ -165,7 +167,7 @@ static inline void posix_cputimers_init_
  * @it_pid:		The pid of the process/task targeted by the signal
  * @it_process:		The task to wakeup on clock_nanosleep (CPU timers)
  * @rcuref:		Reference count for life time management
- * @sigq:		Pointer to preallocated sigqueue
+ * @sigq:		Embedded sigqueue
  * @it:			Union representing the various posix timer type
  *			internals.
  * @rcu:		RCU head for freeing the timer.
@@ -189,7 +191,7 @@ struct k_itimer {
 		struct pid		*it_pid;
 		struct task_struct	*it_process;
 	};
-	struct sigqueue		*sigq;
+	struct sigqueue		sigq;
 	rcuref_t		rcuref;
 	union {
 		struct {
@@ -217,6 +219,23 @@ static inline void posixtimer_putref(str
 	if (rcuref_put(&tmr->rcuref))
 		posixtimer_free_timer(tmr);
 }
+
+static inline void posixtimer_sigqueue_getref(struct sigqueue *q)
+{
+	struct k_itimer *tmr = container_of(q, struct k_itimer, sigq);
+
+	WARN_ON_ONCE(!rcuref_get(&tmr->rcuref));
+}
+
+static inline void posixtimer_sigqueue_putref(struct sigqueue *q)
+{
+	struct k_itimer *tmr = container_of(q, struct k_itimer, sigq);
+
+	posixtimer_putref(tmr);
+}
+#else  /* CONFIG_POSIX_TIMERS */
+static inline void posixtimer_sigqueue_getref(struct sigqueue *q) { }
+static inline void posixtimer_sigqueue_putref(struct sigqueue *q) { }
 #endif /* !CONFIG_POSIX_TIMERS */
 
 #endif
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -566,7 +566,12 @@ static void collect_signal(int sig, stru
 still_pending:
 		list_del_init(&first->list);
 		copy_siginfo(info, &first->info);
-		__sigqueue_free(first);
+		/*
+		 * Do not drop the reference count for posix timer
+		 * signals. That's done in posix_timer_deliver_signal().
+		 */
+		if (info->si_code != SI_TIMER)
+			__sigqueue_free(first);
 	} else {
 		/*
 		 * Ok, it wasn't in the queue.  This must be
@@ -1985,7 +1990,7 @@ static inline struct task_struct *posixt
 
 int posixtimer_send_sigqueue(struct k_itimer *tmr)
 {
-	struct sigqueue *q = tmr->sigq;
+	struct sigqueue *q = &tmr->sigq;
 	int sig = q->info.si_signo;
 	struct task_struct *t;
 	unsigned long flags;
@@ -2045,9 +2050,12 @@ int posixtimer_send_sigqueue(struct k_it
 
 	ret = 0;
 	if (unlikely(!list_empty(&q->list))) {
+		/* This holds a reference count already */
 		result = TRACE_SIGNAL_ALREADY_PENDING;
 		goto out;
 	}
+
+	posixtimer_sigqueue_getref(q);
 	posixtimer_queue_sigqueue(q, t, tmr->it_pid_type);
 	result = TRACE_SIGNAL_DELIVERED;
 out:
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -251,12 +251,13 @@ static void common_hrtimer_rearm(struct
 
 /*
  * This function is called from the signal delivery code. It decides
- * whether the signal should be dropped and rearms interval timers.
+ * whether the signal should be dropped and rearms interval timers.  The
+ * timer can be unconditionally accessed as there is a reference held on
+ * it.
  */
 bool posixtimer_deliver_signal(struct kernel_siginfo *info)
 {
-	struct k_itimer *timr;
-	unsigned long flags;
+	struct k_itimer *timr = info->si_sys_privptr;
 	bool ret = false;
 
 	/*
@@ -264,12 +265,14 @@ bool posixtimer_deliver_signal(struct ke
 	 * timr::it_lock. Keep interrupts disabled.
 	 */
 	spin_unlock(&current->sighand->siglock);
+	spin_lock(&timr->it_lock);
 
-	timr = lock_timer(info->si_tid, &flags);
-	if (!timr)
-		goto out;
-
-	if (timr->it_signal_seq != info->si_sys_private)
+	/*
+	 * Check if the timer is still alive or whether it got modified
+	 * since the signal was queued. In either case, don't rearm and
+	 * drop the signal.
+	 */
+	if (!timr->it_signal || timr->it_signal_seq != info->si_sys_private)
 		goto out_unlock;
 
 	if (timr->it_interval && timr->it_status == POSIX_TIMER_REQUEUE_PENDING) {
@@ -285,8 +288,10 @@ bool posixtimer_deliver_signal(struct ke
 	ret = true;
 
 out_unlock:
-	unlock_timer(timr, flags);
-out:
+	spin_unlock(&timr->it_lock);
+	/* Drop the reference which was acquired when the signal was queued */
+	posixtimer_putref(timr);
+
 	spin_lock(&current->sighand->siglock);
 
 	/* Don't expose the si_sys_priv* values to userspace */
@@ -405,17 +410,17 @@ static struct pid *good_sigevent(sigeven
 	}
 }
 
-static struct k_itimer * alloc_posix_timer(void)
+static struct k_itimer *alloc_posix_timer(void)
 {
 	struct k_itimer *tmr = kmem_cache_zalloc(posix_timers_cache, GFP_KERNEL);
 
 	if (!tmr)
 		return tmr;
-	if (unlikely(!(tmr->sigq = sigqueue_alloc()))) {
+
+	if (unlikely(!posixtimer_init_sigqueue(&tmr->sigq))) {
 		kmem_cache_free(posix_timers_cache, tmr);
 		return NULL;
 	}
-	clear_siginfo(&tmr->sigq->info);
 	rcuref_init(&tmr->rcuref, 1);
 	return tmr;
 }
@@ -430,7 +435,8 @@ static void k_itimer_rcu_free(struct rcu
 void posixtimer_free_timer(struct k_itimer *tmr)
 {
 	put_pid(tmr->it_pid);
-	sigqueue_free(tmr->sigq);
+	if (tmr->sigq.ucounts)
+		dec_rlimit_put_ucounts(tmr->sigq.ucounts, UCOUNT_RLIMIT_SIGPENDING);
 	call_rcu(&tmr->rcu, k_itimer_rcu_free);
 }
 
@@ -492,13 +498,13 @@ static int do_timer_create(clockid_t whi
 			goto out;
 		}
 		new_timer->it_sigev_notify     = event->sigev_notify;
-		new_timer->sigq->info.si_signo = event->sigev_signo;
-		new_timer->sigq->info.si_value = event->sigev_value;
+		new_timer->sigq.info.si_signo = event->sigev_signo;
+		new_timer->sigq.info.si_value = event->sigev_value;
 	} else {
 		new_timer->it_sigev_notify     = SIGEV_SIGNAL;
-		new_timer->sigq->info.si_signo = SIGALRM;
-		memset(&new_timer->sigq->info.si_value, 0, sizeof(sigval_t));
-		new_timer->sigq->info.si_value.sival_int = new_timer->it_id;
+		new_timer->sigq.info.si_signo = SIGALRM;
+		memset(&new_timer->sigq.info.si_value, 0, sizeof(sigval_t));
+		new_timer->sigq.info.si_value.sival_int = new_timer->it_id;
 		new_timer->it_pid = get_pid(task_tgid(current));
 	}
 
@@ -507,9 +513,9 @@ static int do_timer_create(clockid_t whi
 	else
 		new_timer->it_pid_type = PIDTYPE_TGID;
 
-	new_timer->sigq->info.si_tid   = new_timer->it_id;
-	new_timer->sigq->info.si_code  = SI_TIMER;
-	new_timer->sigq->info.si_sys_privptr  = new_timer;
+	new_timer->sigq.info.si_tid = new_timer->it_id;
+	new_timer->sigq.info.si_code = SI_TIMER;
+	new_timer->sigq.info.si_sys_privptr = new_timer;
 
 	if (copy_to_user(created_timer_id, &new_timer_id, sizeof (new_timer_id))) {
 		error = -EFAULT;
@@ -593,7 +599,14 @@ static struct k_itimer *__lock_timer(tim
 	 *  1) Set timr::it_signal to NULL with timr::it_lock held
 	 *  2) Release timr::it_lock
 	 *  3) Remove from the hash under hash_lock
-	 *  4) Call RCU for removal after the grace period
+	 *  4) Put the reference count.
+	 *
+	 * The reference count might not drop to zero if timr::sigq is
+	 * queued. In that case the signal delivery or flush will put the
+	 * last reference count.
+	 *
+	 * When the reference count reaches zero, the timer is scheduled
+	 * for RCU removal after the grace period.
 	 *
 	 * Holding rcu_read_lock() accross the lookup ensures that
 	 * the timer cannot be freed.


  parent reply	other threads:[~2024-06-10 16:43 UTC|newest]

Thread overview: 71+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-06-10 16:42 [patch V3 00/51] posix-timers: Cure inconsistencies and the SIG_IGN mess Thomas Gleixner
2024-06-10 16:42 ` [patch V3 01/51] selftests/timers/posix_timers: Simplify error handling Thomas Gleixner
2024-06-10 16:42 ` [patch V3 02/51] selftests/timers/posix_timers: Add SIG_IGN test Thomas Gleixner
2024-06-10 16:42 ` [patch V3 03/51] selftests/timers/posix_timers: Validate signal rules Thomas Gleixner
2024-06-10 16:42 ` [patch V3 04/51] selftests/timers/posix-timers: Validate SIGEV_NONE Thomas Gleixner
2024-06-10 16:42 ` [patch V3 05/51] selftests/timers/posix-timers: Validate timer_gettime() Thomas Gleixner
2024-06-10 16:42 ` [patch V3 06/51] selftests/timers/posix-timers: Validate overrun after unblock Thomas Gleixner
2024-06-10 16:42 ` [patch V3 07/51] posix-cpu-timers: Split up posix_cpu_timer_get() Thomas Gleixner
2024-06-21 15:28   ` Frederic Weisbecker
2024-06-10 16:42 ` [patch V3 08/51] posix-cpu-timers: Save interval only for armed timers Thomas Gleixner
2024-06-21 15:33   ` Frederic Weisbecker
2024-06-10 16:42 ` [patch V3 09/51] posix-cpu-timers: Handle interval timers correctly in timer_get() Thomas Gleixner
2024-06-22  9:04   ` Frederic Weisbecker
2024-06-10 16:42 ` [patch V3 10/51] posix-cpu-timers: Handle SIGEV_NONE " Thomas Gleixner
2024-06-22 14:28   ` Frederic Weisbecker
2024-06-10 16:42 ` [patch V3 11/51] posix-cpu-timers: Handle SIGEV_NONE timers correctly in timer_set() Thomas Gleixner
2024-06-22 14:35   ` Frederic Weisbecker
2024-06-22 21:56     ` Thomas Gleixner
2024-06-23 11:16   ` [patch V3-2 " Thomas Gleixner
2024-06-23 19:12     ` Frederic Weisbecker
2024-06-10 16:42 ` [patch V3 12/51] posix-cpu-timers: Replace old expiry retrieval in posix_cpu_timer_set() Thomas Gleixner
2024-06-23 11:17   ` [patch V3-2 " Thomas Gleixner
2024-06-23 20:23     ` Frederic Weisbecker
2024-06-10 16:42 ` [patch V3 13/51] posix-cpu-timers: Do not arm SIGEV_NONE timers Thomas Gleixner
2024-06-23 21:04   ` Frederic Weisbecker
2024-06-10 16:42 ` [patch V3 14/51] posix-cpu-timers: Use @now instead of @val for clarity Thomas Gleixner
2024-06-10 16:42 ` [patch V3 15/51] posix-cpu-timers: Remove incorrect comment in posix_cpu_timer_set() Thomas Gleixner
2024-06-10 16:42 ` [patch V3 16/51] posix-cpu-timers: Simplify posix_cpu_timer_set() Thomas Gleixner
2024-06-23 22:41   ` Frederic Weisbecker
2024-06-10 16:42 ` [patch V3 17/51] posix-timers: Retrieve interval in common timer_settime() code Thomas Gleixner
2024-06-25 15:13   ` Frederic Weisbecker
2024-06-10 16:42 ` [patch V3 18/51] posix-timers: Clear overrun in common_timer_set() Thomas Gleixner
2024-06-10 16:42 ` [patch V3 19/51] posix-timers: Convert timer list to hlist Thomas Gleixner
2024-06-10 16:42 ` [patch V3 20/51] posix-timers: Consolidate timer setup Thomas Gleixner
2024-06-25 22:19   ` Frederic Weisbecker
2024-06-10 16:42 ` [patch V3 21/51] posix-cpu-timers: Make k_itimer::it_active consistent Thomas Gleixner
2024-06-25 22:36   ` Frederic Weisbecker
2024-06-10 16:42 ` [patch V3 22/51] posix-timers: Consolidate signal queueing Thomas Gleixner
2024-06-10 16:42 ` [patch V3 23/51] signal: Remove task argument from dequeue_signal() Thomas Gleixner
2024-06-10 16:42 ` [patch V3 24/51] signal: Replace BUG_ON()s Thomas Gleixner
2024-06-10 16:42 ` [patch V3 25/51] signal: Confine POSIX_TIMERS properly Thomas Gleixner
2024-06-10 16:42 ` [patch V3 26/51] signal: Prevent user space from setting si_sys_private Thomas Gleixner
2024-06-10 16:42 ` [patch V3 27/51] signal: Get rid of resched_timer logic Thomas Gleixner
2024-06-10 16:42 ` [patch V3 28/51] posix-timers: Cure si_sys_private race Thomas Gleixner
2024-06-10 16:42 ` [patch V3 29/51] signal: Allow POSIX timer signals to be dropped Thomas Gleixner
2024-06-10 16:42 ` [patch V3 30/51] posix-timers: Drop signal if timer has been deleted or reprogrammed Thomas Gleixner
2024-06-10 16:42 ` [patch V3 31/51] posix-timers: Rename k_itimer::it_requeue_pending Thomas Gleixner
2024-06-10 16:42 ` [patch V3 32/51] posix-timers: Add proper state tracking Thomas Gleixner
2024-06-10 16:42 ` [patch V3 33/51] posix-timers: Make signal delivery consistent Thomas Gleixner
2024-06-10 16:42 ` [patch V3 34/51] posix-timers: Make signal overrun accounting sensible Thomas Gleixner
2024-06-10 16:42 ` [patch V3 35/51] posix-cpu-timers: Use dedicated flag for CPU timer nanosleep Thomas Gleixner
2024-06-10 16:42 ` [patch V3 36/51] posix-timers: Add a refcount to struct k_itimer Thomas Gleixner
2024-06-10 16:42 ` [patch V3 37/51] signal: Split up __sigqueue_alloc() Thomas Gleixner
2024-06-10 16:42 ` [patch V3 38/51] signal: Provide posixtimer_sigqueue_init() Thomas Gleixner
2024-06-10 16:42 ` [patch V3 39/51] signal: Add sys_private_ptr to siginfo::_sifields::_timer Thomas Gleixner
2024-06-23 11:17   ` Thomas Gleixner
2024-06-10 16:42 ` [patch V3 40/51] posix-timers: Store PID type in the timer Thomas Gleixner
2024-06-10 16:42 ` [patch V3 41/51] signal: Refactor send_sigqueue() Thomas Gleixner
2024-06-10 16:42 ` Thomas Gleixner [this message]
2024-06-10 16:42 ` [patch V3 43/51] signal: Cleanup unused posix-timer leftovers Thomas Gleixner
2024-06-10 16:42 ` [patch V3 44/51] signal: Add task argument to flush_sigqueue_mask() Thomas Gleixner
2024-06-10 16:43 ` [patch V3 45/51] signal: Provide ignored_posix_timers list Thomas Gleixner
2024-06-10 16:43 ` [patch V3 46/51] posix-timers: Handle ignored list on delete and exit Thomas Gleixner
2024-06-10 16:43 ` [patch V3 47/51] signal: Handle ignored signals in do_sigaction(action != SIG_IGN) Thomas Gleixner
2024-06-10 16:43 ` [patch V3 48/51] signal: Queue ignored posixtimers on ignore list Thomas Gleixner
2024-06-10 16:43 ` [patch V3 49/51] posix-timers: Cleanup SIG_IGN workaround leftovers Thomas Gleixner
2024-06-10 16:43 ` [patch V3 50/51] alarmtimers: Remove the throttle mechanism from alarm_forward_now() Thomas Gleixner
2024-06-10 16:43 ` [patch V3 51/51] alarmtimers: Remove return value from alarm functions Thomas Gleixner
2024-06-10 19:49 ` [patch V3 00/51] posix-timers: Cure inconsistencies and the SIG_IGN mess Peter Zijlstra
2024-06-11  6:58 ` Thomas Gleixner
2024-06-23 11:24   ` Thomas Gleixner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240610164028.109697550@linutronix.de \
    --to=tglx@linutronix.de \
    --cc=anna-maria@linutronix.de \
    --cc=ebiederm@xmission.com \
    --cc=frederic@kernel.org \
    --cc=jstultz@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@kernel.org \
    --cc=oleg@redhat.com \
    --cc=peterz@infradead.org \
    --cc=sboyd@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox