[SRU][F][PATCH 2/9] timers: Don't block on ->expiry_lock for TIMER_IRQSAFE timers
Massimiliano Pellizzer
massimiliano.pellizzer at canonical.com
Wed Nov 13 16:18:21 UTC 2024
From: Sebastian Andrzej Siewior <bigeasy at linutronix.de>
PREEMPT_RT does not spin and wait until a running timer completes its
callback but instead it blocks on a sleeping lock to prevent a livelock in
the case that the task waiting for the callback completion preempted the
callback.
This cannot be done for timers flagged with TIMER_IRQSAFE. These timers can
be canceled from an interrupt disabled context even on RT kernels.
The expiry callback of such timers is invoked with interrupts disabled so
there is no need to use the expiry lock mechanism because obviously the
callback cannot be preempted even on RT kernels.
Do not use the timer_base::expiry_lock mechanism when waiting for a running
callback to complete if the timer is flagged with TIMER_IRQSAFE.
Also add a lockdep assertion for RT kernels to validate that the expiry
lock mechanism is always invoked in preemptible context.
Reported-by: Mike Galbraith <efault at gmx.de>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy at linutronix.de>
Signed-off-by: Thomas Gleixner <tglx at linutronix.de>
Link: https://lore.kernel.org/r/20201103190937.hga67rqhvknki3tp@linutronix.de
(cherry picked from commit c725dafc95f1b37027840aaeaa8b7e4e9cd20516)
CVE-2024-35887
Signed-off-by: Massimiliano Pellizzer <massimiliano.pellizzer at canonical.com>
---
kernel/time/timer.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 4fe9b36d136a..6e8d80dd32a3 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -1320,7 +1320,7 @@ static void del_timer_wait_running(struct timer_list *timer)
u32 tf;
tf = READ_ONCE(timer->flags);
- if (!(tf & TIMER_MIGRATING)) {
+ if (!(tf & (TIMER_MIGRATING | TIMER_IRQSAFE))) {
struct timer_base *base = get_timer_base(tf);
/*
@@ -1406,6 +1406,13 @@ int timer_delete_sync(struct timer_list *timer)
*/
WARN_ON(in_irq() && !(timer->flags & TIMER_IRQSAFE));
+ /*
+ * Must be able to sleep on PREEMPT_RT because of the slowpath in
+ * del_timer_wait_running().
+ */
+ if (IS_ENABLED(CONFIG_PREEMPT_RT) && !(timer->flags & TIMER_IRQSAFE))
+ lockdep_assert_preemption_enabled();
+
do {
ret = try_to_del_timer_sync(timer);
--
2.43.0
More information about the kernel-team
mailing list