]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees master
authorSasha Levin <sashal@kernel.org>
Mon, 27 Jul 2026 22:18:58 +0000 (18:18 -0400)
committerSasha Levin <sashal@kernel.org>
Mon, 27 Jul 2026 22:18:58 +0000 (18:18 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
29 files changed:
queue-5.10/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch [new file with mode: 0644]
queue-5.10/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch [new file with mode: 0644]
queue-5.10/series
queue-5.15/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch [new file with mode: 0644]
queue-5.15/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch [new file with mode: 0644]
queue-5.15/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch [new file with mode: 0644]
queue-5.15/series
queue-6.1/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch [new file with mode: 0644]
queue-6.1/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch [new file with mode: 0644]
queue-6.1/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch [new file with mode: 0644]
queue-6.1/series
queue-6.12/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch [new file with mode: 0644]
queue-6.12/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch [new file with mode: 0644]
queue-6.12/series
queue-6.18/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch [new file with mode: 0644]
queue-6.18/risc-v-kvm-serialize-virtual-interrupt-pending-state.patch [new file with mode: 0644]
queue-6.18/series
queue-6.18/wifi-mwifiex-fix-freeze-for-60-seconds-caused-by-req.patch [new file with mode: 0644]
queue-6.6/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch [new file with mode: 0644]
queue-6.6/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch [new file with mode: 0644]
queue-6.6/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch [new file with mode: 0644]
queue-6.6/series
queue-7.1/drm-amd-display-add-dp_skip_rbr-flag-for-nutmeg.patch [new file with mode: 0644]
queue-7.1/drm-amd-display-fix-ism-dc_lock-deadlock-during-susp.patch [new file with mode: 0644]
queue-7.1/drm-amd-display-fix-preferred-link-rate-for-nutmeg.patch [new file with mode: 0644]
queue-7.1/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch [new file with mode: 0644]
queue-7.1/risc-v-kvm-serialize-virtual-interrupt-pending-state.patch [new file with mode: 0644]
queue-7.1/series
queue-7.1/wifi-mwifiex-fix-freeze-for-60-seconds-caused-by-req.patch [new file with mode: 0644]

diff --git a/queue-5.10/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch b/queue-5.10/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch
new file mode 100644 (file)
index 0000000..bee6297
--- /dev/null
@@ -0,0 +1,55 @@
+From 0d9ae5875a76227ceda38aab66bef549e6a0b28a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:15:33 +0200
+Subject: crypto: rsa-pkcs1pad: Don't WARN on an empty digest
+
+From: Doruk Tan Ozturk <doruk@0sec.ai>
+
+KEYCTL_PKEY_VERIFY lets an unprivileged caller supply a zero-length
+digest (in_len == 0).  keyctl_pkey_params_get_2() accepts the zero
+length and the request reaches pkcs1pad_verify(), where the empty
+digest is rejected but only after being passed through
+WARN_ON(!req->dst_len).  The warning is therefore directly
+user-triggerable, and on kernels built with panic_on_warn=1 an
+unprivileged process can panic the machine -- a local denial of
+service.  Reproduced as UID 65534 in a setuid sandbox.
+
+Keep rejecting the invalid request with -EINVAL, but do not emit a
+warning for the user-controlled length.
+
+This is the 5.10.y/5.15.y form of the fix, where the length is read
+directly from req->dst_len rather than cached in a digest_size local.
+Mainline does not contain this code path; commit 1e562deacecc
+("crypto: rsassa-pkcs1 - Migrate to sig_alg backend") removed
+pkcs1pad_verify() in v6.13-rc1.  This is a minimal fix for the
+affected stable branches.  The 6.1.y/6.6.y/6.12.y form
+(WARN_ON(!digest_size)) is sent as a separate patch.
+
+Found by 0sec automated security-research tooling (https://0sec.ai).
+
+Fixes: c7381b012872 ("crypto: akcipher - new verify API for public key algorithms")
+Cc: stable@vger.kernel.org
+Assisted-by: 0sec:multi-model
+Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
+Reviewed-by: Lukas Wunner <lukas@wunner.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ crypto/rsa-pkcs1pad.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c
+index a4ebbb889274eb..1cde1e7c9ae43d 100644
+--- a/crypto/rsa-pkcs1pad.c
++++ b/crypto/rsa-pkcs1pad.c
+@@ -535,7 +535,7 @@ static int pkcs1pad_verify(struct akcipher_request *req)
+       int err;
+       if (WARN_ON(req->dst) ||
+-          WARN_ON(!req->dst_len) ||
++          !req->dst_len ||
+           !ctx->key_size || req->src_len != ctx->key_size)
+               return -EINVAL;
+-- 
+2.53.0
+
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
new file mode 100644 (file)
index 0000000..33dbf29
--- /dev/null
@@ -0,0 +1,458 @@
+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 8dafb6c3db965ed4a5d54b6584650f1a3884314f..10dd46bc181ae9418e67114553664f08283f5555 100644 (file)
@@ -55,3 +55,5 @@ 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
diff --git a/queue-5.15/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch b/queue-5.15/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch
new file mode 100644 (file)
index 0000000..19629d4
--- /dev/null
@@ -0,0 +1,55 @@
+From 454c9876d7c1741a946cba716a1dec8633f3b319 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:15:33 +0200
+Subject: crypto: rsa-pkcs1pad: Don't WARN on an empty digest
+
+From: Doruk Tan Ozturk <doruk@0sec.ai>
+
+KEYCTL_PKEY_VERIFY lets an unprivileged caller supply a zero-length
+digest (in_len == 0).  keyctl_pkey_params_get_2() accepts the zero
+length and the request reaches pkcs1pad_verify(), where the empty
+digest is rejected but only after being passed through
+WARN_ON(!req->dst_len).  The warning is therefore directly
+user-triggerable, and on kernels built with panic_on_warn=1 an
+unprivileged process can panic the machine -- a local denial of
+service.  Reproduced as UID 65534 in a setuid sandbox.
+
+Keep rejecting the invalid request with -EINVAL, but do not emit a
+warning for the user-controlled length.
+
+This is the 5.10.y/5.15.y form of the fix, where the length is read
+directly from req->dst_len rather than cached in a digest_size local.
+Mainline does not contain this code path; commit 1e562deacecc
+("crypto: rsassa-pkcs1 - Migrate to sig_alg backend") removed
+pkcs1pad_verify() in v6.13-rc1.  This is a minimal fix for the
+affected stable branches.  The 6.1.y/6.6.y/6.12.y form
+(WARN_ON(!digest_size)) is sent as a separate patch.
+
+Found by 0sec automated security-research tooling (https://0sec.ai).
+
+Fixes: c7381b012872 ("crypto: akcipher - new verify API for public key algorithms")
+Cc: stable@vger.kernel.org
+Assisted-by: 0sec:multi-model
+Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
+Reviewed-by: Lukas Wunner <lukas@wunner.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ crypto/rsa-pkcs1pad.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c
+index e2f4ccbd71dd87..d10076bb6ec237 100644
+--- a/crypto/rsa-pkcs1pad.c
++++ b/crypto/rsa-pkcs1pad.c
+@@ -535,7 +535,7 @@ static int pkcs1pad_verify(struct akcipher_request *req)
+       int err;
+       if (WARN_ON(req->dst) ||
+-          WARN_ON(!req->dst_len) ||
++          !req->dst_len ||
+           !ctx->key_size || req->src_len != ctx->key_size)
+               return -EINVAL;
+-- 
+2.53.0
+
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
new file mode 100644 (file)
index 0000000..feb8a25
--- /dev/null
@@ -0,0 +1,486 @@
+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
+
diff --git a/queue-5.15/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch b/queue-5.15/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch
new file mode 100644 (file)
index 0000000..2d6a028
--- /dev/null
@@ -0,0 +1,28 @@
+From 05cc7eb7bc9503c4765275d64ca933fef4823ed0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 15:59:57 -0400
+Subject: Revert "drm/amd/display: Add missing kdoc for ALLM parameters"
+
+This reverts commit 904eed5c1044b52db948e0322a5f74ccaccd0b8e.
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+index c09e86523e291e..4e075b01d48bb4 100644
+--- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
++++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+@@ -415,8 +415,6 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream,
+  *
+  *  @stream:      contains data we may need to construct VSIF (i.e. timing_3d_format, etc.)
+  *  @info_packet: output structure where to store VSIF
+- *  @ALLMEnabled: indicates whether ALLM HF-VSIF should be generated
+- *  @ALLMValue:   ALLM bit value to advertise in HF-VSIF
+  */
+ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
+               struct dc_info_packet *info_packet)
+-- 
+2.53.0
+
index de822494fa1f149c5cd8524e07d2ba229427e8f7..bbfea89b7238e680832292e5fe452b1814c320ee 100644 (file)
@@ -71,3 +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
diff --git a/queue-6.1/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch b/queue-6.1/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch
new file mode 100644 (file)
index 0000000..b60bf6b
--- /dev/null
@@ -0,0 +1,54 @@
+From 52e95aa47635f9753c8259103a09509f94190efe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:15:25 +0200
+Subject: crypto: rsa-pkcs1pad: Don't WARN on an empty digest
+
+From: Doruk Tan Ozturk <doruk@0sec.ai>
+
+KEYCTL_PKEY_VERIFY lets an unprivileged caller supply a zero-length
+digest (in_len == 0).  keyctl_pkey_params_get_2() accepts the zero
+length and the request reaches pkcs1pad_verify(), where the empty
+digest is rejected but only after being passed through
+WARN_ON(!digest_size).  The warning is therefore directly
+user-triggerable, and on kernels built with panic_on_warn=1 an
+unprivileged process can panic the machine -- a local denial of
+service.  Reproduced as UID 65534 in a setuid sandbox.
+
+Keep rejecting the invalid request with -EINVAL, but do not emit a
+warning for the user-controlled length.
+
+Mainline does not contain this code path; commit 1e562deacecc
+("crypto: rsassa-pkcs1 - Migrate to sig_alg backend") removed
+pkcs1pad_verify() in v6.13-rc1.  This is a minimal fix for the
+affected stable branches.  It applies as-is to 6.1.y, 6.6.y and
+6.12.y (identical pkcs1pad_verify); the 5.10.y/5.15.y form is sent
+as a separate patch due to the older req->dst_len spelling.
+
+Found by 0sec automated security-research tooling (https://0sec.ai).
+
+Fixes: c7381b012872 ("crypto: akcipher - new verify API for public key algorithms")
+Cc: stable@vger.kernel.org
+Assisted-by: 0sec:multi-model
+Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
+Reviewed-by: Lukas Wunner <lukas@wunner.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ crypto/rsa-pkcs1pad.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c
+index 1cf267bc6f9ea1..5d732977bba0d4 100644
+--- a/crypto/rsa-pkcs1pad.c
++++ b/crypto/rsa-pkcs1pad.c
+@@ -537,7 +537,7 @@ static int pkcs1pad_verify(struct akcipher_request *req)
+       const unsigned int digest_size = req->dst_len;
+       int err;
+-      if (WARN_ON(req->dst) || WARN_ON(!digest_size) ||
++      if (WARN_ON(req->dst) || !digest_size ||
+           !ctx->key_size || sig_size != ctx->key_size)
+               return -EINVAL;
+-- 
+2.53.0
+
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
new file mode 100644 (file)
index 0000000..b7bafc9
--- /dev/null
@@ -0,0 +1,486 @@
+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
+
diff --git a/queue-6.1/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch b/queue-6.1/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch
new file mode 100644 (file)
index 0000000..5525f95
--- /dev/null
@@ -0,0 +1,28 @@
+From fd28293b7ce9a579e22ab31fe6577319a6051bb5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:07:47 -0400
+Subject: Revert "drm/amd/display: Add missing kdoc for ALLM parameters"
+
+This reverts commit 7cb4e8ba78f96980a23be4414c8f22f417c814fa.
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+index 6602ac882d6bce..69691058ab8981 100644
+--- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
++++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+@@ -445,8 +445,6 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream,
+  *
+  *  @stream:      contains data we may need to construct VSIF (i.e. timing_3d_format, etc.)
+  *  @info_packet: output structure where to store VSIF
+- *  @ALLMEnabled: indicates whether ALLM HF-VSIF should be generated
+- *  @ALLMValue:   ALLM bit value to advertise in HF-VSIF
+  */
+ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
+               struct dc_info_packet *info_packet)
+-- 
+2.53.0
+
index ccdfdc8ccbb6983d5e41c307b24a26a3e150bb49..c486cc47b1a7c33b8ede12275238d7108b9e9c64 100644 (file)
@@ -86,3 +86,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
diff --git a/queue-6.12/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch b/queue-6.12/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch
new file mode 100644 (file)
index 0000000..2a4cfca
--- /dev/null
@@ -0,0 +1,54 @@
+From cff2dec617f7e17f9689a967791c295b0058e1e4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:15:25 +0200
+Subject: crypto: rsa-pkcs1pad: Don't WARN on an empty digest
+
+From: Doruk Tan Ozturk <doruk@0sec.ai>
+
+KEYCTL_PKEY_VERIFY lets an unprivileged caller supply a zero-length
+digest (in_len == 0).  keyctl_pkey_params_get_2() accepts the zero
+length and the request reaches pkcs1pad_verify(), where the empty
+digest is rejected but only after being passed through
+WARN_ON(!digest_size).  The warning is therefore directly
+user-triggerable, and on kernels built with panic_on_warn=1 an
+unprivileged process can panic the machine -- a local denial of
+service.  Reproduced as UID 65534 in a setuid sandbox.
+
+Keep rejecting the invalid request with -EINVAL, but do not emit a
+warning for the user-controlled length.
+
+Mainline does not contain this code path; commit 1e562deacecc
+("crypto: rsassa-pkcs1 - Migrate to sig_alg backend") removed
+pkcs1pad_verify() in v6.13-rc1.  This is a minimal fix for the
+affected stable branches.  It applies as-is to 6.1.y, 6.6.y and
+6.12.y (identical pkcs1pad_verify); the 5.10.y/5.15.y form is sent
+as a separate patch due to the older req->dst_len spelling.
+
+Found by 0sec automated security-research tooling (https://0sec.ai).
+
+Fixes: c7381b012872 ("crypto: akcipher - new verify API for public key algorithms")
+Cc: stable@vger.kernel.org
+Assisted-by: 0sec:multi-model
+Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
+Reviewed-by: Lukas Wunner <lukas@wunner.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ crypto/rsa-pkcs1pad.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c
+index cd501195f34a1a..225fcc3377dddc 100644
+--- a/crypto/rsa-pkcs1pad.c
++++ b/crypto/rsa-pkcs1pad.c
+@@ -557,7 +557,7 @@ static int pkcs1pad_verify(struct akcipher_request *req)
+       const unsigned int digest_size = req->dst_len;
+       int err;
+-      if (WARN_ON(req->dst) || WARN_ON(!digest_size) ||
++      if (WARN_ON(req->dst) || !digest_size ||
+           !ctx->key_size || sig_size != ctx->key_size)
+               return -EINVAL;
+-- 
+2.53.0
+
diff --git a/queue-6.12/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch b/queue-6.12/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch
new file mode 100644 (file)
index 0000000..693735f
--- /dev/null
@@ -0,0 +1,28 @@
+From 5b53041b98ddb54ee9093c538ae394e2240738da Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:07:38 -0400
+Subject: Revert "drm/amd/display: Add missing kdoc for ALLM parameters"
+
+This reverts commit 8fa292bd7ea0c19a6f87db967db823f48542e170.
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+index ae858a43c35f4a..b3d55cac35694b 100644
+--- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
++++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+@@ -447,8 +447,6 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream,
+  *
+  *  @stream:      contains data we may need to construct VSIF (i.e. timing_3d_format, etc.)
+  *  @info_packet: output structure where to store VSIF
+- *  @ALLMEnabled: indicates whether ALLM HF-VSIF should be generated
+- *  @ALLMValue:   ALLM bit value to advertise in HF-VSIF
+  */
+ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
+               struct dc_info_packet *info_packet)
+-- 
+2.53.0
+
index 6f5e76180d2ec88da263501075b7c3b82bf3bac0..353924a5cf2c5f5fbcecf6cb1e0279e1b4429253 100644 (file)
@@ -129,3 +129,5 @@ 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
+crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch
+revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch
diff --git a/queue-6.18/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch b/queue-6.18/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch
new file mode 100644 (file)
index 0000000..50c10e1
--- /dev/null
@@ -0,0 +1,28 @@
+From f0815ee7d12891ba10f97521d90cdd74d962ea51 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:07:33 -0400
+Subject: Revert "drm/amd/display: Add missing kdoc for ALLM parameters"
+
+This reverts commit 038a0f01dda595dceda8f0c9c5172b1297281929.
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+index ae858a43c35f4a..b3d55cac35694b 100644
+--- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
++++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+@@ -447,8 +447,6 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream,
+  *
+  *  @stream:      contains data we may need to construct VSIF (i.e. timing_3d_format, etc.)
+  *  @info_packet: output structure where to store VSIF
+- *  @ALLMEnabled: indicates whether ALLM HF-VSIF should be generated
+- *  @ALLMValue:   ALLM bit value to advertise in HF-VSIF
+  */
+ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
+               struct dc_info_packet *info_packet)
+-- 
+2.53.0
+
diff --git a/queue-6.18/risc-v-kvm-serialize-virtual-interrupt-pending-state.patch b/queue-6.18/risc-v-kvm-serialize-virtual-interrupt-pending-state.patch
new file mode 100644 (file)
index 0000000..525db07
--- /dev/null
@@ -0,0 +1,396 @@
+From c041b3a03114b22703795ec90ef46da5f49b287b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 10:42:22 +0800
+Subject: RISC-V: KVM: Serialize virtual interrupt pending state updates
+
+From: Xie Bo <xb@ultrarisc.com>
+
+commit d024a0a7879e6f37c0152aacf6d8e37b214a1738 upstream.
+
+KVM RISC-V tracks guest local interrupt state with two bitmaps:
+
+  - irqs_pending: interrupts that should be visible to the guest
+  - irqs_pending_mask: interrupts whose pending state changed
+
+The current code updates those bitmaps with independent atomic bitops
+and assumes a multiple-producer, single-consumer protocol. That model
+does not actually hold.
+
+kvm_riscv_vcpu_sync_interrupts() is not a pure consumer. When the guest
+changes guest-visible HVIP state, sync_interrupts() writes both
+irqs_pending and irqs_pending_mask to reflect the new guest state back
+into KVM state. As a result, irqs_pending and irqs_pending_mask form a
+single logical state transition, but they are not updated atomically as
+a pair.
+
+This allows a race where a newly injected interrupt is lost. For
+example:
+
+  CPU0                              CPU1
+  ----                              ----
+  kvm_riscv_vcpu_set_interrupt(VS_SOFT)
+    set_bit(VS_SOFT, irqs_pending)
+                                    kvm_riscv_vcpu_sync_interrupts()
+                                      sees guest-cleared HVIP.VSSIP
+                                      sets irqs_pending_mask
+                                      clear_bit(IRQ_VS_SOFT, irqs_pending)
+    set_bit(VS_SOFT, irqs_pending_mask)
+    kvm_vcpu_kick()
+
+After that interleaving, a later flush can update HVIP without VSSIP
+even though a new virtual interrupt was injected. In practice, the
+guest can remain blocked in WFI with work pending.
+
+The same pending/mask protocol is shared by VS soft interrupts, PMU
+overflow delivery, and AIA high interrupt synchronization, so the race
+is not limited to one interrupt source.
+
+Fix this by serializing all updates to irqs_pending and irqs_pending_mask
+with a per-vCPU raw spinlock. This keeps the pending bit and the dirty
+mask as one state transition across:
+
+  - set/unset interrupt
+  - guest HVIP sync
+  - interrupt flush to guest CSR state
+  - vCPU reset
+  - AIA CSR writes that clear dirty state
+
+Use non-atomic bitmap operations while holding the lock. Hold the lock
+across the AIA sync, flush, and pending checks as well, so both bitmap
+words share the same serialization domain.
+
+This intentionally replaces the existing lockless protocol instead of
+trying to repair it with additional barriers. The problem is not memory
+ordering on a single field; it is that two separate bitmaps encode one
+shared state machine while both producers and sync paths can modify
+them. A per-vCPU raw spinlock keeps the fix small, local, and suitable
+for backporting.
+
+Fixes: cce69aff689e ("RISC-V: KVM: Implement VCPU interrupts and requests handling")
+Cc: stable@vger.kernel.org
+Signed-off-by: Xie Bo <xb@ultrarisc.com>
+Reviewed-by: Anup Patel <anup@brainfault.org>
+Link: https://lore.kernel.org/r/20260715020359.1521354-2-xb@ultrarisc.com
+Signed-off-by: Anup Patel <anup@brainfault.org>
+
+[ bo: Adapt the AIA and general CSR helpers to their 6.18.y layout. ]
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/riscv/include/asm/kvm_host.h | 10 ++---
+ arch/riscv/kvm/aia.c              | 35 ++++++++++++----
+ arch/riscv/kvm/vcpu.c             | 68 ++++++++++++++++++++++---------
+ arch/riscv/kvm/vcpu_onereg.c      |  8 +++-
+ 4 files changed, 87 insertions(+), 34 deletions(-)
+
+diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h
+index 4d794573e3dbe8..fd3ba183cd8241 100644
+--- a/arch/riscv/include/asm/kvm_host.h
++++ b/arch/riscv/include/asm/kvm_host.h
+@@ -220,13 +220,13 @@ struct kvm_vcpu_arch {
+       /*
+        * VCPU interrupts
+        *
+-       * We have a lockless approach for tracking pending VCPU interrupts
+-       * implemented using atomic bitops. The irqs_pending bitmap represent
+-       * pending interrupts whereas irqs_pending_mask represent bits changed
+-       * in irqs_pending. Our approach is modeled around multiple producer
+-       * and single consumer problem where the consumer is the VCPU itself.
++       * The irqs_pending bitmap represents pending interrupts whereas
++       * irqs_pending_mask represents bits changed in irqs_pending. Updates
++       * to these bitmaps are serialized so vcpu interrupt sync/flush cannot
++       * drop a newly injected interrupt while syncing guest-visible HVIP.
+        */
+ #define KVM_RISCV_VCPU_NR_IRQS        64
++      raw_spinlock_t irqs_pending_lock;
+       DECLARE_BITMAP(irqs_pending, KVM_RISCV_VCPU_NR_IRQS);
+       DECLARE_BITMAP(irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS);
+diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c
+index dad3181856600f..b8b50608215564 100644
+--- a/arch/riscv/kvm/aia.c
++++ b/arch/riscv/kvm/aia.c
+@@ -50,12 +50,15 @@ void kvm_riscv_vcpu_aia_flush_interrupts(struct kvm_vcpu *vcpu)
+       struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
+       unsigned long mask, val;
++      lockdep_assert_held(&vcpu->arch.irqs_pending_lock);
++
+       if (!kvm_riscv_aia_available())
+               return;
+-      if (READ_ONCE(vcpu->arch.irqs_pending_mask[1])) {
+-              mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[1], 0);
+-              val = READ_ONCE(vcpu->arch.irqs_pending[1]) & mask;
++      mask = vcpu->arch.irqs_pending_mask[1];
++      if (mask) {
++              vcpu->arch.irqs_pending_mask[1] = 0;
++              val = vcpu->arch.irqs_pending[1] & mask;
+               csr->hviph &= ~mask;
+               csr->hviph |= val;
+@@ -66,6 +69,8 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu)
+ {
+       struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
++      lockdep_assert_held(&vcpu->arch.irqs_pending_lock);
++
+       if (kvm_riscv_aia_available())
+               csr->vsieh = ncsr_read(CSR_VSIEH);
+ }
+@@ -74,13 +79,22 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu)
+ bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu, u64 mask)
+ {
+       unsigned long seip;
++#ifdef CONFIG_32BIT
++      unsigned long flags;
++      bool pending;
++#endif
+       if (!kvm_riscv_aia_available())
+               return false;
+ #ifdef CONFIG_32BIT
+-      if (READ_ONCE(vcpu->arch.irqs_pending[1]) &
+-          (vcpu->arch.aia_context.guest_csr.vsieh & upper_32_bits(mask)))
++      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++      pending = vcpu->arch.irqs_pending[1] &
++                (vcpu->arch.aia_context.guest_csr.vsieh &
++                 upper_32_bits(mask));
++      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
++
++      if (pending)
+               return true;
+ #endif
+@@ -198,6 +212,9 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu,
+                              unsigned long val)
+ {
+       struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
++#ifdef CONFIG_32BIT
++      unsigned long flags;
++#endif
+       if (reg_num >= sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long))
+               return -ENOENT;
+@@ -206,8 +223,12 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu,
+               ((unsigned long *)csr)[reg_num] = val;
+ #ifdef CONFIG_32BIT
+-              if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph))
+-                      WRITE_ONCE(vcpu->arch.irqs_pending_mask[1], 0);
++              if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) {
++                      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++                      vcpu->arch.irqs_pending_mask[1] = 0;
++                      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock,
++                                                 flags);
++              }
+ #endif
+       }
+diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c
+index d26c4967c20e24..6f8e9b105da7e4 100644
+--- a/arch/riscv/kvm/vcpu.c
++++ b/arch/riscv/kvm/vcpu.c
+@@ -78,6 +78,7 @@ static void kvm_riscv_vcpu_context_reset(struct kvm_vcpu *vcpu,
+ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset)
+ {
++      unsigned long flags;
+       bool loaded;
+       /**
+@@ -102,8 +103,10 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset)
+       kvm_riscv_vcpu_aia_reset(vcpu);
++      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+       bitmap_zero(vcpu->arch.irqs_pending, KVM_RISCV_VCPU_NR_IRQS);
+       bitmap_zero(vcpu->arch.irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS);
++      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+       kvm_riscv_vcpu_pmu_reset(vcpu);
+@@ -147,6 +150,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
+       /* Setup VCPU hfence queue */
+       spin_lock_init(&vcpu->arch.hfence_lock);
++      raw_spin_lock_init(&vcpu->arch.irqs_pending_lock);
+       spin_lock_init(&vcpu->arch.reset_state.lock);
+@@ -348,10 +352,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu)
+ {
+       struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
+       unsigned long mask, val;
++      unsigned long flags;
+-      if (READ_ONCE(vcpu->arch.irqs_pending_mask[0])) {
+-              mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[0], 0);
+-              val = READ_ONCE(vcpu->arch.irqs_pending[0]) & mask;
++      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++
++      mask = vcpu->arch.irqs_pending_mask[0];
++      if (mask) {
++              vcpu->arch.irqs_pending_mask[0] = 0;
++              val = vcpu->arch.irqs_pending[0] & mask;
+               csr->hvip &= ~mask;
+               csr->hvip |= val;
+@@ -359,11 +367,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu)
+       /* Flush AIA high interrupts */
+       kvm_riscv_vcpu_aia_flush_interrupts(vcpu);
++
++      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+ }
+ void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu)
+ {
+       unsigned long hvip;
++      unsigned long flags;
+       struct kvm_vcpu_arch *v = &vcpu->arch;
+       struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
+@@ -372,34 +383,41 @@ void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu)
+       /* Sync-up HVIP.VSSIP bit changes does by Guest */
+       hvip = ncsr_read(CSR_HVIP);
++
++      raw_spin_lock_irqsave(&v->irqs_pending_lock, flags);
++
+       if ((csr->hvip ^ hvip) & (1UL << IRQ_VS_SOFT)) {
+               if (hvip & (1UL << IRQ_VS_SOFT)) {
+-                      if (!test_and_set_bit(IRQ_VS_SOFT,
+-                                            v->irqs_pending_mask))
+-                              set_bit(IRQ_VS_SOFT, v->irqs_pending);
++                      if (!__test_and_set_bit(IRQ_VS_SOFT,
++                                              v->irqs_pending_mask))
++                              __set_bit(IRQ_VS_SOFT, v->irqs_pending);
+               } else {
+-                      if (!test_and_set_bit(IRQ_VS_SOFT,
+-                                            v->irqs_pending_mask))
+-                              clear_bit(IRQ_VS_SOFT, v->irqs_pending);
++                      if (!__test_and_set_bit(IRQ_VS_SOFT,
++                                              v->irqs_pending_mask))
++                              __clear_bit(IRQ_VS_SOFT, v->irqs_pending);
+               }
+       }
+       /* Sync up the HVIP.LCOFIP bit changes (only clear) by the guest */
+       if ((csr->hvip ^ hvip) & (1UL << IRQ_PMU_OVF)) {
+               if (!(hvip & (1UL << IRQ_PMU_OVF)) &&
+-                  !test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask))
+-                      clear_bit(IRQ_PMU_OVF, v->irqs_pending);
++                  !__test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask))
++                      __clear_bit(IRQ_PMU_OVF, v->irqs_pending);
+       }
+       /* Sync-up AIA high interrupts */
+       kvm_riscv_vcpu_aia_sync_interrupts(vcpu);
++      raw_spin_unlock_irqrestore(&v->irqs_pending_lock, flags);
++
+       /* Sync-up timer CSRs */
+       kvm_riscv_vcpu_timer_sync(vcpu);
+ }
+ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
+ {
++      unsigned long flags;
++
+       /*
+        * We only allow VS-mode software, timer, and external
+        * interrupts when irq is one of the local interrupts
+@@ -412,9 +430,10 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
+           irq != IRQ_PMU_OVF)
+               return -EINVAL;
+-      set_bit(irq, vcpu->arch.irqs_pending);
+-      smp_mb__before_atomic();
+-      set_bit(irq, vcpu->arch.irqs_pending_mask);
++      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++      __set_bit(irq, vcpu->arch.irqs_pending);
++      __set_bit(irq, vcpu->arch.irqs_pending_mask);
++      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+       kvm_vcpu_kick(vcpu);
+@@ -423,6 +442,8 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
+ int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
+ {
++      unsigned long flags;
++
+       /*
+        * We only allow VS-mode software, timer, counter overflow and external
+        * interrupts when irq is one of the local interrupts
+@@ -435,26 +456,33 @@ int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
+           irq != IRQ_PMU_OVF)
+               return -EINVAL;
+-      clear_bit(irq, vcpu->arch.irqs_pending);
+-      smp_mb__before_atomic();
+-      set_bit(irq, vcpu->arch.irqs_pending_mask);
++      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++      __clear_bit(irq, vcpu->arch.irqs_pending);
++      __set_bit(irq, vcpu->arch.irqs_pending_mask);
++      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+       return 0;
+ }
+ bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, u64 mask)
+ {
++      unsigned long flags;
+       unsigned long ie;
++      bool ret;
++      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+       ie = ((vcpu->arch.guest_csr.vsie & VSIP_VALID_MASK)
+               << VSIP_TO_HVIP_SHIFT) & (unsigned long)mask;
+       ie |= vcpu->arch.guest_csr.vsie & ~IRQ_LOCAL_MASK &
+               (unsigned long)mask;
+-      if (READ_ONCE(vcpu->arch.irqs_pending[0]) & ie)
+-              return true;
++      ret = vcpu->arch.irqs_pending[0] & ie;
++      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+       /* Check AIA high interrupts */
+-      return kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask);
++      if (!ret)
++              ret = kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask);
++
++      return ret;
+ }
+ void __kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu)
+diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c
+index 865dae903aa0f6..3e8f9a28341215 100644
+--- a/arch/riscv/kvm/vcpu_onereg.c
++++ b/arch/riscv/kvm/vcpu_onereg.c
+@@ -522,6 +522,7 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu,
+                                         unsigned long reg_val)
+ {
+       struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
++      unsigned long flags;
+       if (reg_num >= sizeof(struct kvm_riscv_csr) / sizeof(unsigned long))
+               return -ENOENT;
+@@ -533,8 +534,11 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu,
+       ((unsigned long *)csr)[reg_num] = reg_val;
+-      if (reg_num == KVM_REG_RISCV_CSR_REG(sip))
+-              WRITE_ONCE(vcpu->arch.irqs_pending_mask[0], 0);
++      if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) {
++              raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++              vcpu->arch.irqs_pending_mask[0] = 0;
++              raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
++      }
+       return 0;
+ }
+-- 
+2.53.0
+
index bcdded23bd05e7240fbb8771049577374be11975..6577cefe1acea1c0504a192e6f82bbad2f4b22e4 100644 (file)
@@ -163,3 +163,6 @@ usb-serial-keyspan_pda-fix-data-loss-on-receive-throttling.patch
 usb-serial-option-add-tdtech-mt5710-cn.patch
 usb-typec-ucsi-detect-and-skip-duplicate-altmodes-from-buggy-firmware.patch
 usb-typec-ucsi-add-duplicate-detection-to-nvidia-registration-path.patch
