Skip to content

Commit 1d25bdd

Browse files
committed
posix-timers: Rework timer removal
sys_timer_delete() and the do_exit() cleanup function itimer_delete() are doing the same thing, but have needlessly different implementations instead of sharing the code. The other oddity of timer deletion is the fact that the timer is not invalidated before the actual deletion happens, which allows concurrent lookups to succeed. That's wrong because a timer which is in the process of being deleted should not be visible and any actions like signal queueing, delivery and rearming should not happen once the task, which invoked timer_delete(), has the timer locked. Rework the code so that: 1) The signal queueing and delivery code ignore timers which are marked invalid 2) The deletion implementation between sys_timer_delete() and itimer_delete() is shared 3) The timer is invalidated and removed from the linked lists before the deletion callback of the relevant clock is invoked. That requires to rework timer_wait_running() as it does a lookup of the timer when relocking it at the end. In case of deletion this lookup would fail due to the preceding invalidation and the wait loop would terminate prematurely. But due to the preceding invalidation the timer cannot be accessed by other tasks anymore, so there is no way that the timer has been freed after the timer lock has been dropped. Move the re-validation out of timer_wait_running() and handle it at the only other usage site, timer_settime(). Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-by: Frederic Weisbecker <[email protected]> Link: https://lore.kernel.org/all/87zfht1exf.ffs@tglx
1 parent 50f53b2 commit 1d25bdd

File tree

3 files changed

+90
-113
lines changed

3 files changed

+90
-113
lines changed

include/linux/posix-timers.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,13 @@ static inline void posixtimer_sigqueue_putref(struct sigqueue *q)
240240

241241
posixtimer_putref(tmr);
242242
}
243+
244+
static inline bool posixtimer_valid(const struct k_itimer *timer)
245+
{
246+
unsigned long val = (unsigned long)timer->it_signal;
247+
248+
return !(val & 0x1UL);
249+
}
243250
#else /* CONFIG_POSIX_TIMERS */
244251
static inline void posixtimer_sigqueue_getref(struct sigqueue *q) { }
245252
static inline void posixtimer_sigqueue_putref(struct sigqueue *q) { }

kernel/signal.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2092,7 +2092,7 @@ static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueu
20922092
* from a non-periodic timer, then just drop the reference
20932093
* count. Otherwise queue it on the ignored list.
20942094
*/
2095-
if (tmr->it_signal && tmr->it_sig_periodic)
2095+
if (posixtimer_valid(tmr) && tmr->it_sig_periodic)
20962096
hlist_add_head(&tmr->ignored_list, &tsk->signal->ignored_posix_timers);
20972097
else
20982098
posixtimer_putref(tmr);

kernel/time/posix-timers.c

Lines changed: 82 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ static bool __posixtimer_deliver_signal(struct kernel_siginfo *info, struct k_it
279279
* since the signal was queued. In either case, don't rearm and
280280
* drop the signal.
281281
*/
282-
if (timr->it_signal_seq != timr->it_sigqueue_seq || WARN_ON_ONCE(!timr->it_signal))
282+
if (timr->it_signal_seq != timr->it_sigqueue_seq || WARN_ON_ONCE(!posixtimer_valid(timr)))
283283
return false;
284284

285285
if (!timr->it_interval || WARN_ON_ONCE(timr->it_status != POSIX_TIMER_REQUEUE_PENDING))
@@ -324,6 +324,9 @@ void posix_timer_queue_signal(struct k_itimer *timr)
324324
{
325325
lockdep_assert_held(&timr->it_lock);
326326

327+
if (!posixtimer_valid(timr))
328+
return;
329+
327330
timr->it_status = timr->it_interval ? POSIX_TIMER_REQUEUE_PENDING : POSIX_TIMER_DISARMED;
328331
posixtimer_send_sigqueue(timr);
329332
}
@@ -553,11 +556,11 @@ static struct k_itimer *__lock_timer(timer_t timer_id)
553556
* The hash lookup and the timers are RCU protected.
554557
*
555558
* Timers are added to the hash in invalid state where
556-
* timr::it_signal == NULL. timer::it_signal is only set after the
557-
* rest of the initialization succeeded.
559+
* timr::it_signal is marked invalid. timer::it_signal is only set
560+
* after the rest of the initialization succeeded.
558561
*
559562
* Timer destruction happens in steps:
560-
* 1) Set timr::it_signal to NULL with timr::it_lock held
563+
* 1) Set timr::it_signal marked invalid with timr::it_lock held
561564
* 2) Release timr::it_lock
562565
* 3) Remove from the hash under hash_lock
563566
* 4) Put the reference count.
@@ -574,8 +577,8 @@ static struct k_itimer *__lock_timer(timer_t timer_id)
574577
*
575578
* The lookup validates locklessly that timr::it_signal ==
576579
* current::it_signal and timr::it_id == @timer_id. timr::it_id
577-
* can't change, but timr::it_signal becomes NULL during
578-
* destruction.
580+
* can't change, but timr::it_signal can become invalid during
581+
* destruction, which makes the locked check fail.
579582
*/
580583
guard(rcu)();
581584
timr = posix_timer_by_id(timer_id);
@@ -811,22 +814,13 @@ static void common_timer_wait_running(struct k_itimer *timer)
811814
* when the task which tries to delete or disarm the timer has preempted
812815
* the task which runs the expiry in task work context.
813816
*/
814-
static struct k_itimer *timer_wait_running(struct k_itimer *timer)
817+
static void timer_wait_running(struct k_itimer *timer)
815818
{
816-
timer_t timer_id = READ_ONCE(timer->it_id);
817-
818-
/* Prevent kfree(timer) after dropping the lock */
819-
scoped_guard (rcu) {
820-
unlock_timer(timer);
821-
/*
822-
* kc->timer_wait_running() might drop RCU lock. So @timer
823-
* cannot be touched anymore after the function returns!
824-
*/
825-
timer->kclock->timer_wait_running(timer);
826-
}
827-
828-
/* Relock the timer. It might be not longer hashed. */
829-
return lock_timer(timer_id);
819+
/*
820+
* kc->timer_wait_running() might drop RCU lock. So @timer
821+
* cannot be touched anymore after the function returns!
822+
*/
823+
timer->kclock->timer_wait_running(timer);
830824
}
831825

832826
/*
@@ -885,8 +879,7 @@ static int do_timer_settime(timer_t timer_id, int tmr_flags,
885879
struct itimerspec64 *new_spec64,
886880
struct itimerspec64 *old_spec64)
887881
{
888-
struct k_itimer *timr;
889-
int error;
882+
int ret;
890883

891884
if (!timespec64_valid(&new_spec64->it_interval) ||
892885
!timespec64_valid(&new_spec64->it_value))
@@ -895,29 +888,36 @@ static int do_timer_settime(timer_t timer_id, int tmr_flags,
895888
if (old_spec64)
896889
memset(old_spec64, 0, sizeof(*old_spec64));
897890

898-
timr = lock_timer(timer_id);
899-
retry:
900-
if (!timr)
901-
return -EINVAL;
891+
for (;;) {
892+
struct k_itimer *timr = lock_timer(timer_id);
902893

903-
if (old_spec64)
904-
old_spec64->it_interval = ktime_to_timespec64(timr->it_interval);
894+
if (!timr)
895+
return -EINVAL;
896+
897+
if (old_spec64)
898+
old_spec64->it_interval = ktime_to_timespec64(timr->it_interval);
905899

906-
/* Prevent signal delivery and rearming. */
907-
timr->it_signal_seq++;
900+
/* Prevent signal delivery and rearming. */
901+
timr->it_signal_seq++;
908902

909-
error = timr->kclock->timer_set(timr, tmr_flags, new_spec64, old_spec64);
903+
ret = timr->kclock->timer_set(timr, tmr_flags, new_spec64, old_spec64);
904+
if (ret != TIMER_RETRY) {
905+
unlock_timer(timr);
906+
break;
907+
}
910908

911-
if (error == TIMER_RETRY) {
912-
// We already got the old time...
909+
/* Read the old time only once */
913910
old_spec64 = NULL;
914-
/* Unlocks and relocks the timer if it still exists */
915-
timr = timer_wait_running(timr);
916-
goto retry;
911+
/* Protect the timer from being freed after the lock is dropped */
912+
guard(rcu)();
913+
unlock_timer(timr);
914+
/*
915+
* timer_wait_running() might drop RCU read side protection
916+
* so the timer has to be looked up again!
917+
*/
918+
timer_wait_running(timr);
917919
}
918-
unlock_timer(timr);
919-
920-
return error;
920+
return ret;
921921
}
922922

923923
/* Set a POSIX.1b interval timer */
@@ -988,90 +988,56 @@ static inline void posix_timer_cleanup_ignored(struct k_itimer *tmr)
988988
}
989989
}
990990

991-
/* Delete a POSIX.1b interval timer. */
992-
SYSCALL_DEFINE1(timer_delete, timer_t, timer_id)
991+
static void posix_timer_delete(struct k_itimer *timer)
993992
{
994-
struct k_itimer *timer = lock_timer(timer_id);
995-
996-
retry_delete:
997-
if (!timer)
998-
return -EINVAL;
999-
1000-
/* Prevent signal delivery and rearming. */
993+
/*
994+
* Invalidate the timer, remove it from the linked list and remove
995+
* it from the ignored list if pending.
996+
*
997+
* The invalidation must be written with siglock held so that the
998+
* signal code observes the invalidated timer::it_signal in
999+
* do_sigaction(), which prevents it from moving a pending signal
1000+
* of a deleted timer to the ignore list.
1001+
*
1002+
* The invalidation also prevents signal queueing, signal delivery
1003+
* and therefore rearming from the signal delivery path.
1004+
*
1005+
* A concurrent lookup can still find the timer in the hash, but it
1006+
* will check timer::it_signal with timer::it_lock held and observe
1007+
* bit 0 set, which invalidates it. That also prevents the timer ID
1008+
* from being handed out before this timer is completely gone.
1009+
*/
10011010
timer->it_signal_seq++;
10021011

1003-
if (unlikely(timer->kclock->timer_del(timer) == TIMER_RETRY)) {
1004-
/* Unlocks and relocks the timer if it still exists */
1005-
timer = timer_wait_running(timer);
1006-
goto retry_delete;
1007-
}
1008-
10091012
scoped_guard (spinlock, &current->sighand->siglock) {
1013+
unsigned long sig = (unsigned long)timer->it_signal | 1UL;
1014+
1015+
WRITE_ONCE(timer->it_signal, (struct signal_struct *)sig);
10101016
hlist_del(&timer->list);
10111017
posix_timer_cleanup_ignored(timer);
1012-
/*
1013-
* A concurrent lookup could check timer::it_signal lockless. It
1014-
* will reevaluate with timer::it_lock held and observe the NULL.
1015-
*
1016-
* It must be written with siglock held so that the signal code
1017-
* observes timer->it_signal == NULL in do_sigaction(SIG_IGN),
1018-
* which prevents it from moving a pending signal of a deleted
1019-
* timer to the ignore list.
1020-
*/
1021-
WRITE_ONCE(timer->it_signal, NULL);
10221018
}
10231019

1024-
unlock_timer(timer);
1025-
posix_timer_unhash_and_free(timer);
1026-
return 0;
1020+
while (timer->kclock->timer_del(timer) == TIMER_RETRY) {
1021+
guard(rcu)();
1022+
spin_unlock_irq(&timer->it_lock);
1023+
timer_wait_running(timer);
1024+
spin_lock_irq(&timer->it_lock);
1025+
}
10271026
}
10281027

1029-
/*
1030-
* Delete a timer if it is armed, remove it from the hash and schedule it
1031-
* for RCU freeing.
1032-
*/
1033-
static void itimer_delete(struct k_itimer *timer)
1028+
/* Delete a POSIX.1b interval timer. */
1029+
SYSCALL_DEFINE1(timer_delete, timer_t, timer_id)
10341030
{
1035-
spin_lock_irq(&timer->it_lock);
1036-
1037-
retry_delete:
1038-
/*
1039-
* Even if the timer is not longer accessible from other tasks
1040-
* it still might be armed and queued in the underlying timer
1041-
* mechanism. Worse, that timer mechanism might run the expiry
1042-
* function concurrently.
1043-
*/
1044-
if (timer->kclock->timer_del(timer) == TIMER_RETRY) {
1045-
/*
1046-
* Timer is expired concurrently, prevent livelocks
1047-
* and pointless spinning on RT.
1048-
*
1049-
* timer_wait_running() drops timer::it_lock, which opens
1050-
* the possibility for another task to delete the timer.
1051-
*
1052-
* That's not possible here because this is invoked from
1053-
* do_exit() only for the last thread of the thread group.
1054-
* So no other task can access and delete that timer.
1055-
*/
1056-
if (WARN_ON_ONCE(timer_wait_running(timer) != timer))
1057-
return;
1058-
1059-
goto retry_delete;
1060-
}
1061-
hlist_del(&timer->list);
1062-
1063-
posix_timer_cleanup_ignored(timer);
1031+
struct k_itimer *timer = lock_timer(timer_id);
10641032

1065-
/*
1066-
* Setting timer::it_signal to NULL is technically not required
1067-
* here as nothing can access the timer anymore legitimately via
1068-
* the hash table. Set it to NULL nevertheless so that all deletion
1069-
* paths are consistent.
1070-
*/
1071-
WRITE_ONCE(timer->it_signal, NULL);
1033+
if (!timer)
1034+
return -EINVAL;
10721035

1073-
spin_unlock_irq(&timer->it_lock);
1036+
posix_timer_delete(timer);
1037+
unlock_timer(timer);
1038+
/* Remove it from the hash, which frees up the timer ID */
10741039
posix_timer_unhash_and_free(timer);
1040+
return 0;
10751041
}
10761042

10771043
/*
@@ -1082,6 +1048,8 @@ static void itimer_delete(struct k_itimer *timer)
10821048
void exit_itimers(struct task_struct *tsk)
10831049
{
10841050
struct hlist_head timers;
1051+
struct hlist_node *next;
1052+
struct k_itimer *timer;
10851053

10861054
if (hlist_empty(&tsk->signal->posix_timers))
10871055
return;
@@ -1091,8 +1059,10 @@ void exit_itimers(struct task_struct *tsk)
10911059
hlist_move_list(&tsk->signal->posix_timers, &timers);
10921060

10931061
/* The timers are not longer accessible via tsk::signal */
1094-
while (!hlist_empty(&timers)) {
1095-
itimer_delete(hlist_entry(timers.first, struct k_itimer, list));
1062+
hlist_for_each_entry_safe(timer, next, &timers, list) {
1063+
scoped_guard (spinlock_irq, &timer->it_lock)
1064+
posix_timer_delete(timer);
1065+
posix_timer_unhash_and_free(timer);
10961066
cond_resched();
10971067
}
10981068

0 commit comments

Comments
 (0)