+++ /dev/null
-From aad70180e7c856978814bb3ca066df392cbfef15 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Mon, 27 Jul 2026 11:13:08 +0200
-Subject: posix-cpu-timers: Prevent UAF caused by non-leader exec() race
-
-From: Thomas Gleixner <tglx@kernel.org>
-
-commit 920f893f735e92ba3a1cd9256899a186b161928d upstream.
-
-Wongi and Jungwoo decoded and reported a non-leader exec() related race
-which can result in an UAF:
-
- sys_timer_delete() exec()
- posix_cpu_timer_del()
- // Observes old leader
- p = pid_task(pid, pid_type); de_thread()
- switch_leader();
- release_task(old_leader)
- __exit_signal(old_leader)
- sighand = lock(old_leader, sighand);
- posix_cpu_timers*_exit();
- sighand = lock_task_sighand(p) unhash_task(old_leader);
- sh = lock(p, sighand) old_leader->sighand = NULL;
- unlock(sighand);
- (p->sighand == NULL)
- unlock(sh)
- return NULL;
-
- // Returns without action
- if(!sighand)
- return 0;
- free_posix_timer();
-
-This is "harmless" unless the deleted timer was armed and enqueued in
-p->signal because on exec() a TGID targeted timer is inherited.
-
-As sys_timer_delete() freed the underlying posix timer object
-run_posix_cpu_timers() or any timerqueue related add/delete operations on
-other timers will access the freed object's timerqueue node, which results
-in an UAF.
-
-There is a similar problem vs. posix_cpu_timer_set(). For regular posix
-timers it just transiently returns -ESRCH to user space, but for the use
-case in do_cpu_nanosleep() it's the same UAF just that the k_itimer is
-allocated on the stack.
-
-Also posix_cpu_timer_rearm() fails to rearm the timer, which means it stops
-to expire.
-
-While debating solutions Frederic pointed out another problem:
-
- posix_cpu_timer_del(tmr)
- __exit_signal(p)
- posix_cpu_timers*_exit(p);
- unhash_task(p);
- p->sighand = NULL;
- sh = lock_task_sighand(p)
- sighand = p->sighand;
- if (!sighand)
- return NULL;
- lock(sighand);
-
- if (!sh)
- WARN_ON_ONCE(timer_queued(tmr));
-
-On weakly ordered architectures it is not guaranteed that
-posix_cpu_timer_del() will observe the stores in posix_cpu_timers*_exit()
-when p->sighand is observed as NULL, which means the WARN() can be a false
-positive.
-
-Solve these issues by:
-
- 1) Changing the store in __exit_signal() to smp_store_release().
-
- 2) Adding a smp_acquire__after_ctrl_dep() into the !sighand path
- of lock_task_sighand().
-
- 3) Creating a helper function for looking up the task and locking sighand
- which does not return when sighand == NULL. Instead it retries the
- task lookup and only if that fails it gives up.
-
- 4) Using that helper in the three affected functions.
-
-#1/#2 ensures that the reader side which observes sighand == NULL also
-observes all preceeding stores, i.e. the stores in posix_cpu_timers*_exit()
-and the ones in unhash_task().
-
-#3 ensures that the above described non-leader exec() situation is handled
-gracefully. When the task lookup returns the old leader, but sighand ==
-NULL then it retries. In the non-leader exec() case the subsequent task
-lookup will observe the new leader due to #1/#2. In normal exit() scenarios
-the subsequent lookup fails.
-
-When the task lookup fails, the function also checks whether the timer is
-still enqueued and issues a warning if that's the case. Unfortunately there
-is nothing which can be done about it, but as the task is already not
-longer visible the timer should not be accessed anymore. This check also
-requires memory ordering, which is not provided when the first lookup
-fails. To achieve that the check is preceeded by a smp_rmb() which pairs
-with the smp_wmb() in write_seqlock() in __exit_signal(). That ensures that
-the stores in posix_cpu_timers*_exit() are visible.
-
-The history of the non-leader exec() issue goes back to the early days of
-posix CPU timers, which stored a pointer to the group leader task in the
-timer. That obviously fails when a non-leader exec() switches the leader.
-commit e0a70217107e ("posix-cpu-timers: workaround to suppress the problems
-with mt exec") added a temporary workaround for that in 2010 which survived
-about 10 years. The fix for the workaround changed the task pointer to a
-pid pointer, but failed to see the subtle race described above. So the
-Fixes tag picks that commit, which seems to be halfways accurate.
-
-Thanks to Frederic Weissbecker, Oleg Nesterov and Peter Zijlstra for
-review, feedback and suggestions and to Wongi and Jungwoo for the excellent
-bug report and analysis!
-
-Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task")
-Reported-by: Wongi Lee <qw3rtyp0@gmail.com>
-Reported-by: Jungwoo Lee <jwlee2217@gmail.com>
-Signed-off-by: Thomas Gleixner <tglx@kernel.org>
-Reviewed-by: Oleg Nesterov <oleg@redhat.com>
-Cc: stable@vger.kernel.org
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- kernel/exit.c | 8 +-
- kernel/signal.c | 10 +-
- kernel/time/posix-cpu-timers.c | 185 +++++++++++++++++++++------------
- 3 files changed, 137 insertions(+), 66 deletions(-)
-
-diff --git a/kernel/exit.c b/kernel/exit.c
-index 99ab6d7a09cee6..675a0f4c89e19b 100644
---- a/kernel/exit.c
-+++ b/kernel/exit.c
-@@ -200,7 +200,13 @@ static void __exit_signal(struct task_struct *tsk)
- * doing sigqueue_free() if we have SIGQUEUE_PREALLOC signals.
- */
- flush_sigqueue(&tsk->pending);
-- tsk->sighand = NULL;
-+
-+ /*
-+ * Ensure that all preceeding state is visible. Pairs with
-+ * the smp_acquire__after_ctrl_dep() in the sighand == NULL
-+ * path of lock_task_sighand().
-+ */
-+ smp_store_release(&tsk->sighand, NULL);
- spin_unlock(&sighand->siglock);
-
- __cleanup_sighand(sighand);
-diff --git a/kernel/signal.c b/kernel/signal.c
-index 463b798651b6a6..e0fb44e57a3166 100644
---- a/kernel/signal.c
-+++ b/kernel/signal.c
-@@ -1375,8 +1375,16 @@ struct sighand_struct *__lock_task_sighand(struct task_struct *tsk,
- rcu_read_lock();
- for (;;) {
- sighand = rcu_dereference(tsk->sighand);
-- if (unlikely(sighand == NULL))
-+ if (unlikely(sighand == NULL)) {
-+ /*
-+ * Pairs with the smp_store_release() in
-+ * __exit_signal(). It ensures that all state
-+ * modifications to the task preceeding the store are
-+ * visible to the callers of lock_task_sighand().
-+ */
-+ smp_acquire__after_ctrl_dep();
- break;
-+ }
-
- /*
- * This sighand can be already freed and even reused, but
-diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
-index ded4a0edaa04e9..7cf6f083a54968 100644
---- a/kernel/time/posix-cpu-timers.c
-+++ b/kernel/time/posix-cpu-timers.c
-@@ -405,6 +405,110 @@ static int posix_cpu_timer_create(struct k_itimer *new_timer)
- return 0;
- }
-
-+/*
-+ * Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand.
-+ *
-+ * This can race with the reaping of the task:
-+ *
-+ * CPU0 CPU1
-+ *
-+ * // Finds task
-+ * p = pid_task(pid, pid_type); __exit_signal(p)
-+ * lock(p, sighand);
-+ * posix_cpu_timers*_exit();
-+ * sighand = lock_task_sighand(p); unhash_task(p);
-+ * p->sighand = NULL;
-+ * unlock(sighand);
-+ *
-+ * In this case sighand is NULL, which means the task and the associated timer
-+ * queue cannot be longer accessed safely.
-+ *
-+ * __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is
-+ * dead it also invokes posix_cpu_timers_group_exit(). These functions delete
-+ * all pending timers from the related timer queues. The POSIX timers (k_itimer)
-+ * themself are still accessible, but not longer connected to the task.
-+ *
-+ * exec() works slightly differently. The task which exec()'s terminates all
-+ * other threads in the thread group and runs __exit_signal() on them. As the
-+ * thread group is not dead they only clean up the per task timers via
-+ * posix_cpu_timers_exit().
-+ *
-+ * As the TGID on exec() stays the same per process timers stay queued, if they
-+ * are armed. This works without a problem when exec() is done by the thread
-+ * group leader. If a non-leader thread exec()'s this can end up in the
-+ * following scenario:
-+ *
-+ * CPU0 CPU1
-+ * // Returns old leader
-+ * p = pid_task(pid, pid_type); de_thread()
-+ * switch_leader()
-+ * release_task(old leader)
-+ * __exit_signal()
-+ * old_leader->sighand = NULL;
-+ * // Returns NULL
-+ * sighand = lock_task_sighand(p)
-+ *
-+ * That's problematic for several functions:
-+ *
-+ * - posix_cpu_timer_del(): If the timer is still enqueued on the task the
-+ * underlying k_itimer will be freed which results in a UAF in
-+ * run_posix_cpu_timers() or on timerqueue related add/delete operations.
-+ * If the timer is not enqueued, the failure is harmless
-+ *
-+ * - posix_cpu_timer_set(): Independent of the enqueued state that results in a
-+ * transient failure which is user space visible (-ESRCH) for regular posix
-+ * timers. But for the use case in do_cpu_nanosleep() it's the same UAF
-+ * problem just that the timer is allocated on the stack.
-+ *
-+ * - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this
-+ * silently ignores the rearm request, which is a functional problem as the
-+ * timer wont expire anymore.
-+ */
-+static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags)
-+{
-+ enum pid_type type = clock_pid_type(timer->it_clock);
-+ struct cpu_timer *ctmr = &timer->it.cpu;
-+
-+ guard(rcu)();
-+
-+ for (;;) {
-+ struct task_struct *t = pid_task(timer->it.cpu.pid, type);
-+
-+ /* Fail if the task cannot be found. */
-+ if (!t)
-+ break;
-+
-+ /* Try to lock the task's sighand */
-+ if (lock_task_sighand(t, flags))
-+ return t;
-+
-+ /*
-+ * The next PID lookup might either fail or return the new
-+ * leader. This is correct for both exit() and exec().
-+ */
-+ }
-+
-+ /*
-+ * If the timer is still enqueued, warn. There is nothing safe to do
-+ * here as there might be two timers in there which are removed in
-+ * parallel and that will cause more damage than good. This should never
-+ * happen!
-+ *
-+ * Ensure that the stores to the timer and timerqueue are visible:
-+ *
-+ * __exit_signal()
-+ * posix_cpu_timers*_exit()
-+ * write_seqlock(seqlock)
-+ * smp_wmb(); <-------
-+ * __unhash_process() | !pid_task()
-+ * ----> smp_rmb();
-+ * WARN_ON_ONCE(...)
-+ */
-+ smp_rmb();
-+ WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
-+ return NULL;
-+}
-+
- /*
- * Clean up a CPU-clock timer that is about to be destroyed.
- * This is called from timer deletion with the timer already locked.
-@@ -414,28 +518,13 @@ static int posix_cpu_timer_create(struct k_itimer *new_timer)
- static int posix_cpu_timer_del(struct k_itimer *timer)
- {
- struct cpu_timer *ctmr = &timer->it.cpu;
-- struct sighand_struct *sighand;
- struct task_struct *p;
- unsigned long flags;
- int ret = 0;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p)
-- goto out;
-+ p = timer_lock_sighand(timer, &flags);
-
-- /*
-- * Protect against sighand release/switch in exit/exec and process/
-- * thread timer list entry concurrent read/writes.
-- */
-- sighand = lock_task_sighand(p, &flags);
-- if (unlikely(sighand == NULL)) {
-- /*
-- * This raced with the reaping of the task. The exit cleanup
-- * should have removed this timer from the timer queue.
-- */
-- WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
-- } else {
-+ if (likely(p)) {
- if (timer->it.cpu.firing)
- ret = TIMER_RETRY;
- else
-@@ -444,8 +533,6 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
- unlock_task_sighand(p, &flags);
- }
-
--out:
-- rcu_read_unlock();
- if (!ret)
- put_pid(ctmr->pid);
-
-@@ -575,21 +662,17 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
- u64 old_expires, new_expires, old_incr, val;
- struct cpu_timer *ctmr = &timer->it.cpu;
-- struct sighand_struct *sighand;
- struct task_struct *p;
- unsigned long flags;
- int ret = 0;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p) {
-- /*
-- * If p has just been reaped, we can no
-- * longer get any information about it at all.
-- */
-- rcu_read_unlock();
-+ p = timer_lock_sighand(timer, &flags);
-+ /*
-+ * If p has just been reaped, we can no longer get any information about
-+ * it at all.
-+ */
-+ if (!p)
- return -ESRCH;
-- }
-
- /*
- * Use the to_ktime conversion because that clamps the maximum
-@@ -597,20 +680,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- */
- new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value));
-
-- /*
-- * Protect against sighand release/switch in exit/exec and p->cpu_timers
-- * and p->signal->cpu_timers read/write in arm_timer()
-- */
-- sighand = lock_task_sighand(p, &flags);
-- /*
-- * If p has just been reaped, we can no
-- * longer get any information about it at all.
-- */
-- if (unlikely(sighand == NULL)) {
-- rcu_read_unlock();
-- return -ESRCH;
-- }
--
- /*
- * Disarm any old timer after extracting its expiry time.
- */
-@@ -659,6 +728,8 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- old->it_value.tv_sec = 0;
- }
- }
-+
-+ old->it_interval = ns_to_timespec64(old_incr);
- }
-
- if (unlikely(ret)) {
-@@ -669,7 +740,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- * it as an overrun (thanks to bump_cpu_timer above).
- */
- unlock_task_sighand(p, &flags);
-- goto out;
-+ return ret;
- }
-
- if (new_expires != 0 && !(timer_flags & TIMER_ABSTIME)) {
-@@ -686,7 +757,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- arm_timer(timer, p);
- }
-
-- unlock_task_sighand(p, &flags);
- /*
- * Install the new reload setting, and
- * set up the signal and overrun bookkeeping.
-@@ -703,6 +773,8 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- timer->it_overrun_last = 0;
- timer->it_overrun = -1;
-
-+ unlock_task_sighand(p, &flags);
-+
- if (new_expires != 0 && !(val < new_expires)) {
- /*
- * The designated time already passed, so we notify
-@@ -712,13 +784,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- cpu_timer_fire(timer);
- }
-
-- ret = 0;
-- out:
-- rcu_read_unlock();
-- if (old)
-- old->it_interval = ns_to_timespec64(old_incr);
--
-- return ret;
-+ return 0;
- }
-
- static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp)
-@@ -984,19 +1050,12 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
- {
- clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
- struct task_struct *p;
-- struct sighand_struct *sighand;
- unsigned long flags;
- u64 now;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p)
-- goto out;
--
-- /* Protect timer list r/w in arm_timer() */
-- sighand = lock_task_sighand(p, &flags);
-- if (unlikely(sighand == NULL))
-- goto out;
-+ p = timer_lock_sighand(timer, &flags);
-+ if (unlikely(!p))
-+ return;
-
- /*
- * Fetch the current sample and update the timer's expiry time.
-@@ -1013,8 +1072,6 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
- */
- arm_timer(timer, p);
- unlock_task_sighand(p, &flags);
--out:
-- rcu_read_unlock();
- }
-
- /**
---
-2.53.0
-
usb-serial-ftdi_sio-add-support-for-e-h-fxa291.patch
usb-serial-io_edgeport-cap-received-transmit-credits.patch
usb-serial-option-add-tdtech-mt5710-cn.patch
-posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch
bpf-fix-ld_-abs-ind-failure-path-analysis-in-subprog.patch
usb-xhci-pci-limit-via-vl805-dma-addressing-to-36-bi.patch
+++ /dev/null
-From 09486fef145315e0221c496ec7af5fc66c785357 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Mon, 27 Jul 2026 11:10:14 +0200
-Subject: posix-cpu-timers: Prevent UAF caused by non-leader exec() race
-
-From: Thomas Gleixner <tglx@kernel.org>
-
-commit 920f893f735e92ba3a1cd9256899a186b161928d upstream.
-
-Wongi and Jungwoo decoded and reported a non-leader exec() related race
-which can result in an UAF:
-
- sys_timer_delete() exec()
- posix_cpu_timer_del()
- // Observes old leader
- p = pid_task(pid, pid_type); de_thread()
- switch_leader();
- release_task(old_leader)
- __exit_signal(old_leader)
- sighand = lock(old_leader, sighand);
- posix_cpu_timers*_exit();
- sighand = lock_task_sighand(p) unhash_task(old_leader);
- sh = lock(p, sighand) old_leader->sighand = NULL;
- unlock(sighand);
- (p->sighand == NULL)
- unlock(sh)
- return NULL;
-
- // Returns without action
- if(!sighand)
- return 0;
- free_posix_timer();
-
-This is "harmless" unless the deleted timer was armed and enqueued in
-p->signal because on exec() a TGID targeted timer is inherited.
-
-As sys_timer_delete() freed the underlying posix timer object
-run_posix_cpu_timers() or any timerqueue related add/delete operations on
-other timers will access the freed object's timerqueue node, which results
-in an UAF.
-
-There is a similar problem vs. posix_cpu_timer_set(). For regular posix
-timers it just transiently returns -ESRCH to user space, but for the use
-case in do_cpu_nanosleep() it's the same UAF just that the k_itimer is
-allocated on the stack.
-
-Also posix_cpu_timer_rearm() fails to rearm the timer, which means it stops
-to expire.
-
-While debating solutions Frederic pointed out another problem:
-
- posix_cpu_timer_del(tmr)
- __exit_signal(p)
- posix_cpu_timers*_exit(p);
- unhash_task(p);
- p->sighand = NULL;
- sh = lock_task_sighand(p)
- sighand = p->sighand;
- if (!sighand)
- return NULL;
- lock(sighand);
-
- if (!sh)
- WARN_ON_ONCE(timer_queued(tmr));
-
-On weakly ordered architectures it is not guaranteed that
-posix_cpu_timer_del() will observe the stores in posix_cpu_timers*_exit()
-when p->sighand is observed as NULL, which means the WARN() can be a false
-positive.
-
-Solve these issues by:
-
- 1) Changing the store in __exit_signal() to smp_store_release().
-
- 2) Adding a smp_acquire__after_ctrl_dep() into the !sighand path
- of lock_task_sighand().
-
- 3) Creating a helper function for looking up the task and locking sighand
- which does not return when sighand == NULL. Instead it retries the
- task lookup and only if that fails it gives up.
-
- 4) Using that helper in the three affected functions.
-
-#1/#2 ensures that the reader side which observes sighand == NULL also
-observes all preceeding stores, i.e. the stores in posix_cpu_timers*_exit()
-and the ones in unhash_task().
-
-#3 ensures that the above described non-leader exec() situation is handled
-gracefully. When the task lookup returns the old leader, but sighand ==
-NULL then it retries. In the non-leader exec() case the subsequent task
-lookup will observe the new leader due to #1/#2. In normal exit() scenarios
-the subsequent lookup fails.
-
-When the task lookup fails, the function also checks whether the timer is
-still enqueued and issues a warning if that's the case. Unfortunately there
-is nothing which can be done about it, but as the task is already not
-longer visible the timer should not be accessed anymore. This check also
-requires memory ordering, which is not provided when the first lookup
-fails. To achieve that the check is preceeded by a smp_rmb() which pairs
-with the smp_wmb() in write_seqlock() in __exit_signal(). That ensures that
-the stores in posix_cpu_timers*_exit() are visible.
-
-The history of the non-leader exec() issue goes back to the early days of
-posix CPU timers, which stored a pointer to the group leader task in the
-timer. That obviously fails when a non-leader exec() switches the leader.
-commit e0a70217107e ("posix-cpu-timers: workaround to suppress the problems
-with mt exec") added a temporary workaround for that in 2010 which survived
-about 10 years. The fix for the workaround changed the task pointer to a
-pid pointer, but failed to see the subtle race described above. So the
-Fixes tag picks that commit, which seems to be halfways accurate.
-
-Thanks to Frederic Weissbecker, Oleg Nesterov and Peter Zijlstra for
-review, feedback and suggestions and to Wongi and Jungwoo for the excellent
-bug report and analysis!
-
-Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task")
-Reported-by: Wongi Lee <qw3rtyp0@gmail.com>
-Reported-by: Jungwoo Lee <jwlee2217@gmail.com>
-Signed-off-by: Thomas Gleixner <tglx@kernel.org>
-Reviewed-by: Oleg Nesterov <oleg@redhat.com>
-Cc: stable@vger.kernel.org
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- kernel/exit.c | 8 +-
- kernel/signal.c | 10 +-
- kernel/time/posix-cpu-timers.c | 212 ++++++++++++++++++++-------------
- 3 files changed, 143 insertions(+), 87 deletions(-)
-
-diff --git a/kernel/exit.c b/kernel/exit.c
-index 3c6ddf7320fae6..776994c28887a6 100644
---- a/kernel/exit.c
-+++ b/kernel/exit.c
-@@ -200,7 +200,13 @@ static void __exit_signal(struct task_struct *tsk)
- * doing sigqueue_free() if we have SIGQUEUE_PREALLOC signals.
- */
- flush_sigqueue(&tsk->pending);
-- tsk->sighand = NULL;
-+
-+ /*
-+ * Ensure that all preceeding state is visible. Pairs with
-+ * the smp_acquire__after_ctrl_dep() in the sighand == NULL
-+ * path of lock_task_sighand().
-+ */
-+ smp_store_release(&tsk->sighand, NULL);
- spin_unlock(&sighand->siglock);
-
- __cleanup_sighand(sighand);
-diff --git a/kernel/signal.c b/kernel/signal.c
-index 10a315e461d34b..662377d23c95d0 100644
---- a/kernel/signal.c
-+++ b/kernel/signal.c
-@@ -1388,8 +1388,16 @@ struct sighand_struct *__lock_task_sighand(struct task_struct *tsk,
- rcu_read_lock();
- for (;;) {
- sighand = rcu_dereference(tsk->sighand);
-- if (unlikely(sighand == NULL))
-+ if (unlikely(sighand == NULL)) {
-+ /*
-+ * Pairs with the smp_store_release() in
-+ * __exit_signal(). It ensures that all state
-+ * modifications to the task preceeding the store are
-+ * visible to the callers of lock_task_sighand().
-+ */
-+ smp_acquire__after_ctrl_dep();
- break;
-+ }
-
- /*
- * This sighand can be already freed and even reused, but
-diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
-index c99e11da83e85e..52deada50891ab 100644
---- a/kernel/time/posix-cpu-timers.c
-+++ b/kernel/time/posix-cpu-timers.c
-@@ -455,6 +455,109 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
- trigger_base_recalc_expires(timer, p);
- }
-
-+/*
-+ * Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand.
-+ *
-+ * This can race with the reaping of the task:
-+ *
-+ * CPU0 CPU1
-+ *
-+ * // Finds task
-+ * p = pid_task(pid, pid_type); __exit_signal(p)
-+ * lock(p, sighand);
-+ * posix_cpu_timers*_exit();
-+ * sighand = lock_task_sighand(p); unhash_task(p);
-+ * p->sighand = NULL;
-+ * unlock(sighand);
-+ *
-+ * In this case sighand is NULL, which means the task and the associated timer
-+ * queue cannot be longer accessed safely.
-+ *
-+ * __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is
-+ * dead it also invokes posix_cpu_timers_group_exit(). These functions delete
-+ * all pending timers from the related timer queues. The POSIX timers (k_itimer)
-+ * themself are still accessible, but not longer connected to the task.
-+ *
-+ * exec() works slightly differently. The task which exec()'s terminates all
-+ * other threads in the thread group and runs __exit_signal() on them. As the
-+ * thread group is not dead they only clean up the per task timers via
-+ * posix_cpu_timers_exit().
-+ *
-+ * As the TGID on exec() stays the same per process timers stay queued, if they
-+ * are armed. This works without a problem when exec() is done by the thread
-+ * group leader. If a non-leader thread exec()'s this can end up in the
-+ * following scenario:
-+ *
-+ * CPU0 CPU1
-+ * // Returns old leader
-+ * p = pid_task(pid, pid_type); de_thread()
-+ * switch_leader()
-+ * release_task(old leader)
-+ * __exit_signal()
-+ * old_leader->sighand = NULL;
-+ * // Returns NULL
-+ * sighand = lock_task_sighand(p)
-+ *
-+ * That's problematic for several functions:
-+ *
-+ * - posix_cpu_timer_del(): If the timer is still enqueued on the task the
-+ * underlying k_itimer will be freed which results in a UAF in
-+ * run_posix_cpu_timers() or on timerqueue related add/delete operations.
-+ * If the timer is not enqueued, the failure is harmless
-+ *
-+ * - posix_cpu_timer_set(): Independent of the enqueued state that results in a
-+ * transient failure which is user space visible (-ESRCH) for regular posix
-+ * timers. But for the use case in do_cpu_nanosleep() it's the same UAF
-+ * problem just that the timer is allocated on the stack.
-+ *
-+ * - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this
-+ * silently ignores the rearm request, which is a functional problem as the
-+ * timer wont expire anymore.
-+ */
-+static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags)
-+{
-+ enum pid_type type = clock_pid_type(timer->it_clock);
-+ struct cpu_timer *ctmr = &timer->it.cpu;
-+
-+ guard(rcu)();
-+
-+ for (;;) {
-+ struct task_struct *t = pid_task(timer->it.cpu.pid, type);
-+
-+ /* Fail if the task cannot be found. */
-+ if (!t)
-+ break;
-+
-+ /* Try to lock the task's sighand */
-+ if (lock_task_sighand(t, flags))
-+ return t;
-+
-+ /*
-+ * The next PID lookup might either fail or return the new
-+ * leader. This is correct for both exit() and exec().
-+ */
-+ }
-+
-+ /*
-+ * If the timer is still enqueued, warn. There is nothing safe to do
-+ * here as there might be two timers in there which are removed in
-+ * parallel and that will cause more damage than good. This should never
-+ * happen!
-+ *
-+ * Ensure that the stores to the timer and timerqueue are visible:
-+ *
-+ * __exit_signal()
-+ * posix_cpu_timers*_exit()
-+ * write_seqlock(seqlock)
-+ * smp_wmb(); <-------
-+ * __unhash_process() | !pid_task()
-+ * ----> smp_rmb();
-+ * WARN_ON_ONCE(...)
-+ */
-+ smp_rmb();
-+ WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
-+ return NULL;
-+}
-
- /*
- * Clean up a CPU-clock timer that is about to be destroyed.
-@@ -464,29 +567,13 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
- */
- static int posix_cpu_timer_del(struct k_itimer *timer)
- {
-- struct cpu_timer *ctmr = &timer->it.cpu;
-- struct sighand_struct *sighand;
- struct task_struct *p;
- unsigned long flags;
- int ret = 0;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p)
-- goto out;
-+ p = timer_lock_sighand(timer, &flags);
-
-- /*
-- * Protect against sighand release/switch in exit/exec and process/
-- * thread timer list entry concurrent read/writes.
-- */
-- sighand = lock_task_sighand(p, &flags);
-- if (unlikely(sighand == NULL)) {
-- /*
-- * This raced with the reaping of the task. The exit cleanup
-- * should have removed this timer from the timer queue.
-- */
-- WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
-- } else {
-+ if (likely(p)) {
- if (timer->it.cpu.firing)
- ret = TIMER_RETRY;
- else
-@@ -495,10 +582,8 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
- unlock_task_sighand(p, &flags);
- }
-
--out:
-- rcu_read_unlock();
- if (!ret)
-- put_pid(ctmr->pid);
-+ put_pid(timer->it.cpu.pid);
-
- return ret;
- }
-@@ -620,21 +705,17 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
- u64 old_expires, new_expires, old_incr, val;
- struct cpu_timer *ctmr = &timer->it.cpu;
-- struct sighand_struct *sighand;
- struct task_struct *p;
- unsigned long flags;
- int ret = 0;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p) {
-- /*
-- * If p has just been reaped, we can no
-- * longer get any information about it at all.
-- */
-- rcu_read_unlock();
-+ p = timer_lock_sighand(timer, &flags);
-+ /*
-+ * If p has just been reaped, we can no longer get any information about
-+ * it at all.
-+ */
-+ if (!p)
- return -ESRCH;
-- }
-
- /*
- * Use the to_ktime conversion because that clamps the maximum
-@@ -642,20 +723,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- */
- new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value));
-
-- /*
-- * Protect against sighand release/switch in exit/exec and p->cpu_timers
-- * and p->signal->cpu_timers read/write in arm_timer()
-- */
-- sighand = lock_task_sighand(p, &flags);
-- /*
-- * If p has just been reaped, we can no
-- * longer get any information about it at all.
-- */
-- if (unlikely(sighand == NULL)) {
-- rcu_read_unlock();
-- return -ESRCH;
-- }
--
- /*
- * Disarm any old timer after extracting its expiry time.
- */
-@@ -704,6 +771,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- old->it_value.tv_sec = 0;
- }
- }
-+ old->it_interval = ns_to_timespec64(old_incr);
- }
-
- if (unlikely(ret)) {
-@@ -714,7 +782,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- * it as an overrun (thanks to bump_cpu_timer above).
- */
- unlock_task_sighand(p, &flags);
-- goto out;
-+ return ret;
- }
-
- if (new_expires != 0 && !(timer_flags & TIMER_ABSTIME)) {
-@@ -727,11 +795,11 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- * arm the timer (we'll just fake it for timer_gettime).
- */
- cpu_timer_setexpires(ctmr, new_expires);
-- if (new_expires != 0 && val < new_expires) {
-+ if (new_expires != 0 && val < new_expires)
- arm_timer(timer, p);
-- }
-+ else
-+ trigger_base_recalc_expires(timer, p);
-
-- unlock_task_sighand(p, &flags);
- /*
- * Install the new reload setting, and
- * set up the signal and overrun bookkeeping.
-@@ -748,35 +816,18 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- timer->it_overrun_last = 0;
- timer->it_overrun = -1;
-
-- if (val >= new_expires) {
-- if (new_expires != 0) {
-- /*
-- * The designated time already passed, so we notify
-- * immediately, even if the thread never runs to
-- * accumulate more time on this clock.
-- */
-- cpu_timer_fire(timer);
-- }
-+ unlock_task_sighand(p, &flags);
-
-+ if (new_expires && val >= new_expires) {
- /*
-- * Make sure we don't keep around the process wide cputime
-- * counter or the tick dependency if they are not necessary.
-+ * The designated time already passed, so we notify immediately,
-+ * even if the thread never runs to accumulate more time on this
-+ * clock.
- */
-- sighand = lock_task_sighand(p, &flags);
-- if (!sighand)
-- goto out;
--
-- if (!cpu_timer_queued(ctmr))
-- trigger_base_recalc_expires(timer, p);
--
-- unlock_task_sighand(p, &flags);
-+ cpu_timer_fire(timer);
- }
-- out:
-- rcu_read_unlock();
-- if (old)
-- old->it_interval = ns_to_timespec64(old_incr);
-
-- return ret;
-+ return 0;
- }
-
- static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp)
-@@ -1042,19 +1093,12 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
- {
- clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
- struct task_struct *p;
-- struct sighand_struct *sighand;
- unsigned long flags;
- u64 now;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p)
-- goto out;
--
-- /* Protect timer list r/w in arm_timer() */
-- sighand = lock_task_sighand(p, &flags);
-- if (unlikely(sighand == NULL))
-- goto out;
-+ p = timer_lock_sighand(timer, &flags);
-+ if (unlikely(!p))
-+ return;
-
- /*
- * Fetch the current sample and update the timer's expiry time.
-@@ -1071,8 +1115,6 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
- */
- arm_timer(timer, p);
- unlock_task_sighand(p, &flags);
--out:
-- rcu_read_unlock();
- }
-
- /**
---
-2.53.0
-
usb-serial-io_edgeport-cap-received-transmit-credits.patch
usb-serial-keyspan_pda-fix-data-loss-on-receive-throttling.patch
usb-serial-option-add-tdtech-mt5710-cn.patch
-posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch
revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch
bpf-fix-ld_-abs-ind-failure-path-analysis-in-subprog.patch
+++ /dev/null
-From 30d9395052ea8c37d41b2f1329d6fadca3e801f7 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Mon, 27 Jul 2026 11:10:14 +0200
-Subject: posix-cpu-timers: Prevent UAF caused by non-leader exec() race
-
-From: Thomas Gleixner <tglx@kernel.org>
-
-commit 920f893f735e92ba3a1cd9256899a186b161928d upstream.
-
-Wongi and Jungwoo decoded and reported a non-leader exec() related race
-which can result in an UAF:
-
- sys_timer_delete() exec()
- posix_cpu_timer_del()
- // Observes old leader
- p = pid_task(pid, pid_type); de_thread()
- switch_leader();
- release_task(old_leader)
- __exit_signal(old_leader)
- sighand = lock(old_leader, sighand);
- posix_cpu_timers*_exit();
- sighand = lock_task_sighand(p) unhash_task(old_leader);
- sh = lock(p, sighand) old_leader->sighand = NULL;
- unlock(sighand);
- (p->sighand == NULL)
- unlock(sh)
- return NULL;
-
- // Returns without action
- if(!sighand)
- return 0;
- free_posix_timer();
-
-This is "harmless" unless the deleted timer was armed and enqueued in
-p->signal because on exec() a TGID targeted timer is inherited.
-
-As sys_timer_delete() freed the underlying posix timer object
-run_posix_cpu_timers() or any timerqueue related add/delete operations on
-other timers will access the freed object's timerqueue node, which results
-in an UAF.
-
-There is a similar problem vs. posix_cpu_timer_set(). For regular posix
-timers it just transiently returns -ESRCH to user space, but for the use
-case in do_cpu_nanosleep() it's the same UAF just that the k_itimer is
-allocated on the stack.
-
-Also posix_cpu_timer_rearm() fails to rearm the timer, which means it stops
-to expire.
-
-While debating solutions Frederic pointed out another problem:
-
- posix_cpu_timer_del(tmr)
- __exit_signal(p)
- posix_cpu_timers*_exit(p);
- unhash_task(p);
- p->sighand = NULL;
- sh = lock_task_sighand(p)
- sighand = p->sighand;
- if (!sighand)
- return NULL;
- lock(sighand);
-
- if (!sh)
- WARN_ON_ONCE(timer_queued(tmr));
-
-On weakly ordered architectures it is not guaranteed that
-posix_cpu_timer_del() will observe the stores in posix_cpu_timers*_exit()
-when p->sighand is observed as NULL, which means the WARN() can be a false
-positive.
-
-Solve these issues by:
-
- 1) Changing the store in __exit_signal() to smp_store_release().
-
- 2) Adding a smp_acquire__after_ctrl_dep() into the !sighand path
- of lock_task_sighand().
-
- 3) Creating a helper function for looking up the task and locking sighand
- which does not return when sighand == NULL. Instead it retries the
- task lookup and only if that fails it gives up.
-
- 4) Using that helper in the three affected functions.
-
-#1/#2 ensures that the reader side which observes sighand == NULL also
-observes all preceeding stores, i.e. the stores in posix_cpu_timers*_exit()
-and the ones in unhash_task().
-
-#3 ensures that the above described non-leader exec() situation is handled
-gracefully. When the task lookup returns the old leader, but sighand ==
-NULL then it retries. In the non-leader exec() case the subsequent task
-lookup will observe the new leader due to #1/#2. In normal exit() scenarios
-the subsequent lookup fails.
-
-When the task lookup fails, the function also checks whether the timer is
-still enqueued and issues a warning if that's the case. Unfortunately there
-is nothing which can be done about it, but as the task is already not
-longer visible the timer should not be accessed anymore. This check also
-requires memory ordering, which is not provided when the first lookup
-fails. To achieve that the check is preceeded by a smp_rmb() which pairs
-with the smp_wmb() in write_seqlock() in __exit_signal(). That ensures that
-the stores in posix_cpu_timers*_exit() are visible.
-
-The history of the non-leader exec() issue goes back to the early days of
-posix CPU timers, which stored a pointer to the group leader task in the
-timer. That obviously fails when a non-leader exec() switches the leader.
-commit e0a70217107e ("posix-cpu-timers: workaround to suppress the problems
-with mt exec") added a temporary workaround for that in 2010 which survived
-about 10 years. The fix for the workaround changed the task pointer to a
-pid pointer, but failed to see the subtle race described above. So the
-Fixes tag picks that commit, which seems to be halfways accurate.
-
-Thanks to Frederic Weissbecker, Oleg Nesterov and Peter Zijlstra for
-review, feedback and suggestions and to Wongi and Jungwoo for the excellent
-bug report and analysis!
-
-Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task")
-Reported-by: Wongi Lee <qw3rtyp0@gmail.com>
-Reported-by: Jungwoo Lee <jwlee2217@gmail.com>
-Signed-off-by: Thomas Gleixner <tglx@kernel.org>
-Reviewed-by: Oleg Nesterov <oleg@redhat.com>
-Cc: stable@vger.kernel.org
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- kernel/exit.c | 8 +-
- kernel/signal.c | 10 +-
- kernel/time/posix-cpu-timers.c | 212 ++++++++++++++++++++-------------
- 3 files changed, 143 insertions(+), 87 deletions(-)
-
-diff --git a/kernel/exit.c b/kernel/exit.c
-index fef87ddfa84150..413540df3e1855 100644
---- a/kernel/exit.c
-+++ b/kernel/exit.c
-@@ -203,7 +203,13 @@ static void __exit_signal(struct task_struct *tsk)
- * doing sigqueue_free() if we have SIGQUEUE_PREALLOC signals.
- */
- flush_sigqueue(&tsk->pending);
-- tsk->sighand = NULL;
-+
-+ /*
-+ * Ensure that all preceeding state is visible. Pairs with
-+ * the smp_acquire__after_ctrl_dep() in the sighand == NULL
-+ * path of lock_task_sighand().
-+ */
-+ smp_store_release(&tsk->sighand, NULL);
- spin_unlock(&sighand->siglock);
-
- __cleanup_sighand(sighand);
-diff --git a/kernel/signal.c b/kernel/signal.c
-index 98dbe713829ec3..3dd8c93c2dcc0b 100644
---- a/kernel/signal.c
-+++ b/kernel/signal.c
-@@ -1395,8 +1395,16 @@ struct sighand_struct *__lock_task_sighand(struct task_struct *tsk,
- rcu_read_lock();
- for (;;) {
- sighand = rcu_dereference(tsk->sighand);
-- if (unlikely(sighand == NULL))
-+ if (unlikely(sighand == NULL)) {
-+ /*
-+ * Pairs with the smp_store_release() in
-+ * __exit_signal(). It ensures that all state
-+ * modifications to the task preceeding the store are
-+ * visible to the callers of lock_task_sighand().
-+ */
-+ smp_acquire__after_ctrl_dep();
- break;
-+ }
-
- /*
- * This sighand can be already freed and even reused, but
-diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
-index c77796da8bbce5..c5f7ca683e7eba 100644
---- a/kernel/time/posix-cpu-timers.c
-+++ b/kernel/time/posix-cpu-timers.c
-@@ -462,6 +462,109 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
- trigger_base_recalc_expires(timer, p);
- }
-
-+/*
-+ * Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand.
-+ *
-+ * This can race with the reaping of the task:
-+ *
-+ * CPU0 CPU1
-+ *
-+ * // Finds task
-+ * p = pid_task(pid, pid_type); __exit_signal(p)
-+ * lock(p, sighand);
-+ * posix_cpu_timers*_exit();
-+ * sighand = lock_task_sighand(p); unhash_task(p);
-+ * p->sighand = NULL;
-+ * unlock(sighand);
-+ *
-+ * In this case sighand is NULL, which means the task and the associated timer
-+ * queue cannot be longer accessed safely.
-+ *
-+ * __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is
-+ * dead it also invokes posix_cpu_timers_group_exit(). These functions delete
-+ * all pending timers from the related timer queues. The POSIX timers (k_itimer)
-+ * themself are still accessible, but not longer connected to the task.
-+ *
-+ * exec() works slightly differently. The task which exec()'s terminates all
-+ * other threads in the thread group and runs __exit_signal() on them. As the
-+ * thread group is not dead they only clean up the per task timers via
-+ * posix_cpu_timers_exit().
-+ *
-+ * As the TGID on exec() stays the same per process timers stay queued, if they
-+ * are armed. This works without a problem when exec() is done by the thread
-+ * group leader. If a non-leader thread exec()'s this can end up in the
-+ * following scenario:
-+ *
-+ * CPU0 CPU1
-+ * // Returns old leader
-+ * p = pid_task(pid, pid_type); de_thread()
-+ * switch_leader()
-+ * release_task(old leader)
-+ * __exit_signal()
-+ * old_leader->sighand = NULL;
-+ * // Returns NULL
-+ * sighand = lock_task_sighand(p)
-+ *
-+ * That's problematic for several functions:
-+ *
-+ * - posix_cpu_timer_del(): If the timer is still enqueued on the task the
-+ * underlying k_itimer will be freed which results in a UAF in
-+ * run_posix_cpu_timers() or on timerqueue related add/delete operations.
-+ * If the timer is not enqueued, the failure is harmless
-+ *
-+ * - posix_cpu_timer_set(): Independent of the enqueued state that results in a
-+ * transient failure which is user space visible (-ESRCH) for regular posix
-+ * timers. But for the use case in do_cpu_nanosleep() it's the same UAF
-+ * problem just that the timer is allocated on the stack.
-+ *
-+ * - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this
-+ * silently ignores the rearm request, which is a functional problem as the
-+ * timer wont expire anymore.
-+ */
-+static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags)
-+{
-+ enum pid_type type = clock_pid_type(timer->it_clock);
-+ struct cpu_timer *ctmr = &timer->it.cpu;
-+
-+ guard(rcu)();
-+
-+ for (;;) {
-+ struct task_struct *t = pid_task(timer->it.cpu.pid, type);
-+
-+ /* Fail if the task cannot be found. */
-+ if (!t)
-+ break;
-+
-+ /* Try to lock the task's sighand */
-+ if (lock_task_sighand(t, flags))
-+ return t;
-+
-+ /*
-+ * The next PID lookup might either fail or return the new
-+ * leader. This is correct for both exit() and exec().
-+ */
-+ }
-+
-+ /*
-+ * If the timer is still enqueued, warn. There is nothing safe to do
-+ * here as there might be two timers in there which are removed in
-+ * parallel and that will cause more damage than good. This should never
-+ * happen!
-+ *
-+ * Ensure that the stores to the timer and timerqueue are visible:
-+ *
-+ * __exit_signal()
-+ * posix_cpu_timers*_exit()
-+ * write_seqlock(seqlock)
-+ * smp_wmb(); <-------
-+ * __unhash_process() | !pid_task()
-+ * ----> smp_rmb();
-+ * WARN_ON_ONCE(...)
-+ */
-+ smp_rmb();
-+ WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
-+ return NULL;
-+}
-
- /*
- * Clean up a CPU-clock timer that is about to be destroyed.
-@@ -471,29 +574,13 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
- */
- static int posix_cpu_timer_del(struct k_itimer *timer)
- {
-- struct cpu_timer *ctmr = &timer->it.cpu;
-- struct sighand_struct *sighand;
- struct task_struct *p;
- unsigned long flags;
- int ret = 0;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p)
-- goto out;
-+ p = timer_lock_sighand(timer, &flags);
-
-- /*
-- * Protect against sighand release/switch in exit/exec and process/
-- * thread timer list entry concurrent read/writes.
-- */
-- sighand = lock_task_sighand(p, &flags);
-- if (unlikely(sighand == NULL)) {
-- /*
-- * This raced with the reaping of the task. The exit cleanup
-- * should have removed this timer from the timer queue.
-- */
-- WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
-- } else {
-+ if (likely(p)) {
- if (timer->it.cpu.firing)
- ret = TIMER_RETRY;
- else
-@@ -502,10 +589,8 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
- unlock_task_sighand(p, &flags);
- }
-
--out:
-- rcu_read_unlock();
- if (!ret)
-- put_pid(ctmr->pid);
-+ put_pid(timer->it.cpu.pid);
-
- return ret;
- }
-@@ -627,21 +712,17 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
- u64 old_expires, new_expires, old_incr, val;
- struct cpu_timer *ctmr = &timer->it.cpu;
-- struct sighand_struct *sighand;
- struct task_struct *p;
- unsigned long flags;
- int ret = 0;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p) {
-- /*
-- * If p has just been reaped, we can no
-- * longer get any information about it at all.
-- */
-- rcu_read_unlock();
-+ p = timer_lock_sighand(timer, &flags);
-+ /*
-+ * If p has just been reaped, we can no longer get any information about
-+ * it at all.
-+ */
-+ if (!p)
- return -ESRCH;
-- }
-
- /*
- * Use the to_ktime conversion because that clamps the maximum
-@@ -649,20 +730,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- */
- new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value));
-
-- /*
-- * Protect against sighand release/switch in exit/exec and p->cpu_timers
-- * and p->signal->cpu_timers read/write in arm_timer()
-- */
-- sighand = lock_task_sighand(p, &flags);
-- /*
-- * If p has just been reaped, we can no
-- * longer get any information about it at all.
-- */
-- if (unlikely(sighand == NULL)) {
-- rcu_read_unlock();
-- return -ESRCH;
-- }
--
- /*
- * Disarm any old timer after extracting its expiry time.
- */
-@@ -711,6 +778,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- old->it_value.tv_sec = 0;
- }
- }
-+ old->it_interval = ns_to_timespec64(old_incr);
- }
-
- if (unlikely(ret)) {
-@@ -721,7 +789,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- * it as an overrun (thanks to bump_cpu_timer above).
- */
- unlock_task_sighand(p, &flags);
-- goto out;
-+ return ret;
- }
-
- if (new_expires != 0 && !(timer_flags & TIMER_ABSTIME)) {
-@@ -734,11 +802,11 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- * arm the timer (we'll just fake it for timer_gettime).
- */
- cpu_timer_setexpires(ctmr, new_expires);
-- if (new_expires != 0 && val < new_expires) {
-+ if (new_expires != 0 && val < new_expires)
- arm_timer(timer, p);
-- }
-+ else
-+ trigger_base_recalc_expires(timer, p);
-
-- unlock_task_sighand(p, &flags);
- /*
- * Install the new reload setting, and
- * set up the signal and overrun bookkeeping.
-@@ -755,35 +823,18 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- timer->it_overrun_last = 0;
- timer->it_overrun = -1;
-
-- if (val >= new_expires) {
-- if (new_expires != 0) {
-- /*
-- * The designated time already passed, so we notify
-- * immediately, even if the thread never runs to
-- * accumulate more time on this clock.
-- */
-- cpu_timer_fire(timer);
-- }
-+ unlock_task_sighand(p, &flags);
-
-+ if (new_expires && val >= new_expires) {
- /*
-- * Make sure we don't keep around the process wide cputime
-- * counter or the tick dependency if they are not necessary.
-+ * The designated time already passed, so we notify immediately,
-+ * even if the thread never runs to accumulate more time on this
-+ * clock.
- */
-- sighand = lock_task_sighand(p, &flags);
-- if (!sighand)
-- goto out;
--
-- if (!cpu_timer_queued(ctmr))
-- trigger_base_recalc_expires(timer, p);
--
-- unlock_task_sighand(p, &flags);
-+ cpu_timer_fire(timer);
- }
-- out:
-- rcu_read_unlock();
-- if (old)
-- old->it_interval = ns_to_timespec64(old_incr);
-
-- return ret;
-+ return 0;
- }
-
- static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp)
-@@ -1049,19 +1100,12 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
- {
- clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
- struct task_struct *p;
-- struct sighand_struct *sighand;
- unsigned long flags;
- u64 now;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p)
-- goto out;
--
-- /* Protect timer list r/w in arm_timer() */
-- sighand = lock_task_sighand(p, &flags);
-- if (unlikely(sighand == NULL))
-- goto out;
-+ p = timer_lock_sighand(timer, &flags);
-+ if (unlikely(!p))
-+ return;
-
- /*
- * Fetch the current sample and update the timer's expiry time.
-@@ -1078,8 +1122,6 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
- */
- arm_timer(timer, p);
- unlock_task_sighand(p, &flags);
--out:
-- rcu_read_unlock();
- }
-
- /**
---
-2.53.0
-
usb-serial-io_edgeport-cap-received-transmit-credits.patch
usb-serial-keyspan_pda-fix-data-loss-on-receive-throttling.patch
usb-serial-option-add-tdtech-mt5710-cn.patch
-posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch
revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch
bpf-support-for-hardening-against-jit-spraying.patch
+++ /dev/null
-From cf7b5a3c34e508b49afdda31e7e32ab0d99c7ef9 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Sat, 25 Jul 2026 22:10:32 +0200
-Subject: posix-cpu-timers: Prevent UAF caused by non-leader exec() race
-
-From: Thomas Gleixner <tglx@kernel.org>
-
-commit 920f893f735e92ba3a1cd9256899a186b161928d upstream.
-
-Wongi and Jungwoo decoded and reported a non-leader exec() related race
-which can result in an UAF:
-
- sys_timer_delete() exec()
- posix_cpu_timer_del()
- // Observes old leader
- p = pid_task(pid, pid_type); de_thread()
- switch_leader();
- release_task(old_leader)
- __exit_signal(old_leader)
- sighand = lock(old_leader, sighand);
- posix_cpu_timers*_exit();
- sighand = lock_task_sighand(p) unhash_task(old_leader);
- sh = lock(p, sighand) old_leader->sighand = NULL;
- unlock(sighand);
- (p->sighand == NULL)
- unlock(sh)
- return NULL;
-
- // Returns without action
- if(!sighand)
- return 0;
- free_posix_timer();
-
-This is "harmless" unless the deleted timer was armed and enqueued in
-p->signal because on exec() a TGID targeted timer is inherited.
-
-As sys_timer_delete() freed the underlying posix timer object
-run_posix_cpu_timers() or any timerqueue related add/delete operations on
-other timers will access the freed object's timerqueue node, which results
-in an UAF.
-
-There is a similar problem vs. posix_cpu_timer_set(). For regular posix
-timers it just transiently returns -ESRCH to user space, but for the use
-case in do_cpu_nanosleep() it's the same UAF just that the k_itimer is
-allocated on the stack.
-
-Also posix_cpu_timer_rearm() fails to rearm the timer, which means it stops
-to expire.
-
-While debating solutions Frederic pointed out another problem:
-
- posix_cpu_timer_del(tmr)
- __exit_signal(p)
- posix_cpu_timers*_exit(p);
- unhash_task(p);
- p->sighand = NULL;
- sh = lock_task_sighand(p)
- sighand = p->sighand;
- if (!sighand)
- return NULL;
- lock(sighand);
-
- if (!sh)
- WARN_ON_ONCE(timer_queued(tmr));
-
-On weakly ordered architectures it is not guaranteed that
-posix_cpu_timer_del() will observe the stores in posix_cpu_timers*_exit()
-when p->sighand is observed as NULL, which means the WARN() can be a false
-positive.
-
-Solve these issues by:
-
- 1) Changing the store in __exit_signal() to smp_store_release().
-
- 2) Adding a smp_acquire__after_ctrl_dep() into the !sighand path
- of lock_task_sighand().
-
- 3) Creating a helper function for looking up the task and locking sighand
- which does not return when sighand == NULL. Instead it retries the
- task lookup and only if that fails it gives up.
-
- 4) Using that helper in the three affected functions.
-
-#1/#2 ensures that the reader side which observes sighand == NULL also
-observes all preceeding stores, i.e. the stores in posix_cpu_timers*_exit()
-and the ones in unhash_task().
-
-#3 ensures that the above described non-leader exec() situation is handled
-gracefully. When the task lookup returns the old leader, but sighand ==
-NULL then it retries. In the non-leader exec() case the subsequent task
-lookup will observe the new leader due to #1/#2. In normal exit() scenarios
-the subsequent lookup fails.
-
-When the task lookup fails, the function also checks whether the timer is
-still enqueued and issues a warning if that's the case. Unfortunately there
-is nothing which can be done about it, but as the task is already not
-longer visible the timer should not be accessed anymore. This check also
-requires memory ordering, which is not provided when the first lookup
-fails. To achieve that the check is preceeded by a smp_rmb() which pairs
-with the smp_wmb() in write_seqlock() in __exit_signal(). That ensures that
-the stores in posix_cpu_timers*_exit() are visible.
-
-The history of the non-leader exec() issue goes back to the early days of
-posix CPU timers, which stored a pointer to the group leader task in the
-timer. That obviously fails when a non-leader exec() switches the leader.
-commit e0a70217107e ("posix-cpu-timers: workaround to suppress the problems
-with mt exec") added a temporary workaround for that in 2010 which survived
-about 10 years. The fix for the workaround changed the task pointer to a
-pid pointer, but failed to see the subtle race described above. So the
-Fixes tag picks that commit, which seems to be halfways accurate.
-
-Thanks to Frederic Weissbecker, Oleg Nesterov and Peter Zijlstra for
-review, feedback and suggestions and to Wongi and Jungwoo for the excellent
-bug report and analysis!
-
-Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task")
-Reported-by: Wongi Lee <qw3rtyp0@gmail.com>
-Reported-by: Jungwoo Lee <jwlee2217@gmail.com>
-Signed-off-by: Thomas Gleixner <tglx@kernel.org>
-Reviewed-by: Oleg Nesterov <oleg@redhat.com>
-Cc: stable@vger.kernel.org
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- kernel/exit.c | 8 +-
- kernel/signal.c | 10 +-
- kernel/time/posix-cpu-timers.c | 176 ++++++++++++++++++++++-----------
- 3 files changed, 132 insertions(+), 62 deletions(-)
-
-diff --git a/kernel/exit.c b/kernel/exit.c
-index e798078f958c89..8ae45b2b8cf948 100644
---- a/kernel/exit.c
-+++ b/kernel/exit.c
-@@ -205,7 +205,13 @@ static void __exit_signal(struct task_struct *tsk)
- * doing sigqueue_free() if we have SIGQUEUE_PREALLOC signals.
- */
- flush_sigqueue(&tsk->pending);
-- tsk->sighand = NULL;
-+
-+ /*
-+ * Ensure that all preceeding state is visible. Pairs with
-+ * the smp_acquire__after_ctrl_dep() in the sighand == NULL
-+ * path of lock_task_sighand().
-+ */
-+ smp_store_release(&tsk->sighand, NULL);
- spin_unlock(&sighand->siglock);
-
- __cleanup_sighand(sighand);
-diff --git a/kernel/signal.c b/kernel/signal.c
-index b832158a9c4608..e6e812f8b80ffc 100644
---- a/kernel/signal.c
-+++ b/kernel/signal.c
-@@ -1395,8 +1395,16 @@ struct sighand_struct *__lock_task_sighand(struct task_struct *tsk,
- rcu_read_lock();
- for (;;) {
- sighand = rcu_dereference(tsk->sighand);
-- if (unlikely(sighand == NULL))
-+ if (unlikely(sighand == NULL)) {
-+ /*
-+ * Pairs with the smp_store_release() in
-+ * __exit_signal(). It ensures that all state
-+ * modifications to the task preceeding the store are
-+ * visible to the callers of lock_task_sighand().
-+ */
-+ smp_acquire__after_ctrl_dep();
- break;
-+ }
-
- /*
- * This sighand can be already freed and even reused, but
-diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
-index 635fac5f5fbd58..f0471fbe87288b 100644
---- a/kernel/time/posix-cpu-timers.c
-+++ b/kernel/time/posix-cpu-timers.c
-@@ -462,6 +462,109 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
- trigger_base_recalc_expires(timer, p);
- }
-
-+/*
-+ * Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand.
-+ *
-+ * This can race with the reaping of the task:
-+ *
-+ * CPU0 CPU1
-+ *
-+ * // Finds task
-+ * p = pid_task(pid, pid_type); __exit_signal(p)
-+ * lock(p, sighand);
-+ * posix_cpu_timers*_exit();
-+ * sighand = lock_task_sighand(p); unhash_task(p);
-+ * p->sighand = NULL;
-+ * unlock(sighand);
-+ *
-+ * In this case sighand is NULL, which means the task and the associated timer
-+ * queue cannot be longer accessed safely.
-+ *
-+ * __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is
-+ * dead it also invokes posix_cpu_timers_group_exit(). These functions delete
-+ * all pending timers from the related timer queues. The POSIX timers (k_itimer)
-+ * themself are still accessible, but not longer connected to the task.
-+ *
-+ * exec() works slightly differently. The task which exec()'s terminates all
-+ * other threads in the thread group and runs __exit_signal() on them. As the
-+ * thread group is not dead they only clean up the per task timers via
-+ * posix_cpu_timers_exit().
-+ *
-+ * As the TGID on exec() stays the same per process timers stay queued, if they
-+ * are armed. This works without a problem when exec() is done by the thread
-+ * group leader. If a non-leader thread exec()'s this can end up in the
-+ * following scenario:
-+ *
-+ * CPU0 CPU1
-+ * // Returns old leader
-+ * p = pid_task(pid, pid_type); de_thread()
-+ * switch_leader()
-+ * release_task(old leader)
-+ * __exit_signal()
-+ * old_leader->sighand = NULL;
-+ * // Returns NULL
-+ * sighand = lock_task_sighand(p)
-+ *
-+ * That's problematic for several functions:
-+ *
-+ * - posix_cpu_timer_del(): If the timer is still enqueued on the task the
-+ * underlying k_itimer will be freed which results in a UAF in
-+ * run_posix_cpu_timers() or on timerqueue related add/delete operations.
-+ * If the timer is not enqueued, the failure is harmless
-+ *
-+ * - posix_cpu_timer_set(): Independent of the enqueued state that results in a
-+ * transient failure which is user space visible (-ESRCH) for regular posix
-+ * timers. But for the use case in do_cpu_nanosleep() it's the same UAF
-+ * problem just that the timer is allocated on the stack.
-+ *
-+ * - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this
-+ * silently ignores the rearm request, which is a functional problem as the
-+ * timer wont expire anymore.
-+ */
-+static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags)
-+{
-+ enum pid_type type = clock_pid_type(timer->it_clock);
-+ struct cpu_timer *ctmr = &timer->it.cpu;
-+
-+ guard(rcu)();
-+
-+ for (;;) {
-+ struct task_struct *t = pid_task(timer->it.cpu.pid, type);
-+
-+ /* Fail if the task cannot be found. */
-+ if (!t)
-+ break;
-+
-+ /* Try to lock the task's sighand */
-+ if (lock_task_sighand(t, flags))
-+ return t;
-+
-+ /*
-+ * The next PID lookup might either fail or return the new
-+ * leader. This is correct for both exit() and exec().
-+ */
-+ }
-+
-+ /*
-+ * If the timer is still enqueued, warn. There is nothing safe to do
-+ * here as there might be two timers in there which are removed in
-+ * parallel and that will cause more damage than good. This should never
-+ * happen!
-+ *
-+ * Ensure that the stores to the timer and timerqueue are visible:
-+ *
-+ * __exit_signal()
-+ * posix_cpu_timers*_exit()
-+ * write_seqlock(seqlock)
-+ * smp_wmb(); <-------
-+ * __unhash_process() | !pid_task()
-+ * ----> smp_rmb();
-+ * WARN_ON_ONCE(...)
-+ */
-+ smp_rmb();
-+ WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
-+ return NULL;
-+}
-
- /*
- * Clean up a CPU-clock timer that is about to be destroyed.
-@@ -471,29 +574,13 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
- */
- static int posix_cpu_timer_del(struct k_itimer *timer)
- {
-- struct cpu_timer *ctmr = &timer->it.cpu;
-- struct sighand_struct *sighand;
- struct task_struct *p;
- unsigned long flags;
- int ret = 0;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p)
-- goto out;
-+ p = timer_lock_sighand(timer, &flags);
-
-- /*
-- * Protect against sighand release/switch in exit/exec and process/
-- * thread timer list entry concurrent read/writes.
-- */
-- sighand = lock_task_sighand(p, &flags);
-- if (unlikely(sighand == NULL)) {
-- /*
-- * This raced with the reaping of the task. The exit cleanup
-- * should have removed this timer from the timer queue.
-- */
-- WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
-- } else {
-+ if (likely(p)) {
- if (timer->it.cpu.firing)
- ret = TIMER_RETRY;
- else
-@@ -502,10 +589,8 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
- unlock_task_sighand(p, &flags);
- }
-
--out:
-- rcu_read_unlock();
- if (!ret)
-- put_pid(ctmr->pid);
-+ put_pid(timer->it.cpu.pid);
-
- return ret;
- }
-@@ -627,21 +712,17 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
- struct cpu_timer *ctmr = &timer->it.cpu;
- u64 old_expires, new_expires, now;
-- struct sighand_struct *sighand;
- struct task_struct *p;
- unsigned long flags;
- int ret = 0;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p) {
-- /*
-- * If p has just been reaped, we can no
-- * longer get any information about it at all.
-- */
-- rcu_read_unlock();
-+ p = timer_lock_sighand(timer, &flags);
-+ /*
-+ * If p has just been reaped, we can no longer get any information about
-+ * it at all.
-+ */
-+ if (!p)
- return -ESRCH;
-- }
-
- /*
- * Use the to_ktime conversion because that clamps the maximum
-@@ -649,20 +730,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- */
- new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value));
-
-- /*
-- * Protect against sighand release/switch in exit/exec and p->cpu_timers
-- * and p->signal->cpu_timers read/write in arm_timer()
-- */
-- sighand = lock_task_sighand(p, &flags);
-- /*
-- * If p has just been reaped, we can no
-- * longer get any information about it at all.
-- */
-- if (unlikely(sighand == NULL)) {
-- rcu_read_unlock();
-- return -ESRCH;
-- }
--
- /* Retrieve the current expiry time before disarming the timer */
- old_expires = cpu_timer_getexpires(ctmr);
-
-@@ -693,7 +760,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- /* Retry if the timer expiry is running concurrently */
- if (unlikely(ret)) {
- unlock_task_sighand(p, &flags);
-- goto out;
-+ return ret;
- }
-
- /* Convert relative expiry time to absolute */
-@@ -728,8 +795,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- */
- if (!sigev_none && new_expires && now >= new_expires)
- cpu_timer_fire(timer);
--out:
-- rcu_read_unlock();
- return ret;
- }
-
-@@ -1011,19 +1076,12 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
- {
- clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
- struct task_struct *p;
-- struct sighand_struct *sighand;
- unsigned long flags;
- u64 now;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p)
-- goto out;
--
-- /* Protect timer list r/w in arm_timer() */
-- sighand = lock_task_sighand(p, &flags);
-- if (unlikely(sighand == NULL))
-- goto out;
-+ p = timer_lock_sighand(timer, &flags);
-+ if (unlikely(!p))
-+ return;
-
- /*
- * Fetch the current sample and update the timer's expiry time.
-@@ -1040,8 +1098,6 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
- */
- arm_timer(timer, p);
- unlock_task_sighand(p, &flags);
--out:
-- rcu_read_unlock();
- }
-
- /**
---
-2.53.0
-
input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
input-ims-pcu-fix-logic-error-in-packet-reset.patch
soc-qcom-ice-allow-explicit-votes-on-iface-clock-for.patch
-posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
arm64-tegra-fix-cpu-compatible-string-to-cortex-a78a.patch
firmware-arm_ffa-respect-firmware-advertised-rx-tx-b.patch
ib-mad-drop-unmatched-rmpp-responses-before-reassemb.patch
+++ /dev/null
-From 0ad28e2e40b0d6eb34c62a357edc992edf33f338 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Fri, 3 Jul 2026 12:02:38 +0200
-Subject: posix-cpu-timers: Prevent UAF caused by non-leader exec() race
-
-From: Thomas Gleixner <tglx@kernel.org>
-
-commit 920f893f735e92ba3a1cd9256899a186b161928d upstream.
-
-Wongi and Jungwoo decoded and reported a non-leader exec() related race
-which can result in an UAF:
-
- sys_timer_delete() exec()
- posix_cpu_timer_del()
- // Observes old leader
- p = pid_task(pid, pid_type); de_thread()
- switch_leader();
- release_task(old_leader)
- __exit_signal(old_leader)
- sighand = lock(old_leader, sighand);
- posix_cpu_timers*_exit();
- sighand = lock_task_sighand(p) unhash_task(old_leader);
- sh = lock(p, sighand) old_leader->sighand = NULL;
- unlock(sighand);
- (p->sighand == NULL)
- unlock(sh)
- return NULL;
-
- // Returns without action
- if(!sighand)
- return 0;
- free_posix_timer();
-
-This is "harmless" unless the deleted timer was armed and enqueued in
-p->signal because on exec() a TGID targeted timer is inherited.
-
-As sys_timer_delete() freed the underlying posix timer object
-run_posix_cpu_timers() or any timerqueue related add/delete operations on
-other timers will access the freed object's timerqueue node, which results
-in an UAF.
-
-There is a similar problem vs. posix_cpu_timer_set(). For regular posix
-timers it just transiently returns -ESRCH to user space, but for the use
-case in do_cpu_nanosleep() it's the same UAF just that the k_itimer is
-allocated on the stack.
-
-Also posix_cpu_timer_rearm() fails to rearm the timer, which means it stops
-to expire.
-
-While debating solutions Frederic pointed out another problem:
-
- posix_cpu_timer_del(tmr)
- __exit_signal(p)
- posix_cpu_timers*_exit(p);
- unhash_task(p);
- p->sighand = NULL;
- sh = lock_task_sighand(p)
- sighand = p->sighand;
- if (!sighand)
- return NULL;
- lock(sighand);
-
- if (!sh)
- WARN_ON_ONCE(timer_queued(tmr));
-
-On weakly ordered architectures it is not guaranteed that
-posix_cpu_timer_del() will observe the stores in posix_cpu_timers*_exit()
-when p->sighand is observed as NULL, which means the WARN() can be a false
-positive.
-
-Solve these issues by:
-
- 1) Changing the store in __exit_signal() to smp_store_release().
-
- 2) Adding a smp_acquire__after_ctrl_dep() into the !sighand path
- of lock_task_sighand().
-
- 3) Creating a helper function for looking up the task and locking sighand
- which does not return when sighand == NULL. Instead it retries the
- task lookup and only if that fails it gives up.
-
- 4) Using that helper in the three affected functions.
-
-observes all preceeding stores, i.e. the stores in posix_cpu_timers*_exit()
-and the ones in unhash_task().
-
-gracefully. When the task lookup returns the old leader, but sighand ==
-NULL then it retries. In the non-leader exec() case the subsequent task
-lookup will observe the new leader due to #1/#2. In normal exit() scenarios
-the subsequent lookup fails.
-
-When the task lookup fails, the function also checks whether the timer is
-still enqueued and issues a warning if that's the case. Unfortunately there
-is nothing which can be done about it, but as the task is already not
-longer visible the timer should not be accessed anymore. This check also
-requires memory ordering, which is not provided when the first lookup
-fails. To achieve that the check is preceeded by a smp_rmb() which pairs
-with the smp_wmb() in write_seqlock() in __exit_signal(). That ensures that
-the stores in posix_cpu_timers*_exit() are visible.
-
-The history of the non-leader exec() issue goes back to the early days of
-posix CPU timers, which stored a pointer to the group leader task in the
-timer. That obviously fails when a non-leader exec() switches the leader.
-commit e0a70217107e ("posix-cpu-timers: workaround to suppress the problems
-with mt exec") added a temporary workaround for that in 2010 which survived
-about 10 years. The fix for the workaround changed the task pointer to a
-pid pointer, but failed to see the subtle race described above. So the
-Fixes tag picks that commit, which seems to be halfways accurate.
-
-Thanks to Frederic Weissbecker, Oleg Nesterov and Peter Zijlstra for
-review, feedback and suggestions and to Wongi and Jungwoo for the excellent
-bug report and analysis!
-
-Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task")
-Reported-by: Wongi Lee <qw3rtyp0@gmail.com>
-Reported-by: Jungwoo Lee <jwlee2217@gmail.com>
-Signed-off-by: Thomas Gleixner <tglx@kernel.org>
-Reviewed-by: Oleg Nesterov <oleg@redhat.com>
-Cc: stable@vger.kernel.org
-Signed-off-by: Salvatore Bonaccorso <carnil@debian.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- kernel/exit.c | 7 +-
- kernel/signal.c | 10 +-
- kernel/time/posix-cpu-timers.c | 173 ++++++++++++++++++++++-----------
- 3 files changed, 130 insertions(+), 60 deletions(-)
-
-diff --git a/kernel/exit.c b/kernel/exit.c
-index c832946823f488..a32bc65569a508 100644
---- a/kernel/exit.c
-+++ b/kernel/exit.c
-@@ -212,7 +212,12 @@ static void __exit_signal(struct release_task_post *post, struct task_struct *ts
- __unhash_process(post, tsk, group_dead);
- write_sequnlock(&sig->stats_lock);
-
-- tsk->sighand = NULL;
-+ /*
-+ * Ensure that all preceeding state is visible. Pairs with
-+ * the smp_acquire__after_ctrl_dep() in the sighand == NULL
-+ * path of lock_task_sighand().
-+ */
-+ smp_store_release(&tsk->sighand, NULL);
- spin_unlock(&sighand->siglock);
-
- __cleanup_sighand(sighand);
-diff --git a/kernel/signal.c b/kernel/signal.c
-index 810098300ecdcb..e1843b71efff13 100644
---- a/kernel/signal.c
-+++ b/kernel/signal.c
-@@ -1364,8 +1364,16 @@ struct sighand_struct *__lock_task_sighand(struct task_struct *tsk,
- rcu_read_lock();
- for (;;) {
- sighand = rcu_dereference(tsk->sighand);
-- if (unlikely(sighand == NULL))
-+ if (unlikely(sighand == NULL)) {
-+ /*
-+ * Pairs with the smp_store_release() in
-+ * __exit_signal(). It ensures that all state
-+ * modifications to the task preceeding the store are
-+ * visible to the callers of lock_task_sighand().
-+ */
-+ smp_acquire__after_ctrl_dep();
- break;
-+ }
-
- /*
- * This sighand can be already freed and even reused, but
-diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
-index d14523fbc146cd..2d64972fd0804b 100644
---- a/kernel/time/posix-cpu-timers.c
-+++ b/kernel/time/posix-cpu-timers.c
-@@ -461,6 +461,109 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
- trigger_base_recalc_expires(timer, p);
- }
-
-+/*
-+ * Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand.
-+ *
-+ * This can race with the reaping of the task:
-+ *
-+ * CPU0 CPU1
-+ *
-+ * // Finds task
-+ * p = pid_task(pid, pid_type); __exit_signal(p)
-+ * lock(p, sighand);
-+ * posix_cpu_timers*_exit();
-+ * sighand = lock_task_sighand(p); unhash_task(p);
-+ * p->sighand = NULL;
-+ * unlock(sighand);
-+ *
-+ * In this case sighand is NULL, which means the task and the associated timer
-+ * queue cannot be longer accessed safely.
-+ *
-+ * __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is
-+ * dead it also invokes posix_cpu_timers_group_exit(). These functions delete
-+ * all pending timers from the related timer queues. The POSIX timers (k_itimer)
-+ * themself are still accessible, but not longer connected to the task.
-+ *
-+ * exec() works slightly differently. The task which exec()'s terminates all
-+ * other threads in the thread group and runs __exit_signal() on them. As the
-+ * thread group is not dead they only clean up the per task timers via
-+ * posix_cpu_timers_exit().
-+ *
-+ * As the TGID on exec() stays the same per process timers stay queued, if they
-+ * are armed. This works without a problem when exec() is done by the thread
-+ * group leader. If a non-leader thread exec()'s this can end up in the
-+ * following scenario:
-+ *
-+ * CPU0 CPU1
-+ * // Returns old leader
-+ * p = pid_task(pid, pid_type); de_thread()
-+ * switch_leader()
-+ * release_task(old leader)
-+ * __exit_signal()
-+ * old_leader->sighand = NULL;
-+ * // Returns NULL
-+ * sighand = lock_task_sighand(p)
-+ *
-+ * That's problematic for several functions:
-+ *
-+ * - posix_cpu_timer_del(): If the timer is still enqueued on the task the
-+ * underlying k_itimer will be freed which results in a UAF in
-+ * run_posix_cpu_timers() or on timerqueue related add/delete operations.
-+ * If the timer is not enqueued, the failure is harmless
-+ *
-+ * - posix_cpu_timer_set(): Independent of the enqueued state that results in a
-+ * transient failure which is user space visible (-ESRCH) for regular posix
-+ * timers. But for the use case in do_cpu_nanosleep() it's the same UAF
-+ * problem just that the timer is allocated on the stack.
-+ *
-+ * - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this
-+ * silently ignores the rearm request, which is a functional problem as the
-+ * timer wont expire anymore.
-+ */
-+static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags)
-+{
-+ enum pid_type type = clock_pid_type(timer->it_clock);
-+ struct cpu_timer *ctmr = &timer->it.cpu;
-+
-+ guard(rcu)();
-+
-+ for (;;) {
-+ struct task_struct *t = pid_task(timer->it.cpu.pid, type);
-+
-+ /* Fail if the task cannot be found. */
-+ if (!t)
-+ break;
-+
-+ /* Try to lock the task's sighand */
-+ if (lock_task_sighand(t, flags))
-+ return t;
-+
-+ /*
-+ * The next PID lookup might either fail or return the new
-+ * leader. This is correct for both exit() and exec().
-+ */
-+ }
-+
-+ /*
-+ * If the timer is still enqueued, warn. There is nothing safe to do
-+ * here as there might be two timers in there which are removed in
-+ * parallel and that will cause more damage than good. This should never
-+ * happen!
-+ *
-+ * Ensure that the stores to the timer and timerqueue are visible:
-+ *
-+ * __exit_signal()
-+ * posix_cpu_timers*_exit()
-+ * write_seqlock(seqlock)
-+ * smp_wmb(); <-------
-+ * __unhash_process() | !pid_task()
-+ * ----> smp_rmb();
-+ * WARN_ON_ONCE(...)
-+ */
-+ smp_rmb();
-+ WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
-+ return NULL;
-+}
-
- /*
- * Clean up a CPU-clock timer that is about to be destroyed.
-@@ -470,29 +573,13 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
- */
- static int posix_cpu_timer_del(struct k_itimer *timer)
- {
-- struct cpu_timer *ctmr = &timer->it.cpu;
-- struct sighand_struct *sighand;
- struct task_struct *p;
- unsigned long flags;
- int ret = 0;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p)
-- goto out;
-+ p = timer_lock_sighand(timer, &flags);
-
-- /*
-- * Protect against sighand release/switch in exit/exec and process/
-- * thread timer list entry concurrent read/writes.
-- */
-- sighand = lock_task_sighand(p, &flags);
-- if (unlikely(sighand == NULL)) {
-- /*
-- * This raced with the reaping of the task. The exit cleanup
-- * should have removed this timer from the timer queue.
-- */
-- WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
-- } else {
-+ if (likely(p)) {
- if (timer->it.cpu.firing) {
- /*
- * Prevent signal delivery. The timer cannot be dequeued
-@@ -508,11 +595,8 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
- unlock_task_sighand(p, &flags);
- }
-
--out:
-- rcu_read_unlock();
--
- if (!ret) {
-- put_pid(ctmr->pid);
-+ put_pid(timer->it.cpu.pid);
- timer->it_status = POSIX_TIMER_DISARMED;
- }
- return ret;
-@@ -626,21 +710,17 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
- struct cpu_timer *ctmr = &timer->it.cpu;
- u64 old_expires, new_expires, now;
-- struct sighand_struct *sighand;
- struct task_struct *p;
- unsigned long flags;
- int ret = 0;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p) {
-- /*
-- * If p has just been reaped, we can no
-- * longer get any information about it at all.
-- */
-- rcu_read_unlock();
-+ p = timer_lock_sighand(timer, &flags);
-+ /*
-+ * If p has just been reaped, we can no longer get any information about
-+ * it at all.
-+ */
-+ if (!p)
- return -ESRCH;
-- }
-
- /*
- * Use the to_ktime conversion because that clamps the maximum
-@@ -648,20 +728,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- */
- new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value));
-
-- /*
-- * Protect against sighand release/switch in exit/exec and p->cpu_timers
-- * and p->signal->cpu_timers read/write in arm_timer()
-- */
-- sighand = lock_task_sighand(p, &flags);
-- /*
-- * If p has just been reaped, we can no
-- * longer get any information about it at all.
-- */
-- if (unlikely(sighand == NULL)) {
-- rcu_read_unlock();
-- return -ESRCH;
-- }
--
- /* Retrieve the current expiry time before disarming the timer */
- old_expires = cpu_timer_getexpires(ctmr);
-
-@@ -698,7 +764,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- /* Retry if the timer expiry is running concurrently */
- if (unlikely(ret)) {
- unlock_task_sighand(p, &flags);
-- goto out;
-+ return ret;
- }
-
- /* Convert relative expiry time to absolute */
-@@ -733,8 +799,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- */
- if (!sigev_none && new_expires && now >= new_expires)
- cpu_timer_fire(timer);
--out:
-- rcu_read_unlock();
- return ret;
- }
-
-@@ -1018,19 +1082,12 @@ static void check_process_timers(struct task_struct *tsk,
- static bool posix_cpu_timer_rearm(struct k_itimer *timer)
- {
- clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
-- struct sighand_struct *sighand;
- struct task_struct *p;
- unsigned long flags;
- u64 now;
-
-- guard(rcu)();
-- p = cpu_timer_task_rcu(timer);
-- if (!p)
-- return true;
--
-- /* Protect timer list r/w in arm_timer() */
-- sighand = lock_task_sighand(p, &flags);
-- if (unlikely(sighand == NULL))
-+ p = timer_lock_sighand(timer, &flags);
-+ if (unlikely(!p))
- return true;
-
- /*
---
-2.53.0
-
+++ /dev/null
-From a28e4c85d2a7b518fc6c697fe2bd2e8caebb00e3 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Wed, 8 Apr 2026 13:53:56 +0200
-Subject: posix-timers: Expand timer_[re]arm() callbacks with a boolean return
- value
-
-From: Thomas Gleixner <tglx@kernel.org>
-
-commit 6fdb2677a594ab38eade927919bbd4d9688bfa1c upstream.
-
-In order to catch expiry times which are already in the past the
-timer_arm() and timer_rearm() callbacks need to be able to report back to
-the caller whether the timer has been queued or not.
-
-Change the function signature and let all implementations return true for
-now. While at it simplify posix_cpu_timer_rearm().
-
-No functional change intended.
-
-Signed-off-by: Thomas Gleixner <tglx@kernel.org>
-Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
-Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
-Acked-by: John Stultz <jstultz@google.com>
-Link: https://patch.msgid.link/20260408114952.130222296@kernel.org
-Signed-off-by: Salvatore Bonaccorso <carnil@debian.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- kernel/time/alarmtimer.c | 6 ++++--
- kernel/time/posix-cpu-timers.c | 18 ++++++++++--------
- kernel/time/posix-timers.c | 6 ++++--
- kernel/time/posix-timers.h | 4 ++--
- 4 files changed, 20 insertions(+), 14 deletions(-)
-
-diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
-index 189ee8bbc3e650..525df334b15a79 100644
---- a/kernel/time/alarmtimer.c
-+++ b/kernel/time/alarmtimer.c
-@@ -521,12 +521,13 @@ static void alarm_handle_timer(struct alarm *alarm, ktime_t now)
- * alarm_timer_rearm - Posix timer callback for rearming timer
- * @timr: Pointer to the posixtimer data struct
- */
--static void alarm_timer_rearm(struct k_itimer *timr)
-+static bool alarm_timer_rearm(struct k_itimer *timr)
- {
- struct alarm *alarm = &timr->it.alarm.alarmtimer;
-
- timr->it_overrun += alarm_forward_now(alarm, timr->it_interval);
- alarm_start(alarm, alarm->node.expires);
-+ return true;
- }
-
- /**
-@@ -582,7 +583,7 @@ static void alarm_timer_wait_running(struct k_itimer *timr)
- * @absolute: Expiry value is absolute time
- * @sigev_none: Posix timer does not deliver signals
- */
--static void alarm_timer_arm(struct k_itimer *timr, ktime_t expires,
-+static bool alarm_timer_arm(struct k_itimer *timr, ktime_t expires,
- bool absolute, bool sigev_none)
- {
- struct alarm *alarm = &timr->it.alarm.alarmtimer;
-@@ -594,6 +595,7 @@ static void alarm_timer_arm(struct k_itimer *timr, ktime_t expires,
- alarm->node.expires = expires;
- else
- alarm_start(&timr->it.alarm.alarmtimer, expires);
-+ return true;
- }
-
- /**
-diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
-index 99affd9e228ec5..d14523fbc146cd 100644
---- a/kernel/time/posix-cpu-timers.c
-+++ b/kernel/time/posix-cpu-timers.c
-@@ -19,7 +19,7 @@
-
- #include "posix-timers.h"
-
--static void posix_cpu_timer_rearm(struct k_itimer *timer);
-+static bool posix_cpu_timer_rearm(struct k_itimer *timer);
-
- void posix_cputimers_group_init(struct posix_cputimers *pct, u64 cpu_limit)
- {
-@@ -1011,24 +1011,27 @@ static void check_process_timers(struct task_struct *tsk,
- /*
- * This is called from the signal code (via posixtimer_rearm)
- * when the last timer signal was delivered and we have to reload the timer.
-+ *
-+ * Return true unconditionally so the core code assumes the timer to be
-+ * armed. Otherwise it would requeue the signal.
- */
--static void posix_cpu_timer_rearm(struct k_itimer *timer)
-+static bool posix_cpu_timer_rearm(struct k_itimer *timer)
- {
- clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
-- struct task_struct *p;
- struct sighand_struct *sighand;
-+ struct task_struct *p;
- unsigned long flags;
- u64 now;
-
-- rcu_read_lock();
-+ guard(rcu)();
- p = cpu_timer_task_rcu(timer);
- if (!p)
-- goto out;
-+ return true;
-
- /* Protect timer list r/w in arm_timer() */
- sighand = lock_task_sighand(p, &flags);
- if (unlikely(sighand == NULL))
-- goto out;
-+ return true;
-
- /*
- * Fetch the current sample and update the timer's expiry time.
-@@ -1045,8 +1048,7 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
- */
- arm_timer(timer, p);
- unlock_task_sighand(p, &flags);
--out:
-- rcu_read_unlock();
-+ return true;
- }
-
- /**
-diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
-index 56e17b625c72e8..3082129d836c24 100644
---- a/kernel/time/posix-timers.c
-+++ b/kernel/time/posix-timers.c
-@@ -295,12 +295,13 @@ static inline int timer_overrun_to_int(struct k_itimer *timr)
- return (int)timr->it_overrun_last;
- }
-
--static void common_hrtimer_rearm(struct k_itimer *timr)
-+static bool common_hrtimer_rearm(struct k_itimer *timr)
- {
- struct hrtimer *timer = &timr->it.real.timer;
-
- timr->it_overrun += hrtimer_forward_now(timer, timr->it_interval);
- hrtimer_restart(timer);
-+ return true;
- }
-
- static bool __posixtimer_deliver_signal(struct kernel_siginfo *info, struct k_itimer *timr)
-@@ -802,7 +803,7 @@ SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)
- return timer_overrun_to_int(scoped_timer);
- }
-
--static void common_hrtimer_arm(struct k_itimer *timr, ktime_t expires,
-+static bool common_hrtimer_arm(struct k_itimer *timr, ktime_t expires,
- bool absolute, bool sigev_none)
- {
- struct hrtimer *timer = &timr->it.real.timer;
-@@ -829,6 +830,7 @@ static void common_hrtimer_arm(struct k_itimer *timr, ktime_t expires,
-
- if (!sigev_none)
- hrtimer_start_expires(timer, HRTIMER_MODE_ABS);
-+ return true;
- }
-
- static int common_hrtimer_try_to_cancel(struct k_itimer *timr)
-diff --git a/kernel/time/posix-timers.h b/kernel/time/posix-timers.h
-index 7f259e845d240c..4ea9611dd7165c 100644
---- a/kernel/time/posix-timers.h
-+++ b/kernel/time/posix-timers.h
-@@ -27,11 +27,11 @@ struct k_clock {
- int (*timer_del)(struct k_itimer *timr);
- void (*timer_get)(struct k_itimer *timr,
- struct itimerspec64 *cur_setting);
-- void (*timer_rearm)(struct k_itimer *timr);
-+ bool (*timer_rearm)(struct k_itimer *timr);
- s64 (*timer_forward)(struct k_itimer *timr, ktime_t now);
- ktime_t (*timer_remaining)(struct k_itimer *timr, ktime_t now);
- int (*timer_try_to_cancel)(struct k_itimer *timr);
-- void (*timer_arm)(struct k_itimer *timr, ktime_t expires,
-+ bool (*timer_arm)(struct k_itimer *timr, ktime_t expires,
- bool absolute, bool sigev_none);
- void (*timer_wait_running)(struct k_itimer *timr);
- };
---
-2.53.0
-
-posix-timers-expand-timer_-re-arm-callbacks-with-a-b.patch
-posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch
bpf-fix-ld_-abs-ind-failure-path-analysis-in-subprog.patch
selftests-bpf-add-tests-for-ld_-abs-ind-failure-path.patch
+++ /dev/null
-From df66f7b592d359455c4054a628f3ea06b1b3d806 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Mon, 27 Jul 2026 11:10:14 +0200
-Subject: posix-cpu-timers: Prevent UAF caused by non-leader exec() race
-
-From: Thomas Gleixner <tglx@kernel.org>
-
-commit 920f893f735e92ba3a1cd9256899a186b161928d upstream.
-
-Wongi and Jungwoo decoded and reported a non-leader exec() related race
-which can result in an UAF:
-
- sys_timer_delete() exec()
- posix_cpu_timer_del()
- // Observes old leader
- p = pid_task(pid, pid_type); de_thread()
- switch_leader();
- release_task(old_leader)
- __exit_signal(old_leader)
- sighand = lock(old_leader, sighand);
- posix_cpu_timers*_exit();
- sighand = lock_task_sighand(p) unhash_task(old_leader);
- sh = lock(p, sighand) old_leader->sighand = NULL;
- unlock(sighand);
- (p->sighand == NULL)
- unlock(sh)
- return NULL;
-
- // Returns without action
- if(!sighand)
- return 0;
- free_posix_timer();
-
-This is "harmless" unless the deleted timer was armed and enqueued in
-p->signal because on exec() a TGID targeted timer is inherited.
-
-As sys_timer_delete() freed the underlying posix timer object
-run_posix_cpu_timers() or any timerqueue related add/delete operations on
-other timers will access the freed object's timerqueue node, which results
-in an UAF.
-
-There is a similar problem vs. posix_cpu_timer_set(). For regular posix
-timers it just transiently returns -ESRCH to user space, but for the use
-case in do_cpu_nanosleep() it's the same UAF just that the k_itimer is
-allocated on the stack.
-
-Also posix_cpu_timer_rearm() fails to rearm the timer, which means it stops
-to expire.
-
-While debating solutions Frederic pointed out another problem:
-
- posix_cpu_timer_del(tmr)
- __exit_signal(p)
- posix_cpu_timers*_exit(p);
- unhash_task(p);
- p->sighand = NULL;
- sh = lock_task_sighand(p)
- sighand = p->sighand;
- if (!sighand)
- return NULL;
- lock(sighand);
-
- if (!sh)
- WARN_ON_ONCE(timer_queued(tmr));
-
-On weakly ordered architectures it is not guaranteed that
-posix_cpu_timer_del() will observe the stores in posix_cpu_timers*_exit()
-when p->sighand is observed as NULL, which means the WARN() can be a false
-positive.
-
-Solve these issues by:
-
- 1) Changing the store in __exit_signal() to smp_store_release().
-
- 2) Adding a smp_acquire__after_ctrl_dep() into the !sighand path
- of lock_task_sighand().
-
- 3) Creating a helper function for looking up the task and locking sighand
- which does not return when sighand == NULL. Instead it retries the
- task lookup and only if that fails it gives up.
-
- 4) Using that helper in the three affected functions.
-
-#1/#2 ensures that the reader side which observes sighand == NULL also
-observes all preceeding stores, i.e. the stores in posix_cpu_timers*_exit()
-and the ones in unhash_task().
-
-#3 ensures that the above described non-leader exec() situation is handled
-gracefully. When the task lookup returns the old leader, but sighand ==
-NULL then it retries. In the non-leader exec() case the subsequent task
-lookup will observe the new leader due to #1/#2. In normal exit() scenarios
-the subsequent lookup fails.
-
-When the task lookup fails, the function also checks whether the timer is
-still enqueued and issues a warning if that's the case. Unfortunately there
-is nothing which can be done about it, but as the task is already not
-longer visible the timer should not be accessed anymore. This check also
-requires memory ordering, which is not provided when the first lookup
-fails. To achieve that the check is preceeded by a smp_rmb() which pairs
-with the smp_wmb() in write_seqlock() in __exit_signal(). That ensures that
-the stores in posix_cpu_timers*_exit() are visible.
-
-The history of the non-leader exec() issue goes back to the early days of
-posix CPU timers, which stored a pointer to the group leader task in the
-timer. That obviously fails when a non-leader exec() switches the leader.
-commit e0a70217107e ("posix-cpu-timers: workaround to suppress the problems
-with mt exec") added a temporary workaround for that in 2010 which survived
-about 10 years. The fix for the workaround changed the task pointer to a
-pid pointer, but failed to see the subtle race described above. So the
-Fixes tag picks that commit, which seems to be halfways accurate.
-
-Thanks to Frederic Weissbecker, Oleg Nesterov and Peter Zijlstra for
-review, feedback and suggestions and to Wongi and Jungwoo for the excellent
-bug report and analysis!
-
-Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task")
-Reported-by: Wongi Lee <qw3rtyp0@gmail.com>
-Reported-by: Jungwoo Lee <jwlee2217@gmail.com>
-Signed-off-by: Thomas Gleixner <tglx@kernel.org>
-Reviewed-by: Oleg Nesterov <oleg@redhat.com>
-Cc: stable@vger.kernel.org
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- kernel/exit.c | 8 +-
- kernel/signal.c | 10 +-
- kernel/time/posix-cpu-timers.c | 212 ++++++++++++++++++++-------------
- 3 files changed, 143 insertions(+), 87 deletions(-)
-
-diff --git a/kernel/exit.c b/kernel/exit.c
-index 5ebe01e8f37e15..55a8aa868298f0 100644
---- a/kernel/exit.c
-+++ b/kernel/exit.c
-@@ -204,7 +204,13 @@ static void __exit_signal(struct task_struct *tsk)
- * doing sigqueue_free() if we have SIGQUEUE_PREALLOC signals.
- */
- flush_sigqueue(&tsk->pending);
-- tsk->sighand = NULL;
-+
-+ /*
-+ * Ensure that all preceeding state is visible. Pairs with
-+ * the smp_acquire__after_ctrl_dep() in the sighand == NULL
-+ * path of lock_task_sighand().
-+ */
-+ smp_store_release(&tsk->sighand, NULL);
- spin_unlock(&sighand->siglock);
-
- __cleanup_sighand(sighand);
-diff --git a/kernel/signal.c b/kernel/signal.c
-index 3a484ea4bab658..9fe386e6badfb0 100644
---- a/kernel/signal.c
-+++ b/kernel/signal.c
-@@ -1408,8 +1408,16 @@ struct sighand_struct *__lock_task_sighand(struct task_struct *tsk,
- rcu_read_lock();
- for (;;) {
- sighand = rcu_dereference(tsk->sighand);
-- if (unlikely(sighand == NULL))
-+ if (unlikely(sighand == NULL)) {
-+ /*
-+ * Pairs with the smp_store_release() in
-+ * __exit_signal(). It ensures that all state
-+ * modifications to the task preceeding the store are
-+ * visible to the callers of lock_task_sighand().
-+ */
-+ smp_acquire__after_ctrl_dep();
- break;
-+ }
-
- /*
- * This sighand can be already freed and even reused, but
-diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
-index b99c386c50e432..fa730130fe154b 100644
---- a/kernel/time/posix-cpu-timers.c
-+++ b/kernel/time/posix-cpu-timers.c
-@@ -461,6 +461,109 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
- trigger_base_recalc_expires(timer, p);
- }
-
-+/*
-+ * Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand.
-+ *
-+ * This can race with the reaping of the task:
-+ *
-+ * CPU0 CPU1
-+ *
-+ * // Finds task
-+ * p = pid_task(pid, pid_type); __exit_signal(p)
-+ * lock(p, sighand);
-+ * posix_cpu_timers*_exit();
-+ * sighand = lock_task_sighand(p); unhash_task(p);
-+ * p->sighand = NULL;
-+ * unlock(sighand);
-+ *
-+ * In this case sighand is NULL, which means the task and the associated timer
-+ * queue cannot be longer accessed safely.
-+ *
-+ * __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is
-+ * dead it also invokes posix_cpu_timers_group_exit(). These functions delete
-+ * all pending timers from the related timer queues. The POSIX timers (k_itimer)
-+ * themself are still accessible, but not longer connected to the task.
-+ *
-+ * exec() works slightly differently. The task which exec()'s terminates all
-+ * other threads in the thread group and runs __exit_signal() on them. As the
-+ * thread group is not dead they only clean up the per task timers via
-+ * posix_cpu_timers_exit().
-+ *
-+ * As the TGID on exec() stays the same per process timers stay queued, if they
-+ * are armed. This works without a problem when exec() is done by the thread
-+ * group leader. If a non-leader thread exec()'s this can end up in the
-+ * following scenario:
-+ *
-+ * CPU0 CPU1
-+ * // Returns old leader
-+ * p = pid_task(pid, pid_type); de_thread()
-+ * switch_leader()
-+ * release_task(old leader)
-+ * __exit_signal()
-+ * old_leader->sighand = NULL;
-+ * // Returns NULL
-+ * sighand = lock_task_sighand(p)
-+ *
-+ * That's problematic for several functions:
-+ *
-+ * - posix_cpu_timer_del(): If the timer is still enqueued on the task the
-+ * underlying k_itimer will be freed which results in a UAF in
-+ * run_posix_cpu_timers() or on timerqueue related add/delete operations.
-+ * If the timer is not enqueued, the failure is harmless
-+ *
-+ * - posix_cpu_timer_set(): Independent of the enqueued state that results in a
-+ * transient failure which is user space visible (-ESRCH) for regular posix
-+ * timers. But for the use case in do_cpu_nanosleep() it's the same UAF
-+ * problem just that the timer is allocated on the stack.
-+ *
-+ * - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this
-+ * silently ignores the rearm request, which is a functional problem as the
-+ * timer wont expire anymore.
-+ */
-+static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags)
-+{
-+ enum pid_type type = clock_pid_type(timer->it_clock);
-+ struct cpu_timer *ctmr = &timer->it.cpu;
-+
-+ guard(rcu)();
-+
-+ for (;;) {
-+ struct task_struct *t = pid_task(timer->it.cpu.pid, type);
-+
-+ /* Fail if the task cannot be found. */
-+ if (!t)
-+ break;
-+
-+ /* Try to lock the task's sighand */
-+ if (lock_task_sighand(t, flags))
-+ return t;
-+
-+ /*
-+ * The next PID lookup might either fail or return the new
-+ * leader. This is correct for both exit() and exec().
-+ */
-+ }
-+
-+ /*
-+ * If the timer is still enqueued, warn. There is nothing safe to do
-+ * here as there might be two timers in there which are removed in
-+ * parallel and that will cause more damage than good. This should never
-+ * happen!
-+ *
-+ * Ensure that the stores to the timer and timerqueue are visible:
-+ *
-+ * __exit_signal()
-+ * posix_cpu_timers*_exit()
-+ * write_seqlock(seqlock)
-+ * smp_wmb(); <-------
-+ * __unhash_process() | !pid_task()
-+ * ----> smp_rmb();
-+ * WARN_ON_ONCE(...)
-+ */
-+ smp_rmb();
-+ WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
-+ return NULL;
-+}
-
- /*
- * Clean up a CPU-clock timer that is about to be destroyed.
-@@ -470,29 +573,13 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
- */
- static int posix_cpu_timer_del(struct k_itimer *timer)
- {
-- struct cpu_timer *ctmr = &timer->it.cpu;
-- struct sighand_struct *sighand;
- struct task_struct *p;
- unsigned long flags;
- int ret = 0;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p)
-- goto out;
-+ p = timer_lock_sighand(timer, &flags);
-
-- /*
-- * Protect against sighand release/switch in exit/exec and process/
-- * thread timer list entry concurrent read/writes.
-- */
-- sighand = lock_task_sighand(p, &flags);
-- if (unlikely(sighand == NULL)) {
-- /*
-- * This raced with the reaping of the task. The exit cleanup
-- * should have removed this timer from the timer queue.
-- */
-- WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
-- } else {
-+ if (likely(p)) {
- if (timer->it.cpu.firing)
- ret = TIMER_RETRY;
- else
-@@ -501,10 +588,8 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
- unlock_task_sighand(p, &flags);
- }
-
--out:
-- rcu_read_unlock();
- if (!ret)
-- put_pid(ctmr->pid);
-+ put_pid(timer->it.cpu.pid);
-
- return ret;
- }
-@@ -626,21 +711,17 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
- u64 old_expires, new_expires, old_incr, val;
- struct cpu_timer *ctmr = &timer->it.cpu;
-- struct sighand_struct *sighand;
- struct task_struct *p;
- unsigned long flags;
- int ret = 0;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p) {
-- /*
-- * If p has just been reaped, we can no
-- * longer get any information about it at all.
-- */
-- rcu_read_unlock();
-+ p = timer_lock_sighand(timer, &flags);
-+ /*
-+ * If p has just been reaped, we can no longer get any information about
-+ * it at all.
-+ */
-+ if (!p)
- return -ESRCH;
-- }
-
- /*
- * Use the to_ktime conversion because that clamps the maximum
-@@ -648,20 +729,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- */
- new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value));
-
-- /*
-- * Protect against sighand release/switch in exit/exec and p->cpu_timers
-- * and p->signal->cpu_timers read/write in arm_timer()
-- */
-- sighand = lock_task_sighand(p, &flags);
-- /*
-- * If p has just been reaped, we can no
-- * longer get any information about it at all.
-- */
-- if (unlikely(sighand == NULL)) {
-- rcu_read_unlock();
-- return -ESRCH;
-- }
--
- /*
- * Disarm any old timer after extracting its expiry time.
- */
-@@ -710,6 +777,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- old->it_value.tv_sec = 0;
- }
- }
-+ old->it_interval = ns_to_timespec64(old_incr);
- }
-
- if (unlikely(ret)) {
-@@ -720,7 +788,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- * it as an overrun (thanks to bump_cpu_timer above).
- */
- unlock_task_sighand(p, &flags);
-- goto out;
-+ return ret;
- }
-
- if (new_expires != 0 && !(timer_flags & TIMER_ABSTIME)) {
-@@ -733,11 +801,11 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- * arm the timer (we'll just fake it for timer_gettime).
- */
- cpu_timer_setexpires(ctmr, new_expires);
-- if (new_expires != 0 && val < new_expires) {
-+ if (new_expires != 0 && val < new_expires)
- arm_timer(timer, p);
-- }
-+ else
-+ trigger_base_recalc_expires(timer, p);
-
-- unlock_task_sighand(p, &flags);
- /*
- * Install the new reload setting, and
- * set up the signal and overrun bookkeeping.
-@@ -754,35 +822,18 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
- timer->it_overrun_last = 0;
- timer->it_overrun = -1;
-
-- if (val >= new_expires) {
-- if (new_expires != 0) {
-- /*
-- * The designated time already passed, so we notify
-- * immediately, even if the thread never runs to
-- * accumulate more time on this clock.
-- */
-- cpu_timer_fire(timer);
-- }
-+ unlock_task_sighand(p, &flags);
-
-+ if (new_expires && val >= new_expires) {
- /*
-- * Make sure we don't keep around the process wide cputime
-- * counter or the tick dependency if they are not necessary.
-+ * The designated time already passed, so we notify immediately,
-+ * even if the thread never runs to accumulate more time on this
-+ * clock.
- */
-- sighand = lock_task_sighand(p, &flags);
-- if (!sighand)
-- goto out;
--
-- if (!cpu_timer_queued(ctmr))
-- trigger_base_recalc_expires(timer, p);
--
-- unlock_task_sighand(p, &flags);
-+ cpu_timer_fire(timer);
- }
-- out:
-- rcu_read_unlock();
-- if (old)
-- old->it_interval = ns_to_timespec64(old_incr);
-
-- return ret;
-+ return 0;
- }
-
- static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp)
-@@ -1048,19 +1099,12 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
- {
- clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
- struct task_struct *p;
-- struct sighand_struct *sighand;
- unsigned long flags;
- u64 now;
-
-- rcu_read_lock();
-- p = cpu_timer_task_rcu(timer);
-- if (!p)
-- goto out;
--
-- /* Protect timer list r/w in arm_timer() */
-- sighand = lock_task_sighand(p, &flags);
-- if (unlikely(sighand == NULL))
-- goto out;
-+ p = timer_lock_sighand(timer, &flags);
-+ if (unlikely(!p))
-+ return;
-
- /*
- * Fetch the current sample and update the timer's expiry time.
-@@ -1077,8 +1121,6 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
- */
- arm_timer(timer, p);
- unlock_task_sighand(p, &flags);
--out:
-- rcu_read_unlock();
- }
-
- /**
---
-2.53.0
-
usb-serial-io_edgeport-cap-received-transmit-credits.patch
usb-serial-keyspan_pda-fix-data-loss-on-receive-throttling.patch
usb-serial-option-add-tdtech-mt5710-cn.patch
-posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch
revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch
risc-v-kvm-serialize-virtual-interrupt-pending-state.patch