+wifi-mwifiex-fix-freeze-for-60-seconds-caused-by-req.patch
+risc-v-kvm-serialize-virtual-interrupt-pending-state.patch
+revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch
diff --git a/queue-6.18/wifi-mwifiex-fix-freeze-for-60-seconds-caused-by-req.patch b/queue-6.18/wifi-mwifiex-fix-freeze-for-60-seconds-caused-by-req.patch
new file mode 100644 (file)
index 0000000..c422976
--- /dev/null
@@ -0,0 +1,78 @@
+From 85998c10fa1ae7be3ebed1b9702a829ef4914e0a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 01:17:09 +0300
+Subject: wifi: mwifiex: fix freeze for 60 seconds caused by request_firmware
+
+From: Georgi Valkov <gvalkov@gmail.com>
+
+[ Upstream commit 121a96c5a0db8d18e2ba2cb89660cca8a40508fe ]
+
+Fix regression in rgpower table loading, caused by using
+request_firmware(): when the requested firmware does not exist, e.g.
+nxp/rgpower_WW.bin does not exist on OpenWRT builds for WRT3200ACM,
+request_firmware() falls back to firmware_fallback_sysfs(), which expects
+the firmware to be provided by user space using SYSFS. No such utility is
+provided in this configuration, so the entire system locks up for 60
+seconds, until the request times out. During this time, no other log
+messages are observed, and the device does not respond to commands over
+UART.
+
+The request_firmware() call is performed in the following context:
+current->comm kworker/1:2  in_task 1  irqs_disabled 0  in_atomic 0
+
+Fixed by using request_firmware_direct(). This prevents fallback to SYSFS,
+and avoids delay. The rgpower table is optional. The driver falls back
+to the device tree power table if the firmware is not present.
+
+The error code is printed for debugging and returned to the caller,
+which only cares for success or failure, so there are no side effects.
+
+Fixes: 7b6f16a25806 ("wifi: mwifiex: add rgpower table loading support")
+Signed-off-by: Georgi Valkov <gvalkov@gmail.com>
+Reviewed-by: Francesco Dolcini <francesco.dolcini@toradex.com>
+Link: https://patch.msgid.link/20260712221709.7099-1-gvalkov@gmail.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
+index ef6722ffdc74d8..358de94eeb5ed9 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
++++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
+@@ -196,6 +196,7 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv)
+       struct mwifiex_adapter *adapter = priv->adapter;
+       char rgpower_table_name[30];
+       char country_code[3];
++      int ret;
+       strscpy(country_code, domain_info->country_code, sizeof(country_code));
+@@ -214,16 +215,17 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv)
+               adapter->rgpower_data = NULL;
+       }
+-      if ((request_firmware(&adapter->rgpower_data, rgpower_table_name,
+-                            adapter->dev))) {
++      ret = request_firmware_direct(&adapter->rgpower_data, rgpower_table_name,
++                                    adapter->dev);
++
++      if (ret) {
+               mwifiex_dbg(
+                       adapter, INFO,
+-                      "info: %s: failed to request regulatory power table\n",
+-                      __func__);
+-              return -EIO;
++                      "info: %s: failed to request regulatory power table: %d\n",
++                      __func__, ret);
+       }
+-      return 0;
++      return ret;
+ }
+ static int mwifiex_dnld_rgpower_table(struct mwifiex_private *priv)
+-- 
+2.53.0
+
diff --git a/queue-6.6/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch b/queue-6.6/crypto-rsa-pkcs1pad-don-t-warn-on-an-empty-digest.patch
new file mode 100644 (file)
index 0000000..150de7b
--- /dev/null
@@ -0,0 +1,54 @@
+From 9890232304d9dde9d7d2b6ee97eddb69fbb55fc9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:15:25 +0200
+Subject: crypto: rsa-pkcs1pad: Don't WARN on an empty digest
+
+From: Doruk Tan Ozturk <doruk@0sec.ai>
+
+KEYCTL_PKEY_VERIFY lets an unprivileged caller supply a zero-length
+digest (in_len == 0).  keyctl_pkey_params_get_2() accepts the zero
+length and the request reaches pkcs1pad_verify(), where the empty
+digest is rejected but only after being passed through
+WARN_ON(!digest_size).  The warning is therefore directly
+user-triggerable, and on kernels built with panic_on_warn=1 an
+unprivileged process can panic the machine -- a local denial of
+service.  Reproduced as UID 65534 in a setuid sandbox.
+
+Keep rejecting the invalid request with -EINVAL, but do not emit a
+warning for the user-controlled length.
+
+Mainline does not contain this code path; commit 1e562deacecc
+("crypto: rsassa-pkcs1 - Migrate to sig_alg backend") removed
+pkcs1pad_verify() in v6.13-rc1.  This is a minimal fix for the
+affected stable branches.  It applies as-is to 6.1.y, 6.6.y and
+6.12.y (identical pkcs1pad_verify); the 5.10.y/5.15.y form is sent
+as a separate patch due to the older req->dst_len spelling.
+
+Found by 0sec automated security-research tooling (https://0sec.ai).
+
+Fixes: c7381b012872 ("crypto: akcipher - new verify API for public key algorithms")
+Cc: stable@vger.kernel.org
+Assisted-by: 0sec:multi-model
+Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
+Reviewed-by: Lukas Wunner <lukas@wunner.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ crypto/rsa-pkcs1pad.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c
+index d2e5e104f8cfe3..8172f0b0b481d7 100644
+--- a/crypto/rsa-pkcs1pad.c
++++ b/crypto/rsa-pkcs1pad.c
+@@ -534,7 +534,7 @@ static int pkcs1pad_verify(struct akcipher_request *req)
+       const unsigned int digest_size = req->dst_len;
+       int err;
+-      if (WARN_ON(req->dst) || WARN_ON(!digest_size) ||
++      if (WARN_ON(req->dst) || !digest_size ||
+           !ctx->key_size || sig_size != ctx->key_size)
+               return -EINVAL;
+-- 
+2.53.0
+
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
new file mode 100644 (file)
index 0000000..4024523
--- /dev/null
@@ -0,0 +1,486 @@
+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
+
diff --git a/queue-6.6/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch b/queue-6.6/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch
new file mode 100644 (file)
index 0000000..2bd7d9b
--- /dev/null
@@ -0,0 +1,28 @@
+From 7429cdc8ea69912041d7fb5b68abdf9f8a2ac553 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:07:42 -0400
+Subject: Revert "drm/amd/display: Add missing kdoc for ALLM parameters"
+
+This reverts commit bca364c4c75796650e53837fbec174a866141511.
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+index dbb62b911c75c1..84f9b412a4f117 100644
+--- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
++++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+@@ -447,8 +447,6 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream,
+  *
+  *  @stream:      contains data we may need to construct VSIF (i.e. timing_3d_format, etc.)
+  *  @info_packet: output structure where to store VSIF
+- *  @ALLMEnabled: indicates whether ALLM HF-VSIF should be generated
+- *  @ALLMValue:   ALLM bit value to advertise in HF-VSIF
+  */
+ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
+               struct dc_info_packet *info_packet)
+-- 
+2.53.0
+
index bcd941e8deab11f937d32c679feb5e079188324e..39f8e6224635649aad0ab3247dca4335aa866bda 100644 (file)
@@ -109,3 +109,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
diff --git a/queue-7.1/drm-amd-display-add-dp_skip_rbr-flag-for-nutmeg.patch b/queue-7.1/drm-amd-display-add-dp_skip_rbr-flag-for-nutmeg.patch
new file mode 100644 (file)
index 0000000..b503359
--- /dev/null
@@ -0,0 +1,80 @@
+From c618b36cccac0ff9b25d2d43f7031121714b8137 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 11:09:09 +0200
+Subject: drm/amd/display: Add dp_skip_rbr flag for NUTMEG
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Timur Kristóf <timur.kristof@gmail.com>
+
+[ Upstream commit e39b7cf5c62e027af166772e46382356ecb45c36 ]
+
+No functional changes. Just clean up a conceptual mismatch.
+
+Based on feedback on the NUTMEG code in DC, the
+preferred_link_setting is meant to force the DP link to a
+specific setting, meaning both the link rate and lane count
+should be locked to an exact value. What NUTMEG needs is
+a lower bound on the link rate, which is not the same concept.
+
+Implement this as a HW workaround flag instead.
+
+Suggested-by: Wenjing Liu <wenjing.liu@amd.com>
+Signed-off-by: Timur Kristóf <timur.kristof@gmail.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit 871ceb853841bcaa4e6cec3723b16c4887a760be)
+Cc: stable@vger.kernel.org
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/amd/display/dc/dc.h                         | 2 ++
+ drivers/gpu/drm/amd/display/dc/link/link_detection.c        | 2 +-
+ .../drm/amd/display/dc/link/protocols/link_dp_capability.c  | 6 +++---
+ 3 files changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
+index 55152f12af48be..61a28e287c1b98 100644
+--- a/drivers/gpu/drm/amd/display/dc/dc.h
++++ b/drivers/gpu/drm/amd/display/dc/dc.h
+@@ -1727,6 +1727,8 @@ struct dc_scratch_space {
+               bool dp_skip_DID2;
+               bool dp_skip_reset_segment;
+               bool dp_skip_fs_144hz;
++              /* Some DP bridges don't work with RBR and must use HBR. */
++              bool dp_skip_rbr;
+               bool dp_mot_reset_segment;
+               /* Some USB4 docks do not handle turning off MST DSC once it has been enabled. */
+               bool dpia_mst_dsc_always_on;
+diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
+index 794dd6a9591830..faf33e9b1c6f37 100644
+--- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c
++++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
+@@ -621,7 +621,7 @@ static bool detect_dp(struct dc_link *link,
+               link->dpcd_caps.sink_count.bits.SINK_COUNT = 1;
+               /* NUTMEG requires that we use HBR, doesn't work with RBR. */
+               if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_00001A)
+-                      link->preferred_link_setting.link_rate = LINK_RATE_HIGH;
++                      link->wa_flags.dp_skip_rbr = true;
+       }
+       return true;
+diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
+index c9c76b8c3e3ba2..fe8420b803eeda 100644
+--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
++++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
+@@ -750,9 +750,9 @@ static bool decide_dp_link_settings(struct dc_link *link, struct dc_link_setting
+       if (req_bw > dp_link_bandwidth_kbps(link, &link->verified_link_cap))
+               return false;
+-      if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN) {
+-              initial_link_setting.link_rate = link->preferred_link_setting.link_rate;
+-              current_link_setting.link_rate = link->preferred_link_setting.link_rate;
++      if (link->wa_flags.dp_skip_rbr) {
++              initial_link_setting.link_rate = LINK_RATE_HIGH;
++              current_link_setting.link_rate = LINK_RATE_HIGH;
+       }
+       /* search for the minimum link setting that:
+-- 
+2.53.0
+
diff --git a/queue-7.1/drm-amd-display-fix-ism-dc_lock-deadlock-during-susp.patch b/queue-7.1/drm-amd-display-fix-ism-dc_lock-deadlock-during-susp.patch
new file mode 100644 (file)
index 0000000..b237c72
--- /dev/null
@@ -0,0 +1,193 @@
+From 62fd176e318fd76ca00f1ad8649829dd272b62b1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 Apr 2026 10:08:16 +0800
+Subject: drm/amd/display: Fix ISM dc_lock deadlock during suspend
+
+From: Ray Wu <ray.wu@amd.com>
+
+[ Upstream commit 3714fe242592e3699ac5e2c19d68b275a210be7d ]
+
+[Why]
+System hang observed during suspend/resume while video is playing.
+amdgpu_dm_ism_disable() is called under dc_lock and waits for ISM
+delayed work via disable_delayed_work_sync(). The work handlers
+themselves take dc_lock, producing an ABBA deadlock when a worker is
+in flight at suspend time.
+
+[How]
+Split the disable path into two phases with opposite locking
+contracts:
+  1. amdgpu_dm_ism_disable() -- quiesces workers, must NOT hold
+     dc_lock.
+  2. amdgpu_dm_ism_force_full_power() (new) -- drives the ISM FSM
+     back to FULL_POWER_RUNNING, must hold dc_lock.
+
+Reviewed-by: Sun peng (Leo) Li <sunpeng.li@amd.com>
+Signed-off-by: Ray Wu <ray.wu@amd.com>
+Signed-off-by: Ivan Lipski <ivan.lipski@amd.com>
+Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 25 +++++++--
+ .../drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c | 56 ++++++++++++++++---
+ .../drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h |  1 +
+ 3 files changed, 70 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+index 1ecd7bef9ed850..45951d47ed58c8 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+@@ -2260,9 +2260,16 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev)
+               adev->dm.idle_workqueue = NULL;
+       }
+-      /* Disable ISM before dc_destroy() invalidates dm->dc */
++      /*
++       * Disable ISM before dc_destroy() invalidates dm->dc.
++       *
++       * Quiesce workers first without dc_lock (they take dc_lock
++       * themselves, so syncing under it would deadlock), then drive the
++       * FSM back to FULL_POWER_RUNNING under dc_lock.
++       */
++      amdgpu_dm_ism_disable(&adev->dm);
+       scoped_guard(mutex, &adev->dm.dc_lock)
+-              amdgpu_dm_ism_disable(&adev->dm);
++              amdgpu_dm_ism_force_full_power(&adev->dm);
+       amdgpu_dm_destroy_drm_device(&adev->dm);
+@@ -3290,9 +3297,14 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
+       if (amdgpu_in_reset(adev)) {
+               enum dc_status res;
++              /* Quiesce ISM workers before taking dc_lock (workers take
++               * dc_lock themselves; syncing under it would deadlock).
++               */
++              amdgpu_dm_ism_disable(dm);
++
+               mutex_lock(&dm->dc_lock);
+-              amdgpu_dm_ism_disable(dm);
++              amdgpu_dm_ism_force_full_power(dm);
+               dc_allow_idle_optimizations(adev->dm.dc, false);
+               dm->cached_dc_state = dc_state_create_copy(dm->dc->current_state);
+@@ -3326,8 +3338,13 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
+       amdgpu_dm_irq_suspend(adev);
++      /*
++       * Quiesce ISM workers before taking dc_lock (workers take dc_lock
++       * themselves; syncing under it would deadlock).
++       */
++      amdgpu_dm_ism_disable(dm);
+       scoped_guard(mutex, &dm->dc_lock)
+-              amdgpu_dm_ism_disable(dm);
++              amdgpu_dm_ism_force_full_power(dm);
+       hpd_rx_irq_work_suspend(dm);
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c
+index a64e95860e99b3..b32c8d3ac15234 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c
+@@ -524,13 +524,20 @@ static void dm_ism_sso_delayed_work_func(struct work_struct *work)
+ }
+ /**
+- * amdgpu_dm_ism_disable - Disable the ISM
++ * amdgpu_dm_ism_disable - Quiesce ISM workers
+  *
+  * @dm: The amdgpu display manager
+  *
+- * Disable the idle state manager by disabling any ISM work, canceling pending
+- * work, and waiting for in-progress work to finish. After disabling, the system
+- * is left in DM_ISM_STATE_FULL_POWER_RUNNING state.
++ * Cancels and disables any pending or in-flight ISM delayed work and waits
++ * for in-progress work to finish. After this returns, no ISM worker can run
++ * and subsequent mod_delayed_work() calls become no-ops via
++ * clear_pending_if_disabled().
++ *
++ * Must NOT be called with dc_lock held: the workers themselves take dc_lock,
++ * so a synchronous wait under dc_lock would deadlock.
++ *
++ * The caller is responsible for driving the FSM back to FULL_POWER_RUNNING
++ * (under dc_lock) by calling amdgpu_dm_ism_force_full_power().
+  */
+ void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm)
+ {
+@@ -538,21 +545,54 @@ void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm)
+       struct amdgpu_crtc *acrtc;
+       struct amdgpu_dm_ism *ism;
+-      ASSERT(mutex_is_locked(&dm->dc_lock));
++      /*
++       * Caller must NOT hold dc_lock: the ISM delayed work handlers
++       * acquire dc_lock themselves, so waiting for them via
++       * disable_delayed_work_sync() while holding dc_lock would
++       * self-deadlock against an in-flight worker.
++       */
++      lockdep_assert_not_held(&dm->dc_lock);
+       drm_for_each_crtc(crtc, dm->ddev) {
+               acrtc = to_amdgpu_crtc(crtc);
+               ism = &acrtc->ism;
+-              /* Cancel and disable any pending work */
+               disable_delayed_work_sync(&ism->delayed_work);
+               disable_delayed_work_sync(&ism->sso_delayed_work);
++      }
++}
++
++/**
++ * amdgpu_dm_ism_force_full_power - Force every CRTC's ISM FSM to FULL_POWER
++ *
++ * @dm: The amdgpu display manager
++ *
++ * Sends DM_ISM_EVENT_EXIT_IDLE_REQUESTED to every CRTC's ISM, leaving each
++ * FSM in FULL_POWER_RUNNING. Intended to be paired with
++ * amdgpu_dm_ism_disable(): callers should first quiesce workers (without
++ * dc_lock), then take dc_lock and call this helper.
++ *
++ * Must be called with dc_lock held.
++ */
++void amdgpu_dm_ism_force_full_power(struct amdgpu_display_manager *dm)
++{
++      struct drm_crtc *crtc;
++      struct amdgpu_crtc *acrtc;
++
++      /*
++       * Caller must hold dc_lock: commit_event() drives the FSM and
++       * may touch dc state via dc_allow_idle_optimizations() etc.
++       */
++      lockdep_assert_held(&dm->dc_lock);
++
++      drm_for_each_crtc(crtc, dm->ddev) {
++              acrtc = to_amdgpu_crtc(crtc);
+               /*
+                * When disabled, leave in FULL_POWER_RUNNING state.
+-               * EXIT_IDLE will not queue any work
++               * EXIT_IDLE will not queue any work.
+                */
+-              amdgpu_dm_ism_commit_event(ism,
++              amdgpu_dm_ism_commit_event(&acrtc->ism,
+                                          DM_ISM_EVENT_EXIT_IDLE_REQUESTED);
+       }
+ }
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h
+index fde0ddc8d4e4bc..964408cd9a839c 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h
+@@ -146,6 +146,7 @@ void amdgpu_dm_ism_fini(struct amdgpu_dm_ism *ism);
+ void amdgpu_dm_ism_commit_event(struct amdgpu_dm_ism *ism,
+                               enum amdgpu_dm_ism_event event);
+ void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm);
++void amdgpu_dm_ism_force_full_power(struct amdgpu_display_manager *dm);
+ void amdgpu_dm_ism_enable(struct amdgpu_display_manager *dm);
+ #endif
+-- 
+2.53.0
+
diff --git a/queue-7.1/drm-amd-display-fix-preferred-link-rate-for-nutmeg.patch b/queue-7.1/drm-amd-display-fix-preferred-link-rate-for-nutmeg.patch
new file mode 100644 (file)
index 0000000..c08293f
--- /dev/null
@@ -0,0 +1,52 @@
+From a06719574023f83ea17471ce958fd9fd88f598d8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 11:09:08 +0200
+Subject: drm/amd/display: Fix preferred link rate for NUTMEG
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Timur Kristóf <timur.kristof@gmail.com>
+
+[ Upstream commit 9fb646bc4d87f62bcbf0a7ea326430eb802c475c ]
+
+When there is a preferred link rate setting, it needs to be
+applied to both the current and initial link rate.
+This was regressed by a "coding style" fix, which caused
+the current link rate to not respect the preferred value.
+
+This commit restores the functionality of NUTMEG,
+the DP bridge encoder found on old APUs such as Kaveri.
+
+Fixes: a62346043a89 ("drm/amd/display: Fix coding style issue")
+Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5465
+Cc: Chuanyu Tseng <Chuanyu.Tseng@amd.com>
+Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
+Signed-off-by: Timur Kristóf <timur.kristof@gmail.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit e78b0a367f8690b682029d90e75308dc84ed51de)
+Cc: stable@vger.kernel.org
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../drm/amd/display/dc/link/protocols/link_dp_capability.c    | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
+index 782a45caa13d4a..c9c76b8c3e3ba2 100644
+--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
++++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
+@@ -750,8 +750,10 @@ static bool decide_dp_link_settings(struct dc_link *link, struct dc_link_setting
+       if (req_bw > dp_link_bandwidth_kbps(link, &link->verified_link_cap))
+               return false;
+-      if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN)
++      if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN) {
+               initial_link_setting.link_rate = link->preferred_link_setting.link_rate;
++              current_link_setting.link_rate = link->preferred_link_setting.link_rate;
++      }
+       /* search for the minimum link setting that:
+        * 1. is supported according to the link training result
+-- 
+2.53.0
+
diff --git a/queue-7.1/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch b/queue-7.1/revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch
new file mode 100644 (file)
index 0000000..2e0d1dc
--- /dev/null
@@ -0,0 +1,28 @@
+From bc474ad336d937459a848d014056c508fe1987e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:07:29 -0400
+Subject: Revert "drm/amd/display: Add missing kdoc for ALLM parameters"
+
+This reverts commit 994a42b890cef46cd576a3bf700f1dbd6bb21346.
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+index 614db22d62f38b..00473c6284d5cc 100644
+--- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
++++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+@@ -502,8 +502,6 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream,
+  *
+  *  @stream:      contains data we may need to construct VSIF (i.e. timing_3d_format, etc.)
+  *  @info_packet: output structure where to store VSIF
+- *  @ALLMEnabled: indicates whether ALLM HF-VSIF should be generated
+- *  @ALLMValue:   ALLM bit value to advertise in HF-VSIF
+  */
+ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
+               struct dc_info_packet *info_packet)
+-- 
+2.53.0
+
diff --git a/queue-7.1/risc-v-kvm-serialize-virtual-interrupt-pending-state.patch b/queue-7.1/risc-v-kvm-serialize-virtual-interrupt-pending-state.patch
new file mode 100644 (file)
index 0000000..e10e17f
--- /dev/null
@@ -0,0 +1,397 @@
+From 4d834797fcf7abeba7225c2fed88a07932b35a56 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 10:03:59 +0800
+Subject: RISC-V: KVM: Serialize virtual interrupt pending state updates
+
+From: Xie Bo <xb@ultrarisc.com>
+
+commit d024a0a7879e6f37c0152aacf6d8e37b214a1738 upstream.
+
+KVM RISC-V tracks guest local interrupt state with two bitmaps:
+
+  - irqs_pending: interrupts that should be visible to the guest
+  - irqs_pending_mask: interrupts whose pending state changed
+
+The current code updates those bitmaps with independent atomic bitops
+and assumes a multiple-producer, single-consumer protocol. That model
+does not actually hold.
+
+kvm_riscv_vcpu_sync_interrupts() is not a pure consumer. When the guest
+changes guest-visible HVIP state, sync_interrupts() writes both
+irqs_pending and irqs_pending_mask to reflect the new guest state back
+into KVM state. As a result, irqs_pending and irqs_pending_mask form a
+single logical state transition, but they are not updated atomically as
+a pair.
+
+This allows a race where a newly injected interrupt is lost. For
+example:
+
+  CPU0                              CPU1
+  ----                              ----
+  kvm_riscv_vcpu_set_interrupt(VS_SOFT)
+    set_bit(VS_SOFT, irqs_pending)
+                                    kvm_riscv_vcpu_sync_interrupts()
+                                      sees guest-cleared HVIP.VSSIP
+                                      sets irqs_pending_mask
+                                      clear_bit(IRQ_VS_SOFT, irqs_pending)
+    set_bit(VS_SOFT, irqs_pending_mask)
+    kvm_vcpu_kick()
+
+After that interleaving, a later flush can update HVIP without VSSIP
+even though a new virtual interrupt was injected. In practice, the
+guest can remain blocked in WFI with work pending.
+
+The same pending/mask protocol is shared by VS soft interrupts, PMU
+overflow delivery, and AIA high interrupt synchronization, so the race
+is not limited to one interrupt source.
+
+Fix this by serializing all updates to irqs_pending and irqs_pending_mask
+with a per-vCPU raw spinlock. This keeps the pending bit and the dirty
+mask as one state transition across:
+
+  - set/unset interrupt
+  - guest HVIP sync
+  - interrupt flush to guest CSR state
+  - vCPU reset
+  - AIA CSR writes that clear dirty state
+
+Use non-atomic bitmap operations while holding the lock. Hold the lock
+across the AIA sync, flush, and pending checks as well, so both bitmap
+words share the same serialization domain.
+
+This intentionally replaces the existing lockless protocol instead of
+trying to repair it with additional barriers. The problem is not memory
+ordering on a single field; it is that two separate bitmaps encode one
+shared state machine while both producers and sync paths can modify
+them. A per-vCPU raw spinlock keeps the fix small, local, and suitable
+for backporting.
+
+Fixes: cce69aff689e ("RISC-V: KVM: Implement VCPU interrupts and requests handling")
+Cc: stable@vger.kernel.org
+[ Adapted for 7.1.y: place the new 'unsigned long flags;' declaration in
+  kvm_riscv_vcpu_general_set_csr() explicitly. A direct apply anchors it in
+  kvm_riscv_vcpu_general_get_csr(), whose prologue is byte-identical, leaving
+  'flags' undeclared at its use site. ]
+
+Signed-off-by: Xie Bo <xb@ultrarisc.com>
+Reviewed-by: Anup Patel <anup@brainfault.org>
+Signed-off-by: Anup Patel <anup@brainfault.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/riscv/include/asm/kvm_host.h | 10 ++---
+ arch/riscv/kvm/aia.c              | 35 ++++++++++++----
+ arch/riscv/kvm/vcpu.c             | 68 ++++++++++++++++++++++---------
+ arch/riscv/kvm/vcpu_onereg.c      |  8 +++-
+ 4 files changed, 87 insertions(+), 34 deletions(-)
+
+diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h
+index 75b0a951c1bc6f..97e42645cbabee 100644
+--- a/arch/riscv/include/asm/kvm_host.h
++++ b/arch/riscv/include/asm/kvm_host.h
+@@ -207,13 +207,13 @@ struct kvm_vcpu_arch {
+       /*
+        * VCPU interrupts
+        *
+-       * We have a lockless approach for tracking pending VCPU interrupts
+-       * implemented using atomic bitops. The irqs_pending bitmap represent
+-       * pending interrupts whereas irqs_pending_mask represent bits changed
+-       * in irqs_pending. Our approach is modeled around multiple producer
+-       * and single consumer problem where the consumer is the VCPU itself.
++       * The irqs_pending bitmap represents pending interrupts whereas
++       * irqs_pending_mask represents bits changed in irqs_pending. Updates
++       * to these bitmaps are serialized so vcpu interrupt sync/flush cannot
++       * drop a newly injected interrupt while syncing guest-visible HVIP.
+        */
+ #define KVM_RISCV_VCPU_NR_IRQS        64
++      raw_spinlock_t irqs_pending_lock;
+       DECLARE_BITMAP(irqs_pending, KVM_RISCV_VCPU_NR_IRQS);
+       DECLARE_BITMAP(irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS);
+diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c
+index 5ec503288555d1..5e92fe14f79ae7 100644
+--- a/arch/riscv/kvm/aia.c
++++ b/arch/riscv/kvm/aia.c
+@@ -51,12 +51,15 @@ void kvm_riscv_vcpu_aia_flush_interrupts(struct kvm_vcpu *vcpu)
+       struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
+       unsigned long mask, val;
++      lockdep_assert_held(&vcpu->arch.irqs_pending_lock);
++
+       if (!kvm_riscv_aia_available())
+               return;
+-      if (READ_ONCE(vcpu->arch.irqs_pending_mask[1])) {
+-              mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[1], 0);
+-              val = READ_ONCE(vcpu->arch.irqs_pending[1]) & mask;
++      mask = vcpu->arch.irqs_pending_mask[1];
++      if (mask) {
++              vcpu->arch.irqs_pending_mask[1] = 0;
++              val = vcpu->arch.irqs_pending[1] & mask;
+               csr->hviph &= ~mask;
+               csr->hviph |= val;
+@@ -67,6 +70,8 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu)
+ {
+       struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
++      lockdep_assert_held(&vcpu->arch.irqs_pending_lock);
++
+       if (kvm_riscv_aia_available())
+               csr->vsieh = ncsr_read(CSR_VSIEH);
+ }
+@@ -75,13 +80,22 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu)
+ bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu, u64 mask)
+ {
+       unsigned long seip;
++#ifdef CONFIG_32BIT
++      unsigned long flags;
++      bool pending;
++#endif
+       if (!kvm_riscv_aia_available())
+               return false;
+ #ifdef CONFIG_32BIT
+-      if (READ_ONCE(vcpu->arch.irqs_pending[1]) &
+-          (vcpu->arch.aia_context.guest_csr.vsieh & upper_32_bits(mask)))
++      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++      pending = vcpu->arch.irqs_pending[1] &
++                (vcpu->arch.aia_context.guest_csr.vsieh &
++                 upper_32_bits(mask));
++      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
++
++      if (pending)
+               return true;
+ #endif
+@@ -205,6 +219,9 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu,
+ {
+       struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
+       unsigned long regs_max = sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long);
++#ifdef CONFIG_32BIT
++      unsigned long flags;
++#endif
+       if (!riscv_isa_extension_available(vcpu->arch.isa, SSAIA))
+               return -ENOENT;
+@@ -217,8 +234,12 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu,
+               ((unsigned long *)csr)[reg_num] = val;
+ #ifdef CONFIG_32BIT
+-              if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph))
+-                      WRITE_ONCE(vcpu->arch.irqs_pending_mask[1], 0);
++              if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) {
++                      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++                      vcpu->arch.irqs_pending_mask[1] = 0;
++                      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock,
++                                                 flags);
++              }
+ #endif
+       }
+diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c
+index a73690eda84b5a..61b3029080b1f8 100644
+--- a/arch/riscv/kvm/vcpu.c
++++ b/arch/riscv/kvm/vcpu.c
+@@ -80,6 +80,7 @@ static void kvm_riscv_vcpu_context_reset(struct kvm_vcpu *vcpu,
+ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset)
+ {
++      unsigned long flags;
+       bool loaded;
+       /**
+@@ -104,8 +105,10 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset)
+       kvm_riscv_vcpu_aia_reset(vcpu);
++      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+       bitmap_zero(vcpu->arch.irqs_pending, KVM_RISCV_VCPU_NR_IRQS);
+       bitmap_zero(vcpu->arch.irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS);
++      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+       kvm_riscv_vcpu_pmu_reset(vcpu);
+@@ -151,6 +154,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
+       /* Setup VCPU hfence queue */
+       spin_lock_init(&vcpu->arch.hfence_lock);
++      raw_spin_lock_init(&vcpu->arch.irqs_pending_lock);
+       spin_lock_init(&vcpu->arch.reset_state.lock);
+@@ -352,10 +356,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu)
+ {
+       struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
+       unsigned long mask, val;
++      unsigned long flags;
+-      if (READ_ONCE(vcpu->arch.irqs_pending_mask[0])) {
+-              mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[0], 0);
+-              val = READ_ONCE(vcpu->arch.irqs_pending[0]) & mask;
++      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++
++      mask = vcpu->arch.irqs_pending_mask[0];
++      if (mask) {
++              vcpu->arch.irqs_pending_mask[0] = 0;
++              val = vcpu->arch.irqs_pending[0] & mask;
+               csr->hvip &= ~mask;
+               csr->hvip |= val;
+@@ -363,11 +371,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu)
+       /* Flush AIA high interrupts */
+       kvm_riscv_vcpu_aia_flush_interrupts(vcpu);
++
++      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+ }
+ void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu)
+ {
+       unsigned long hvip;
++      unsigned long flags;
+       struct kvm_vcpu_arch *v = &vcpu->arch;
+       struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
+@@ -376,34 +387,41 @@ void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu)
+       /* Sync-up HVIP.VSSIP bit changes does by Guest */
+       hvip = ncsr_read(CSR_HVIP);
++
++      raw_spin_lock_irqsave(&v->irqs_pending_lock, flags);
++
+       if ((csr->hvip ^ hvip) & (1UL << IRQ_VS_SOFT)) {
+               if (hvip & (1UL << IRQ_VS_SOFT)) {
+-                      if (!test_and_set_bit(IRQ_VS_SOFT,
+-                                            v->irqs_pending_mask))
+-                              set_bit(IRQ_VS_SOFT, v->irqs_pending);
++                      if (!__test_and_set_bit(IRQ_VS_SOFT,
++                                              v->irqs_pending_mask))
++                              __set_bit(IRQ_VS_SOFT, v->irqs_pending);
+               } else {
+-                      if (!test_and_set_bit(IRQ_VS_SOFT,
+-                                            v->irqs_pending_mask))
+-                              clear_bit(IRQ_VS_SOFT, v->irqs_pending);
++                      if (!__test_and_set_bit(IRQ_VS_SOFT,
++                                              v->irqs_pending_mask))
++                              __clear_bit(IRQ_VS_SOFT, v->irqs_pending);
+               }
+       }
+       /* Sync up the HVIP.LCOFIP bit changes (only clear) by the guest */
+       if ((csr->hvip ^ hvip) & (1UL << IRQ_PMU_OVF)) {
+               if (!(hvip & (1UL << IRQ_PMU_OVF)) &&
+-                  !test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask))
+-                      clear_bit(IRQ_PMU_OVF, v->irqs_pending);
++                  !__test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask))
++                      __clear_bit(IRQ_PMU_OVF, v->irqs_pending);
+       }
+       /* Sync-up AIA high interrupts */
+       kvm_riscv_vcpu_aia_sync_interrupts(vcpu);
++      raw_spin_unlock_irqrestore(&v->irqs_pending_lock, flags);
++
+       /* Sync-up timer CSRs */
+       kvm_riscv_vcpu_timer_sync(vcpu);
+ }
+ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
+ {
++      unsigned long flags;
++
+       /*
+        * We only allow VS-mode software, timer, and external
+        * interrupts when irq is one of the local interrupts
+@@ -416,9 +434,10 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
+           irq != IRQ_PMU_OVF)
+               return -EINVAL;
+-      set_bit(irq, vcpu->arch.irqs_pending);
+-      smp_mb__before_atomic();
+-      set_bit(irq, vcpu->arch.irqs_pending_mask);
++      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++      __set_bit(irq, vcpu->arch.irqs_pending);
++      __set_bit(irq, vcpu->arch.irqs_pending_mask);
++      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+       kvm_vcpu_kick(vcpu);
+@@ -427,6 +446,8 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
+ int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
+ {
++      unsigned long flags;
++
+       /*
+        * We only allow VS-mode software, timer, counter overflow and external
+        * interrupts when irq is one of the local interrupts
+@@ -439,26 +460,33 @@ int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
+           irq != IRQ_PMU_OVF)
+               return -EINVAL;
+-      clear_bit(irq, vcpu->arch.irqs_pending);
+-      smp_mb__before_atomic();
+-      set_bit(irq, vcpu->arch.irqs_pending_mask);
++      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++      __clear_bit(irq, vcpu->arch.irqs_pending);
++      __set_bit(irq, vcpu->arch.irqs_pending_mask);
++      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+       return 0;
+ }
+ bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, u64 mask)
+ {
++      unsigned long flags;
+       unsigned long ie;
++      bool ret;
++      raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+       ie = ((vcpu->arch.guest_csr.vsie & VSIP_VALID_MASK)
+               << VSIP_TO_HVIP_SHIFT) & (unsigned long)mask;
+       ie |= vcpu->arch.guest_csr.vsie & ~IRQ_LOCAL_MASK &
+               (unsigned long)mask;
+-      if (READ_ONCE(vcpu->arch.irqs_pending[0]) & ie)
+-              return true;
++      ret = vcpu->arch.irqs_pending[0] & ie;
++      raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+       /* Check AIA high interrupts */
+-      return kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask);
++      if (!ret)
++              ret = kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask);
++
++      return ret;
+ }
+ void __kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu)
+diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c
+index bb920e8923c930..2031beb9bba6e7 100644
+--- a/arch/riscv/kvm/vcpu_onereg.c
++++ b/arch/riscv/kvm/vcpu_onereg.c
+@@ -298,6 +298,7 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu,
+ {
+       struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
+       unsigned long regs_max = sizeof(struct kvm_riscv_csr) / sizeof(unsigned long);
++      unsigned long flags;
+       if (reg_num >= regs_max)
+               return -ENOENT;
+@@ -311,8 +312,11 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu,
+       ((unsigned long *)csr)[reg_num] = reg_val;
+-      if (reg_num == KVM_REG_RISCV_CSR_REG(sip))
+-              WRITE_ONCE(vcpu->arch.irqs_pending_mask[0], 0);
++      if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) {
++              raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++              vcpu->arch.irqs_pending_mask[0] = 0;
++              raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
++      }
+       return 0;
+ }
+-- 
+2.53.0
+
index a1786d4dc94a9de2a562a860f0a80e8606cdde85..ab5db8cdbe119121ad2979c3db49161fc7a48ef1 100644 (file)
@@ -156,3 +156,9 @@ usb-serial-keyspan_pda-fix-data-loss-on-receive-throttling.patch
 usb-serial-option-add-tdtech-mt5710-cn.patch
 usb-typec-ucsi-detect-and-skip-duplicate-altmodes-from-buggy-firmware.patch
 usb-typec-ucsi-add-duplicate-detection-to-nvidia-registration-path.patch
