]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
posix-timers: Ensure that timer initialization is fully visible
authorThomas Gleixner <tglx@linutronix.de>
Sat, 8 Mar 2025 16:48:10 +0000 (17:48 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 13 Mar 2025 11:07:16 +0000 (12:07 +0100)
Frederic pointed out that the memory operations to initialize the timer are
not guaranteed to be visible, when __lock_timer() observes timer::it_signal
valid under timer::it_lock:

  T0                                      T1
  ---------                               -----------
  do_timer_create()
      // A
      new_timer->.... = ....
      spin_lock(current->sighand)
      // B
      WRITE_ONCE(new_timer->it_signal, current->signal)
      spin_unlock(current->sighand)
sys_timer_*()
   t =  __lock_timer()
  spin_lock(&timr->it_lock)
  // observes B
  if (timr->it_signal == current->signal)
    return timr;
                   if (!t)
       return;
// Is not guaranteed to observe A

Protect the write of timer::it_signal, which makes the timer valid, with
timer::it_lock as well. This guarantees that T1 must observe the
initialization A completely, when it observes the valid signal pointer
under timer::it_lock. sighand::siglock must still be taken to protect the
signal::posix_timers list.

Reported-by: Frederic Weisbecker <frederic@kernel.org>
Suggested-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Link: https://lore.kernel.org/all/20250308155623.507944489@linutronix.de
kernel/time/posix-timers.c

index 44ba7db07e9001b728893df3e9eb13103e5a6403..6bf468b16ad65893d96ed3efa900483a4b309283 100644 (file)
@@ -462,14 +462,21 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
        if (error)
                goto out;
 
-       spin_lock_irq(&current->sighand->siglock);
-       /* This makes the timer valid in the hash table */
-       WRITE_ONCE(new_timer->it_signal, current->signal);
-       hlist_add_head(&new_timer->list, &current->signal->posix_timers);
-       spin_unlock_irq(&current->sighand->siglock);
        /*
-        * After unlocking sighand::siglock @new_timer is subject to
-        * concurrent removal and cannot be touched anymore
+        * timer::it_lock ensures that __lock_timer() observes a fully
+        * initialized timer when it observes a valid timer::it_signal.
+        *
+        * sighand::siglock is required to protect signal::posix_timers.
+        */
+       scoped_guard (spinlock_irq, &new_timer->it_lock) {
+               guard(spinlock)(&current->sighand->siglock);
+               /* This makes the timer valid in the hash table */
+               WRITE_ONCE(new_timer->it_signal, current->signal);
+               hlist_add_head(&new_timer->list, &current->signal->posix_timers);
+       }
+       /*
+        * After unlocking @new_timer is subject to concurrent removal and
+        * cannot be touched anymore
         */
        return 0;
 out: