]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
drop patches already applied
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 30 Jul 2026 11:19:26 +0000 (13:19 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 30 Jul 2026 11:19:26 +0000 (13:19 +0200)
13 files changed:
queue-5.10/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch [deleted file]
queue-5.10/series
queue-5.15/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch [deleted file]
queue-5.15/series
queue-6.1/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch [deleted file]
queue-6.1/series
queue-6.12/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch [deleted file]
queue-6.12/series
queue-6.18/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch [deleted file]
queue-6.18/posix-timers-expand-timer_-re-arm-callbacks-with-a-b.patch [deleted file]
queue-6.18/series
queue-6.6/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch [deleted file]
queue-6.6/series

diff --git a/queue-5.10/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch b/queue-5.10/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
deleted file mode 100644 (file)
index 33dbf29..0000000
+++ /dev/null
@@ -1,458 +0,0 @@
-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
-
index 339f1739a3712512c3513c5fa34e9889dce04c29..276166d83ab5f8bd604598a5f0e5b18c30a95f59 100644 (file)
@@ -55,7 +55,6 @@ usb-gadget-uvc-clamp-send_response-length-to-the-response-buffer.patch
 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
diff --git a/queue-5.15/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch b/queue-5.15/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
deleted file mode 100644 (file)
index feb8a25..0000000
+++ /dev/null
@@ -1,486 +0,0 @@
-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
-
index 7259ea01363051a3fab92e0103f5fe8a126d7bef..74221b65e648ca3e91bf53fa0190e910780c08da 100644 (file)
@@ -71,7 +71,6 @@ usb-serial-ftdi_sio-add-support-for-e-h-fxa291.patch
 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
diff --git a/queue-6.1/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch b/queue-6.1/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
deleted file mode 100644 (file)
index b7bafc9..0000000
+++ /dev/null
@@ -1,486 +0,0 @@
-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
-
index 4e274c94867f37cc48fc8637cf3ab3c9a91d0c23..d48bad259b7ba5e94b99d330050cdd83ac228518 100644 (file)
@@ -85,7 +85,6 @@ usb-serial-ftdi_sio-add-support-for-e-h-fxa291.patch
 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
diff --git a/queue-6.12/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch b/queue-6.12/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
deleted file mode 100644 (file)
index 56080dc..0000000
+++ /dev/null
@@ -1,429 +0,0 @@
-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
-
index f9017c16701422a20f0ef105690cf8ba094091ba..dabd02514d244bfbc748cfec9c747cfda2e6fe2e 100644 (file)
@@ -33,7 +33,6 @@ xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
 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
diff --git a/queue-6.18/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch b/queue-6.18/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
deleted file mode 100644 (file)
index 2fb0cdb..0000000
+++ /dev/null
@@ -1,419 +0,0 @@
-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
-
diff --git a/queue-6.18/posix-timers-expand-timer_-re-arm-callbacks-with-a-b.patch b/queue-6.18/posix-timers-expand-timer_-re-arm-callbacks-with-a-b.patch
deleted file mode 100644 (file)
index bbd9307..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-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
-
index 1d45ec6d6c9ecb0c303e027de129e85ad2984349..8846b64317da0f7bcd03bdad1347e4992d60c614 100644 (file)
@@ -1,5 +1,3 @@
-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
diff --git a/queue-6.6/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch b/queue-6.6/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
deleted file mode 100644 (file)
index 4024523..0000000
+++ /dev/null
@@ -1,486 +0,0 @@
-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
-
index b836ba01ace316f87640c3e3057ceffb31395591..a5afa24d6f694f0ba1fd76a3a690e2d3bb7a0684 100644 (file)
@@ -108,7 +108,6 @@ usb-serial-ftdi_sio-add-support-for-e-h-fxa291.patch
 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