+drm-amd-display-fix-ism-dc_lock-deadlock-during-susp.patch
+drm-amd-display-fix-preferred-link-rate-for-nutmeg.patch
+drm-amd-display-add-dp_skip_rbr-flag-for-nutmeg.patch
+wifi-mwifiex-fix-freeze-for-60-seconds-caused-by-req.patch
+risc-v-kvm-serialize-virtual-interrupt-pending-state.patch
+revert-drm-amd-display-add-missing-kdoc-for-allm-par.patch
diff --git a/queue-7.1/wifi-mwifiex-fix-freeze-for-60-seconds-caused-by-req.patch b/queue-7.1/wifi-mwifiex-fix-freeze-for-60-seconds-caused-by-req.patch
new file mode 100644 (file)
index 0000000..be2403c
--- /dev/null
@@ -0,0 +1,78 @@
+From 5814723c48d41b2675a769850ea7edbecfd917db Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 01:17:09 +0300
+Subject: wifi: mwifiex: fix freeze for 60 seconds caused by request_firmware
+
+From: Georgi Valkov <gvalkov@gmail.com>
+
+[ Upstream commit 121a96c5a0db8d18e2ba2cb89660cca8a40508fe ]
+
+Fix regression in rgpower table loading, caused by using
+request_firmware(): when the requested firmware does not exist, e.g.
+nxp/rgpower_WW.bin does not exist on OpenWRT builds for WRT3200ACM,
+request_firmware() falls back to firmware_fallback_sysfs(), which expects
+the firmware to be provided by user space using SYSFS. No such utility is
+provided in this configuration, so the entire system locks up for 60
+seconds, until the request times out. During this time, no other log
+messages are observed, and the device does not respond to commands over
+UART.
+
+The request_firmware() call is performed in the following context:
+current->comm kworker/1:2  in_task 1  irqs_disabled 0  in_atomic 0
+
+Fixed by using request_firmware_direct(). This prevents fallback to SYSFS,
+and avoids delay. The rgpower table is optional. The driver falls back
+to the device tree power table if the firmware is not present.
+
+The error code is printed for debugging and returned to the caller,
+which only cares for success or failure, so there are no side effects.
+
+Fixes: 7b6f16a25806 ("wifi: mwifiex: add rgpower table loading support")
+Signed-off-by: Georgi Valkov <gvalkov@gmail.com>
+Reviewed-by: Francesco Dolcini <francesco.dolcini@toradex.com>
+Link: https://patch.msgid.link/20260712221709.7099-1-gvalkov@gmail.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
+index a6550548d3b435..9460d5352b234e 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
++++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
+@@ -196,6 +196,7 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv)
+       struct mwifiex_adapter *adapter = priv->adapter;
+       char rgpower_table_name[30];
+       char country_code[3];
++      int ret;
+       strscpy(country_code, domain_info->country_code, sizeof(country_code));
+@@ -214,16 +215,17 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv)
+               adapter->rgpower_data = NULL;
+       }
+-      if ((request_firmware(&adapter->rgpower_data, rgpower_table_name,
+-                            adapter->dev))) {
++      ret = request_firmware_direct(&adapter->rgpower_data, rgpower_table_name,
++                                    adapter->dev);
++
++      if (ret) {
+               mwifiex_dbg(
+                       adapter, INFO,
+-                      "info: %s: failed to request regulatory power table\n",
+-                      __func__);
+-              return -EIO;
++                      "info: %s: failed to request regulatory power table: %d\n",
++                      __func__, ret);
+       }
+-      return 0;
++      return ret;
+ }
+ static int mwifiex_dnld_rgpower_table(struct mwifiex_private *priv)
+-- 
+2.53.0
+