[06/20] posix-timers: Annotate concurrent access to k_itimer::it_signal

Message ID 20230425183313.143596887@linutronix.de
State New
Headers
Series posix-timers: Fixes and cleanups |

Commit Message

Thomas Gleixner April 25, 2023, 6:49 p.m. UTC
  k_itimer::it_signal is read lockless in the RCU protected hash lookup, but
it can be written concurrently in the timer_create() and timer_delete()
path. Annotate these places with READ_ONCE() and WRITE_ONCE()

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 kernel/time/posix-timers.c |   14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
  

Comments

Frederic Weisbecker May 9, 2023, 11:04 a.m. UTC | #1
On Tue, Apr 25, 2023 at 08:49:05PM +0200, Thomas Gleixner wrote:
> k_itimer::it_signal is read lockless in the RCU protected hash lookup, but
> it can be written concurrently in the timer_create() and timer_delete()
> path. Annotate these places with READ_ONCE() and WRITE_ONCE()
> 
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
  

Patch

--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -109,9 +109,9 @@  static struct k_itimer *__posix_timers_f
 {
 	struct k_itimer *timer;
 
-	hlist_for_each_entry_rcu(timer, head, t_hash,
-				 lockdep_is_held(&hash_lock)) {
-		if ((timer->it_signal == sig) && (timer->it_id == id))
+	hlist_for_each_entry_rcu(timer, head, t_hash, lockdep_is_held(&hash_lock)) {
+		/* timer->it_signal can be set concurrently */
+		if ((READ_ONCE(timer->it_signal) == sig) && (timer->it_id == id))
 			return timer;
 	}
 	return NULL;
@@ -557,7 +557,7 @@  static int do_timer_create(clockid_t whi
 
 	spin_lock_irq(&current->sighand->siglock);
 	/* This makes the timer valid in the hash table */
-	new_timer->it_signal = current->signal;
+	WRITE_ONCE(new_timer->it_signal, current->signal);
 	list_add(&new_timer->list, &current->signal->posix_timers);
 	spin_unlock_irq(&current->sighand->siglock);
 
@@ -1051,10 +1051,10 @@  SYSCALL_DEFINE1(timer_delete, timer_t, t
 	list_del(&timer->list);
 	spin_unlock(&current->sighand->siglock);
 	/*
-	 * This keeps any tasks waiting on the spin lock from thinking
-	 * they got something (see the lock code above).
+	 * A concurrent lookup could check timer::it_signal lockless. It
+	 * will reevaluate with timer::it_lock held and observe the NULL.
 	 */
-	timer->it_signal = NULL;
+	WRITE_ONCE(timer->it_signal, NULL);
 
 	unlock_timer(timer, flags);
 	release_posix_timer(timer, IT_ID_SET);