]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
7.1-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 2 Jul 2026 13:03:55 +0000 (15:03 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 2 Jul 2026 13:03:55 +0000 (15:03 +0200)
added patches:
block-invalidate-cached-plug-timestamp-after-task-switch.patch
err.h-use-__always_inline-on-all-error-pointer-helpers.patch
fbdev-fix-use-after-free-in-store_modes.patch
fscrypt-fix-key-setup-in-edge-case-with-multiple-data-unit-sizes.patch
gcov-use-atomic-counter-updates-to-fix-concurrent-access-crashes.patch
keys-fix-overflow-in-keyctl_pkey_params_get_2.patch
keys-pin-request_key_auth-payload-in-instantiate-paths.patch
kvm-arm64-omit-tag-sync-on-stage-2-mappings-of-the-zero-page.patch
ntb-epf-avoid-pci_iounmap-with-offset-when-peer_spad-and-config-share-bar.patch
userfaultfd-build-__vma_uffd_flags-from-config-gated-masks.patch
userfaultfd-ensure-mremap_userfaultfd_fail-releases-mmap_changing.patch
wifi-ath11k-fix-warning-when-unbinding.patch
wifi-iwlwifi-mld-fix-race-condition-in-ptp-removal.patch
wifi-iwlwifi-mld-validate-sta_mask-before-ffs-in-ba-session-handlers.patch
wifi-iwlwifi-mvm-fix-race-condition-in-ptp-removal.patch
wifi-mt76-mt76x2u-add-support-for-elecom-wdc-867su3s.patch
wifi-mt76-mt7925-don-t-disable-ap-bss-when-removing-tdls-peer.patch
wifi-rtl8xxxu-detect-the-maximum-supported-channel-width.patch
wifi-rtlwifi-rtl8821ae-fix-c2h-bit-location-in-rx-descriptor.patch
wifi-rtw88-increase-tx-report-timeout-to-fix-race-condition.patch
wifi-rtw88-usb-fix-memory-leaks-on-usb-write-failures.patch

22 files changed:
queue-7.1/block-invalidate-cached-plug-timestamp-after-task-switch.patch [new file with mode: 0644]
queue-7.1/err.h-use-__always_inline-on-all-error-pointer-helpers.patch [new file with mode: 0644]
queue-7.1/fbdev-fix-use-after-free-in-store_modes.patch [new file with mode: 0644]
queue-7.1/fscrypt-fix-key-setup-in-edge-case-with-multiple-data-unit-sizes.patch [new file with mode: 0644]
queue-7.1/gcov-use-atomic-counter-updates-to-fix-concurrent-access-crashes.patch [new file with mode: 0644]
queue-7.1/keys-fix-overflow-in-keyctl_pkey_params_get_2.patch [new file with mode: 0644]
queue-7.1/keys-pin-request_key_auth-payload-in-instantiate-paths.patch [new file with mode: 0644]
queue-7.1/kvm-arm64-omit-tag-sync-on-stage-2-mappings-of-the-zero-page.patch [new file with mode: 0644]
queue-7.1/ntb-epf-avoid-pci_iounmap-with-offset-when-peer_spad-and-config-share-bar.patch [new file with mode: 0644]
queue-7.1/series
queue-7.1/userfaultfd-build-__vma_uffd_flags-from-config-gated-masks.patch [new file with mode: 0644]
queue-7.1/userfaultfd-ensure-mremap_userfaultfd_fail-releases-mmap_changing.patch [new file with mode: 0644]
queue-7.1/wifi-ath11k-fix-warning-when-unbinding.patch [new file with mode: 0644]
queue-7.1/wifi-iwlwifi-mld-fix-race-condition-in-ptp-removal.patch [new file with mode: 0644]
queue-7.1/wifi-iwlwifi-mld-validate-sta_mask-before-ffs-in-ba-session-handlers.patch [new file with mode: 0644]
queue-7.1/wifi-iwlwifi-mvm-fix-race-condition-in-ptp-removal.patch [new file with mode: 0644]
queue-7.1/wifi-mt76-mt76x2u-add-support-for-elecom-wdc-867su3s.patch [new file with mode: 0644]
queue-7.1/wifi-mt76-mt7925-don-t-disable-ap-bss-when-removing-tdls-peer.patch [new file with mode: 0644]
queue-7.1/wifi-rtl8xxxu-detect-the-maximum-supported-channel-width.patch [new file with mode: 0644]
queue-7.1/wifi-rtlwifi-rtl8821ae-fix-c2h-bit-location-in-rx-descriptor.patch [new file with mode: 0644]
queue-7.1/wifi-rtw88-increase-tx-report-timeout-to-fix-race-condition.patch [new file with mode: 0644]
queue-7.1/wifi-rtw88-usb-fix-memory-leaks-on-usb-write-failures.patch [new file with mode: 0644]

diff --git a/queue-7.1/block-invalidate-cached-plug-timestamp-after-task-switch.patch b/queue-7.1/block-invalidate-cached-plug-timestamp-after-task-switch.patch
new file mode 100644 (file)
index 0000000..40fa361
--- /dev/null
@@ -0,0 +1,109 @@
+From fad156c2af227f42ca796cbb20ddc354a6dd9932 Mon Sep 17 00:00:00 2001
+From: Usama Arif <usama.arif@linux.dev>
+Date: Tue, 16 Jun 2026 07:15:18 -0700
+Subject: block: invalidate cached plug timestamp after task switch
+
+From: Usama Arif <usama.arif@linux.dev>
+
+commit fad156c2af227f42ca796cbb20ddc354a6dd9932 upstream.
+
+blk_time_get_ns() caches ktime_get_ns() in current->plug->cur_ktime
+and marks the task with PF_BLOCK_TS. That cache is only valid while the
+task keeps running; if the task is switched out, wall-clock time
+advances and the cached value must not be reused when the task runs again.
+
+The existing invalidation covers explicit plug flushes through
+__blk_flush_plug(), and the schedule() / rtmutex paths through
+sched_update_worker(). It does not cover in-kernel preemption paths such
+as preempt_schedule(), preempt_schedule_notrace(), and
+preempt_schedule_irq(), which enter __schedule(SM_PREEMPT) directly and
+return without calling sched_update_worker().
+
+As a result, a task preempted while holding a plug with PF_BLOCK_TS set
+can reuse a stale plug->cur_ktime after it is scheduled back in. blk-iocost
+then consumes that stale timestamp through ioc_now(), producing stale vnow
+values for throttle decisions, and through ioc_rqos_done(), inflating
+on-queue time and feeding false missed-QoS samples into vrate
+adjustment.
+
+Move the schedule-side invalidation to finish_task_switch(), which runs
+for the scheduled-in task after every actual context switch regardless
+of which schedule entry point was used. Keep __blk_flush_plug() as the
+explicit flush/finish-plug invalidation path, and remove only the
+PF_BLOCK_TS handling from sched_update_worker().
+
+Fixes: 06b23f92af87 ("block: update cached timestamp post schedule/preemption")
+Cc: stable@vger.kernel.org
+Signed-off-by: Usama Arif <usama.arif@linux.dev>
+Link: https://patch.msgid.link/20260616141604.328820-3-usama.arif@linux.dev
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/blkdev.h |   18 +++++++-----------
+ kernel/sched/core.c    |   12 ++++++++----
+ 2 files changed, 15 insertions(+), 15 deletions(-)
+
+--- a/include/linux/blkdev.h
++++ b/include/linux/blkdev.h
+@@ -1214,16 +1214,12 @@ static inline void blk_flush_plug(struct
+               __blk_flush_plug(plug, async);
+ }
+-/*
+- * tsk == current here
+- */
+-static inline void blk_plug_invalidate_ts(struct task_struct *tsk)
+-{
+-      struct blk_plug *plug = tsk->plug;
+-
+-      if (plug)
+-              plug->cur_ktime = 0;
+-      current->flags &= ~PF_BLOCK_TS;
++static __always_inline void blk_plug_invalidate_ts(void)
++{
++      if (unlikely(current->flags & PF_BLOCK_TS)) {
++              current->plug->cur_ktime = 0;
++              current->flags &= ~PF_BLOCK_TS;
++      }
+ }
+ int blkdev_issue_flush(struct block_device *bdev);
+@@ -1249,7 +1245,7 @@ static inline void blk_flush_plug(struct
+ {
+ }
+-static inline void blk_plug_invalidate_ts(struct task_struct *tsk)
++static inline void blk_plug_invalidate_ts(void)
+ {
+ }
+--- a/kernel/sched/core.c
++++ b/kernel/sched/core.c
+@@ -5252,6 +5252,12 @@ static struct rq *finish_task_switch(str
+        */
+       kmap_local_sched_in();
++      /*
++       * Any cached block-layer timestamp (plug->cur_ktime) is stale now,
++       * invalidate it.
++       */
++      blk_plug_invalidate_ts();
++
+       fire_sched_in_preempt_notifiers(current);
+       /*
+        * When switching through a kernel thread, the loop in
+@@ -7251,12 +7257,10 @@ static inline void sched_submit_work(str
+ static void sched_update_worker(struct task_struct *tsk)
+ {
+-      if (tsk->flags & (PF_WQ_WORKER | PF_IO_WORKER | PF_BLOCK_TS)) {
+-              if (tsk->flags & PF_BLOCK_TS)
+-                      blk_plug_invalidate_ts(tsk);
++      if (tsk->flags & (PF_WQ_WORKER | PF_IO_WORKER)) {
+               if (tsk->flags & PF_WQ_WORKER)
+                       wq_worker_running(tsk);
+-              else if (tsk->flags & PF_IO_WORKER)
++              else
+                       io_wq_worker_running(tsk);
+       }
+ }
diff --git a/queue-7.1/err.h-use-__always_inline-on-all-error-pointer-helpers.patch b/queue-7.1/err.h-use-__always_inline-on-all-error-pointer-helpers.patch
new file mode 100644 (file)
index 0000000..6f0e3b0
--- /dev/null
@@ -0,0 +1,96 @@
+From 94bfc7f3b0c7c33331ba4ff6cc64ff309dfcbce8 Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 26 May 2026 12:18:41 +0200
+Subject: err.h: use __always_inline on all error pointer helpers
+
+From: Arnd Bergmann <arnd@arndb.de>
+
+commit 94bfc7f3b0c7c33331ba4ff6cc64ff309dfcbce8 upstream.
+
+While testing randconfig builds on s390, I came across a link failure with
+CONFIG_DMA_SHARED_BUFFER disabled:
+
+ERROR: modpost: "dma_buf_put" [drivers/iommu/iommufd/iommufd.ko] undefined!
+
+The problem here is that IS_ERR() is not inlined and dead code elimination
+fails as a consequence.
+
+The err.h helpers all turn into a trivial assignment of a bit mask and
+should never result in a function call, so force them to always be inline.
+This should generally result in better object code aside from avoiding
+the link failure above.
+
+Link: https://lore.kernel.org/20260526101851.2495110-1-arnd@kernel.org
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
+Reviewed-by: Nathan Chancellor <nathan@kernel.org>
+Tested-by: Tamir Duberstein <tamird@kernel.org>
+Cc: Alexander Gordeev <agordeev@linux.ibm.com>
+Cc: Andriy Shevchenko <andriy.shevchenko@linux.intel.com>
+Cc: Ansuel Smith <ansuelsmth@gmail.com>
+Cc: Bjorn Andersson <andersson@kernel.org>
+Cc: Heiko Carstens <hca@linux.ibm.com>
+Cc: Vasily Gorbik <gor@linux.ibm.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/err.h |   12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/include/linux/err.h
++++ b/include/linux/err.h
+@@ -36,7 +36,7 @@
+  *
+  * Return: A pointer with @error encoded within its value.
+  */
+-static inline void * __must_check ERR_PTR(long error)
++static __always_inline void * __must_check ERR_PTR(long error)
+ {
+       return (void *) error;
+ }
+@@ -60,7 +60,7 @@ static inline void * __must_check ERR_PT
+  * @ptr: An error pointer.
+  * Return: The error code within @ptr.
+  */
+-static inline long __must_check PTR_ERR(__force const void *ptr)
++static __always_inline long __must_check PTR_ERR(__force const void *ptr)
+ {
+       return (long) ptr;
+ }
+@@ -73,7 +73,7 @@ static inline long __must_check PTR_ERR(
+  * @ptr: The pointer to check.
+  * Return: true if @ptr is an error pointer, false otherwise.
+  */
+-static inline bool __must_check IS_ERR(__force const void *ptr)
++static __always_inline bool __must_check IS_ERR(__force const void *ptr)
+ {
+       return IS_ERR_VALUE((unsigned long)ptr);
+ }
+@@ -87,7 +87,7 @@ static inline bool __must_check IS_ERR(_
+  *
+  * Like IS_ERR(), but also returns true for a null pointer.
+  */
+-static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr)
++static __always_inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr)
+ {
+       return unlikely(!ptr) || IS_ERR_VALUE((unsigned long)ptr);
+ }
+@@ -99,7 +99,7 @@ static inline bool __must_check IS_ERR_O
+  * Explicitly cast an error-valued pointer to another pointer type in such a
+  * way as to make it clear that's what's going on.
+  */
+-static inline void * __must_check ERR_CAST(__force const void *ptr)
++static __always_inline void * __must_check ERR_CAST(__force const void *ptr)
+ {
+       /* cast away the const */
+       return (void *) ptr;
+@@ -122,7 +122,7 @@ static inline void * __must_check ERR_CA
+  *
+  * Return: The error code within @ptr if it is an error pointer; 0 otherwise.
+  */
+-static inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr)
++static __always_inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr)
+ {
+       if (IS_ERR(ptr))
+               return PTR_ERR(ptr);
diff --git a/queue-7.1/fbdev-fix-use-after-free-in-store_modes.patch b/queue-7.1/fbdev-fix-use-after-free-in-store_modes.patch
new file mode 100644 (file)
index 0000000..83eacd1
--- /dev/null
@@ -0,0 +1,73 @@
+From 2c1c805c65fb7dc7524e20376d6987721e73a0b1 Mon Sep 17 00:00:00 2001
+From: Ian Bridges <icb@fastmail.org>
+Date: Thu, 25 Jun 2026 23:50:48 -0500
+Subject: fbdev: fix use-after-free in store_modes()
+
+From: Ian Bridges <icb@fastmail.org>
+
+commit 2c1c805c65fb7dc7524e20376d6987721e73a0b1 upstream.
+
+store_modes() replaces a framebuffer's modelist with modes from userspace.
+On success it frees the old modelist with fb_destroy_modelist(). Two
+fields still point into that freed list.
+
+One pointer is fb_display[i].mode, the mode a console is using.
+fbcon_new_modelist() moves these pointers to the new list. It only does so
+for consoles still mapped to the framebuffer. An unmapped console is
+skipped and keeps its stale pointer. Unbinding fbcon, for example, sets
+con2fb_map[i] to -1 but leaves fb_display[i].mode set. An
+FBIOPUT_VSCREENINFO ioctl with FB_ACTIVATE_INV_MODE later reaches
+fbcon_mode_deleted(). That function reads the stale fb_display[i].mode
+through fb_mode_is_equal(). The read is a use-after-free.
+
+The other pointer is fb_info->mode, the current mode. It is set through
+the mode sysfs attribute. store_modes() does not update fb_info->mode, so
+it is left pointing into the freed list. show_mode(), the attribute's read
+handler, dereferences the stale fb_info->mode through mode_string(). The
+read is a use-after-free.
+
+Clear both pointers before freeing the list. Commit a1f305893074 ("fbcon:
+Set fb_display[i]->mode to NULL when the mode is released") added the
+helper fbcon_delete_modelist(). It clears every fb_display[i].mode that
+points into a given list. So far it is called only from the unregister
+path. Call it from store_modes() too, and set fb_info->mode to NULL.
+
+Reported-by: syzbot+81c7c6b52649fd07299d@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=81c7c6b52649fd07299d
+Cc: stable@vger.kernel.org
+Link: https://lore.kernel.org/all/ajjoDhAi2y4ArSlz@dev/
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Ian Bridges <icb@fastmail.org>
+Signed-off-by: Helge Deller <deller@gmx.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/video/fbdev/core/fbsysfs.c |   10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+--- a/drivers/video/fbdev/core/fbsysfs.c
++++ b/drivers/video/fbdev/core/fbsysfs.c
+@@ -11,6 +11,7 @@
+ #include <linux/major.h>
+ #include "fb_internal.h"
++#include "fbcon.h"
+ static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
+ {
+@@ -111,8 +112,15 @@ static ssize_t store_modes(struct device
+       if (fb_new_modelist(fb_info)) {
+               fb_destroy_modelist(&fb_info->modelist);
+               list_splice(&old_list, &fb_info->modelist);
+-      } else
++      } else {
++              /*
++               * fb_display[i].mode and fb_info->mode both point into the old
++               * list. Clear them before it is freed.
++               */
++              fbcon_delete_modelist(&old_list);
++              fb_info->mode = NULL;
+               fb_destroy_modelist(&old_list);
++      }
+       unlock_fb_info(fb_info);
+       console_unlock();
diff --git a/queue-7.1/fscrypt-fix-key-setup-in-edge-case-with-multiple-data-unit-sizes.patch b/queue-7.1/fscrypt-fix-key-setup-in-edge-case-with-multiple-data-unit-sizes.patch
new file mode 100644 (file)
index 0000000..22f53f7
--- /dev/null
@@ -0,0 +1,420 @@
+From dd015b566d505d698386103e9c80b739c7336eb8 Mon Sep 17 00:00:00 2001
+From: Eric Biggers <ebiggers@kernel.org>
+Date: Thu, 18 Jun 2026 11:06:51 -0700
+Subject: fscrypt: Fix key setup in edge case with multiple data unit sizes
+
+From: Eric Biggers <ebiggers@kernel.org>
+
+commit dd015b566d505d698386103e9c80b739c7336eb8 upstream.
+
+The addition of support for customizable data unit sizes introduced an
+edge case where a file's contents can be en/decrypted with the wrong
+data unit size.  It occurs when there are multiple v2 policies that:
+
+- Have *different* data unit sizes, via the log2_data_unit_size field
+
+- Share the same master_key_identifier, contents_encryption_mode, and
+  either FSCRYPT_POLICY_FLAG_DIRECT_KEY,
+  FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32, or
+  FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64
+
+- Are being used on the same filesystem, which also must be mounted with
+  the "inlinecrypt" mount option.
+
+Fortunately this edge case doesn't actually occur in practice.  I just
+found it via code review.  But it needs to be fixed regardless.
+
+The bug is caused by the data unit size not being fully considered when
+blk_crypto_keys are cached in mk_direct_keys, mk_iv_ino_lblk_32_keys,
+and mk_iv_ino_lblk_64_keys.  They're differentiated only by master key,
+encryption mode, and flag.  However, each one actually has a data unit
+size too.  Only the first data unit size that is cached is used.
+
+To fix this, start using the data unit size to differentiate the cached
+keys.  For several reasons, including avoiding increasing the size of
+struct fscrypt_master_key, just replace all three arrays with a single
+linked list instead of changing them into two-dimensional arrays.  This
+works well when considering that in practice at most 2 entries are used
+across all three arrays, so it was already mostly wasted space.
+
+For simplicity, make the list also take over the publish/subscribe of
+the prepared key itself.  That is, create separate list nodes for
+blk_crypto_keys vs crypto_skciphers, and add nodes to the list only when
+their key is actually prepared.  (Note that the legacy
+fscrypt_direct_keys table in fs/crypto/keysetup_v1.c already works this
+way.)  This eliminates the need for the additional memory barriers when
+reading and writing the fields of struct fscrypt_prepared_key.
+
+Note that I technically should have included the data unit size in the
+HKDF info string as well.  But it's too late to change that.
+
+Fixes: 5b1188847180 ("fscrypt: support crypto data unit size less than filesystem block size")
+Cc: stable@vger.kernel.org
+Link: https://patch.msgid.link/20260618180652.52742-1-ebiggers@kernel.org
+Signed-off-by: Eric Biggers <ebiggers@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/crypto/fscrypt_private.h |   52 +++++++++++-------
+ fs/crypto/inline_crypt.c    |    8 --
+ fs/crypto/keyring.c         |   23 +++++---
+ fs/crypto/keysetup.c        |  122 +++++++++++++++++++++++++++-----------------
+ 4 files changed, 122 insertions(+), 83 deletions(-)
+
+--- a/fs/crypto/fscrypt_private.h
++++ b/fs/crypto/fscrypt_private.h
+@@ -236,7 +236,7 @@ struct fscrypt_symlink_data {
+  * @tfm: crypto API transform object
+  * @blk_key: key for blk-crypto
+  *
+- * Normally only one of the fields will be non-NULL.
++ * Only one of the fields is non-NULL.
+  */
+ struct fscrypt_prepared_key {
+       struct crypto_sync_skcipher *tfm;
+@@ -245,6 +245,15 @@ struct fscrypt_prepared_key {
+ #endif
+ };
++/* An entry in the linked list ->mk_mode_keys */
++struct fscrypt_mode_key {
++      struct fscrypt_prepared_key key;
++      struct list_head link;
++      u8 hkdf_context;
++      u8 mode_num;
++      u8 data_unit_bits;
++};
++
+ /*
+  * fscrypt_inode_info - the "encryption key" for an inode
+  *
+@@ -430,20 +439,12 @@ int fscrypt_derive_sw_secret(struct supe
+  * @prep_key, depending on which encryption implementation the file will use.
+  */
+ static inline bool
+-fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
++fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key,
+                       const struct fscrypt_inode_info *ci)
+ {
+-      /*
+-       * The two smp_load_acquire()'s here pair with the smp_store_release()'s
+-       * in fscrypt_prepare_inline_crypt_key() and fscrypt_prepare_key().
+-       * I.e., in some cases (namely, if this prep_key is a per-mode
+-       * encryption key) another task can publish blk_key or tfm concurrently,
+-       * executing a RELEASE barrier.  We need to use smp_load_acquire() here
+-       * to safely ACQUIRE the memory the other task published.
+-       */
+       if (fscrypt_using_inline_encryption(ci))
+-              return smp_load_acquire(&prep_key->blk_key) != NULL;
+-      return smp_load_acquire(&prep_key->tfm) != NULL;
++              return prep_key->blk_key != NULL;
++      return prep_key->tfm != NULL;
+ }
+ #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
+@@ -486,10 +487,10 @@ fscrypt_derive_sw_secret(struct super_bl
+ }
+ static inline bool
+-fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
++fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key,
+                       const struct fscrypt_inode_info *ci)
+ {
+-      return smp_load_acquire(&prep_key->tfm) != NULL;
++      return prep_key->tfm != NULL;
+ }
+ #endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
+@@ -577,8 +578,8 @@ struct fscrypt_master_key {
+       /*
+        * Active and structural reference counts.  An active ref guarantees
+        * that the struct continues to exist, continues to be in the keyring
+-       * ->s_master_keys, and that any embedded subkeys (e.g.
+-       * ->mk_direct_keys) that have been prepared continue to exist.
++       * ->s_master_keys, and that any non-file-scoped subkeys (e.g.
++       * ->mk_mode_keys) that have been prepared continue to exist.
+        * A structural ref only guarantees that the struct continues to exist.
+        *
+        * There is one active ref associated with ->mk_present being true, and
+@@ -632,12 +633,21 @@ struct fscrypt_master_key {
+       spinlock_t              mk_decrypted_inodes_lock;
+       /*
+-       * Per-mode encryption keys for the various types of encryption policies
+-       * that use them.  Allocated and derived on-demand.
++       * A list of 'struct fscrypt_mode_key' for the (hkdf_context, mode_num,
++       * data_unit_bits, inlinecrypt) combinations that are in use for this
++       * master key, for hkdf_context in [HKDF_CONTEXT_DIRECT_KEY,
++       * HKDF_CONTEXT_IV_INO_LBLK_32_KEY, HKDF_CONTEXT_IV_INO_LBLK_64_KEY].
++       *
++       * This is a linked list and not a hash table because in practice
++       * there's just a single encryption policy per master key, using
++       * _at most_ 2 nodes in this list.  Per-file keys don't use this at all.
++       *
++       * This list is append-only until the master key is fully removed, at
++       * which time the list is cleared.  Before then,
++       * fscrypt_mode_key_setup_mutex synchronizes appends, and searches use
++       * the RCU read lock together with ->mk_sem held for read.
+        */
+-      struct fscrypt_prepared_key mk_direct_keys[FSCRYPT_MODE_MAX + 1];
+-      struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[FSCRYPT_MODE_MAX + 1];
+-      struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[FSCRYPT_MODE_MAX + 1];
++      struct list_head        mk_mode_keys;
+       /* Hash key for inode numbers.  Initialized only when needed. */
+       siphash_key_t           mk_ino_hash_key;
+--- a/fs/crypto/inline_crypt.c
++++ b/fs/crypto/inline_crypt.c
+@@ -198,13 +198,7 @@ int fscrypt_prepare_inline_crypt_key(str
+               goto fail;
+       }
+-      /*
+-       * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared().
+-       * I.e., here we publish ->blk_key with a RELEASE barrier so that
+-       * concurrent tasks can ACQUIRE it.  Note that this concurrency is only
+-       * possible for per-mode keys, not for per-file keys.
+-       */
+-      smp_store_release(&prep_key->blk_key, blk_key);
++      prep_key->blk_key = blk_key;
+       return 0;
+ fail:
+--- a/fs/crypto/keyring.c
++++ b/fs/crypto/keyring.c
+@@ -87,14 +87,14 @@ void fscrypt_put_master_key(struct fscry
+ void fscrypt_put_master_key_activeref(struct super_block *sb,
+                                     struct fscrypt_master_key *mk)
+ {
+-      size_t i;
++      struct fscrypt_mode_key *node, *tmp;
+       if (!refcount_dec_and_test(&mk->mk_active_refs))
+               return;
+       /*
+        * No active references left, so complete the full removal of this
+        * fscrypt_master_key struct by removing it from the keyring and
+-       * destroying any subkeys embedded in it.
++       * destroying any non-file-scoped subkeys.
+        */
+       if (WARN_ON_ONCE(!sb->s_master_keys))
+@@ -110,13 +110,16 @@ void fscrypt_put_master_key_activeref(st
+       WARN_ON_ONCE(mk->mk_present);
+       WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_inodes));
+-      for (i = 0; i <= FSCRYPT_MODE_MAX; i++) {
+-              fscrypt_destroy_prepared_key(
+-                              sb, &mk->mk_direct_keys[i]);
+-              fscrypt_destroy_prepared_key(
+-                              sb, &mk->mk_iv_ino_lblk_64_keys[i]);
+-              fscrypt_destroy_prepared_key(
+-                              sb, &mk->mk_iv_ino_lblk_32_keys[i]);
++      /*
++       * Destroy any non-file-scoped subkeys.  Since ->mk_active_refs == 0,
++       * they're no longer referenced by any inodes.  Nor can key setup run
++       * and use them again.  So they're no longer needed.  (This implies no
++       * concurrent readers, so we don't need list_del_rcu() for example.)
++       */
++      list_for_each_entry_safe(node, tmp, &mk->mk_mode_keys, link) {
++              fscrypt_destroy_prepared_key(sb, &node->key);
++              list_del(&node->link);
++              kfree(node);
+       }
+       memzero_explicit(&mk->mk_ino_hash_key,
+                        sizeof(mk->mk_ino_hash_key));
+@@ -445,6 +448,8 @@ static int add_new_master_key(struct sup
+       INIT_LIST_HEAD(&mk->mk_decrypted_inodes);
+       spin_lock_init(&mk->mk_decrypted_inodes_lock);
++      INIT_LIST_HEAD(&mk->mk_mode_keys);
++
+       if (mk_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {
+               err = allocate_master_key_users_keyring(mk);
+               if (err)
+--- a/fs/crypto/keysetup.c
++++ b/fs/crypto/keysetup.c
+@@ -163,13 +163,7 @@ int fscrypt_prepare_key(struct fscrypt_p
+       tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode);
+       if (IS_ERR(tfm))
+               return PTR_ERR(tfm);
+-      /*
+-       * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared().
+-       * I.e., here we publish ->tfm with a RELEASE barrier so that
+-       * concurrent tasks can ACQUIRE it.  Note that this concurrency is only
+-       * possible for per-mode keys, not for per-file keys.
+-       */
+-      smp_store_release(&prep_key->tfm, tfm);
++      prep_key->tfm = tfm;
+       return 0;
+ }
+@@ -190,9 +184,37 @@ int fscrypt_set_per_file_enc_key(struct
+       return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci);
+ }
++/*
++ * Find the fscrypt_prepared_key (if any) for a particular (mk, hkdf_context,
++ * mode_num, data_unit_bits, inlinecrypt) combination.
++ *
++ * The caller must hold ->mk_sem for reading and ->mk_present must be true,
++ * ensuring that ->mk_mode_keys is still append-only.
++ */
++static struct fscrypt_prepared_key *
++fscrypt_find_mode_key(struct fscrypt_master_key *mk, u8 hkdf_context,
++                    u8 mode_num, const struct fscrypt_inode_info *ci)
++{
++      struct fscrypt_mode_key *node;
++
++      /*
++       * The RCU read lock here is used only to synchronize with concurrent
++       * list_add_tail_rcu().  Concurrent deletions are impossible here, so
++       * returning a pointer to a node without taking any refcount is safe.
++       */
++      guard(rcu)();
++      list_for_each_entry_rcu(node, &mk->mk_mode_keys, link) {
++              if (node->hkdf_context == hkdf_context &&
++                  node->mode_num == mode_num &&
++                  node->data_unit_bits == ci->ci_data_unit_bits &&
++                  fscrypt_is_key_prepared(&node->key, ci))
++                      return &node->key;
++      }
++      return NULL;
++}
++
+ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci,
+                                 struct fscrypt_master_key *mk,
+-                                struct fscrypt_prepared_key *keys,
+                                 u8 hkdf_context, bool include_fs_uuid)
+ {
+       const struct inode *inode = ci->ci_inode;
+@@ -200,7 +222,8 @@ static int setup_per_mode_enc_key(struct
+       struct fscrypt_mode *mode = ci->ci_mode;
+       const u8 mode_num = mode - fscrypt_modes;
+       struct fscrypt_prepared_key *prep_key;
+-      u8 mode_key[FSCRYPT_MAX_RAW_KEY_SIZE];
++      struct fscrypt_mode_key *new_node;
++      u8 raw_mode_key[FSCRYPT_MAX_RAW_KEY_SIZE];
+       u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)];
+       unsigned int hkdf_infolen = 0;
+       bool use_hw_wrapped_key = false;
+@@ -223,48 +246,56 @@ static int setup_per_mode_enc_key(struct
+               use_hw_wrapped_key = true;
+       }
+-      prep_key = &keys[mode_num];
+-      if (fscrypt_is_key_prepared(prep_key, ci)) {
++      prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci);
++      if (prep_key) {
+               ci->ci_enc_key = *prep_key;
+               return 0;
+       }
+-      mutex_lock(&fscrypt_mode_key_setup_mutex);
++      guard(mutex)(&fscrypt_mode_key_setup_mutex);
++
++      prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci);
++      if (prep_key) {
++              ci->ci_enc_key = *prep_key;
++              return 0;
++      }
+-      if (fscrypt_is_key_prepared(prep_key, ci))
+-              goto done_unlock;
++      new_node = kzalloc_obj(*new_node);
++      if (!new_node)
++              return -ENOMEM;
++      new_node->hkdf_context = hkdf_context;
++      new_node->mode_num = mode_num;
++      new_node->data_unit_bits = ci->ci_data_unit_bits;
++      prep_key = &new_node->key;
+       if (use_hw_wrapped_key) {
+               err = fscrypt_prepare_inline_crypt_key(prep_key,
+                                                      mk->mk_secret.bytes,
+                                                      mk->mk_secret.size, true,
+                                                      ci);
+-              if (err)
+-                      goto out_unlock;
+-              goto done_unlock;
+-      }
+-
+-      BUILD_BUG_ON(sizeof(mode_num) != 1);
+-      BUILD_BUG_ON(sizeof(sb->s_uuid) != 16);
+-      BUILD_BUG_ON(sizeof(hkdf_info) != 17);
+-      hkdf_info[hkdf_infolen++] = mode_num;
+-      if (include_fs_uuid) {
+-              memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid,
+-                     sizeof(sb->s_uuid));
+-              hkdf_infolen += sizeof(sb->s_uuid);
+-      }
+-      fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, hkdf_info,
+-                          hkdf_infolen, mode_key, mode->keysize);
+-      err = fscrypt_prepare_key(prep_key, mode_key, ci);
+-      memzero_explicit(mode_key, mode->keysize);
+-      if (err)
+-              goto out_unlock;
+-done_unlock:
++      } else {
++              static_assert(sizeof(mode_num) == 1);
++              static_assert(sizeof(sb->s_uuid) == 16);
++              static_assert(sizeof(hkdf_info) == 17);
++              hkdf_info[hkdf_infolen++] = mode_num;
++              if (include_fs_uuid) {
++                      memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid,
++                             sizeof(sb->s_uuid));
++                      hkdf_infolen += sizeof(sb->s_uuid);
++              }
++              fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context,
++                                  hkdf_info, hkdf_infolen, raw_mode_key,
++                                  mode->keysize);
++              err = fscrypt_prepare_key(prep_key, raw_mode_key, ci);
++              memzero_explicit(raw_mode_key, mode->keysize);
++      }
++      if (err) {
++              kfree(new_node);
++              return err;
++      }
++      list_add_tail_rcu(&new_node->link, &mk->mk_mode_keys);
+       ci->ci_enc_key = *prep_key;
+-      err = 0;
+-out_unlock:
+-      mutex_unlock(&fscrypt_mode_key_setup_mutex);
+-      return err;
++      return 0;
+ }
+ /*
+@@ -311,8 +342,8 @@ static int fscrypt_setup_iv_ino_lblk_32_
+ {
+       int err;
+-      err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_32_keys,
+-                                   HKDF_CONTEXT_IV_INO_LBLK_32_KEY, true);
++      err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_IV_INO_LBLK_32_KEY,
++                                   true);
+       if (err)
+               return err;
+@@ -364,8 +395,8 @@ static int fscrypt_setup_v2_file_key(str
+                * encryption key.  This ensures that the master key is
+                * consistently used only for HKDF, avoiding key reuse issues.
+                */
+-              err = setup_per_mode_enc_key(ci, mk, mk->mk_direct_keys,
+-                                           HKDF_CONTEXT_DIRECT_KEY, false);
++              err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_DIRECT_KEY,
++                                           false);
+       } else if (ci->ci_policy.v2.flags &
+                  FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) {
+               /*
+@@ -374,9 +405,8 @@ static int fscrypt_setup_v2_file_key(str
+                * the IVs.  This format is optimized for use with inline
+                * encryption hardware compliant with the UFS standard.
+                */
+-              err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_64_keys,
+-                                           HKDF_CONTEXT_IV_INO_LBLK_64_KEY,
+-                                           true);
++              err = setup_per_mode_enc_key(
++                      ci, mk, HKDF_CONTEXT_IV_INO_LBLK_64_KEY, true);
+       } else if (ci->ci_policy.v2.flags &
+                  FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) {
+               err = fscrypt_setup_iv_ino_lblk_32_key(ci, mk);
diff --git a/queue-7.1/gcov-use-atomic-counter-updates-to-fix-concurrent-access-crashes.patch b/queue-7.1/gcov-use-atomic-counter-updates-to-fix-concurrent-access-crashes.patch
new file mode 100644 (file)
index 0000000..4206ac7
--- /dev/null
@@ -0,0 +1,125 @@
+From 56cb9b7d96b28a1173a510ab25354b6599ad3a33 Mon Sep 17 00:00:00 2001
+From: Konstantin Khorenko <khorenko@virtuozzo.com>
+Date: Mon, 11 May 2026 12:50:52 +0200
+Subject: gcov: use atomic counter updates to fix concurrent access crashes
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Konstantin Khorenko <khorenko@virtuozzo.com>
+
+commit 56cb9b7d96b28a1173a510ab25354b6599ad3a33 upstream.
+
+GCC's GCOV instrumentation can merge global branch counters with loop
+induction variables as an optimization.  In inflate_fast(), the inner copy
+loops get transformed so that the GCOV counter value is loaded multiple
+times to compute the loop base address, start index, and end bound.  Since
+GCOV counters are global (not per-CPU), concurrent execution on different
+CPUs causes the counter to change between loads, producing inconsistent
+values and out-of-bounds memory writes.
+
+The crash manifests during IPComp (IP Payload Compression) processing when
+inflate_fast() runs concurrently on multiple CPUs:
+
+  BUG: unable to handle page fault for address: ffffd0a3c0902ffa
+  RIP: inflate_fast+1431
+  Call Trace:
+   zlib_inflate
+   __deflate_decompress
+   crypto_comp_decompress
+   ipcomp_decompress [xfrm_ipcomp]
+   ipcomp_input [xfrm_ipcomp]
+   xfrm_input
+
+At the crash point, the compiler generated three loads from the same
+global GCOV counter (__gcov0.inflate_fast+216) to compute base, start, and
+end for an indexed loop.  Another CPU modified the counter between loads,
+making the values inconsistent - the write went 3.4 MB past a 65 KB
+buffer.
+
+Add -fprofile-update=prefer-atomic to CFLAGS_GCOV at the global level in
+the top-level Makefile, guarded by a try-run compile test.  The test
+compiles a minimal program with and without -fprofile-update=prefer-atomic
+using the full KBUILD_CFLAGS, then compares undefined symbols in the
+resulting object files.  If prefer-atomic introduces new undefined
+references (such as __atomic_fetch_add_8 on i386 or __aarch64_ldadd8_relax
+on arm64 with outline-atomics), the flag is not added -- the kernel does
+not link against libatomic.
+
+On architectures where GCC inlines 64-bit atomic counter updates (x86_64,
+s390, ...) the test passes and the flag is enabled, preventing the
+compiler from merging counters with loop induction variables and fixing
+the observed concurrent-access crash.
+
+On architectures where the flag would introduce libatomic dependencies, it
+is silently omitted and behaviour is no worse than before this patch.
+
+Move the CFLAGS_GCOV block from its original position (before the arch
+Makefile include) to after the core KBUILD_CFLAGS assignments but before
+the scripts/Makefile.gcc-plugins include.  This placement ensures the
+try-run test sees arch-specific flags (-m32, -march=,
+-mno-outline-atomics) while avoiding GCC plugin flags (-fplugin=) that
+would break the test on clean builds when plugin shared objects do not yet
+exist.
+
+Link: https://lore.kernel.org/20260511105052.417187-2-khorenko@virtuozzo.com
+Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
+Tested-by: Arnd Bergmann <arnd@arndb.de>
+Tested-by: Peter Oberparleiter <oberpar@linux.ibm.com>
+Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
+Cc: Masahiro Yamada <masahiroy@kernel.org>
+Cc: Miguel Ojeda <ojeda@kernel.org>
+Cc: Mikhail Zaslonko <zaslonko@linux.ibm.com>
+Cc: Nathan Chancellor <nathan@kernel.org>
+Cc: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
+Cc: Thomas Weißschuh <linux@weissschuh.net>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ Makefile |   27 +++++++++++++++++++++------
+ 1 file changed, 21 insertions(+), 6 deletions(-)
+
+--- a/Makefile
++++ b/Makefile
+@@ -827,12 +827,6 @@ endif # KBUILD_EXTMOD
+ # Defaults to vmlinux, but the arch makefile usually adds further targets
+ all: vmlinux
+-CFLAGS_GCOV   := -fprofile-arcs -ftest-coverage
+-ifdef CONFIG_CC_IS_GCC
+-CFLAGS_GCOV   += -fno-tree-loop-im
+-endif
+-export CFLAGS_GCOV
+-
+ # The arch Makefiles can override CC_FLAGS_FTRACE. We may also append it later.
+ ifdef CONFIG_FUNCTION_TRACER
+   CC_FLAGS_FTRACE := -pg
+@@ -1150,6 +1144,27 @@ endif
+ # Ensure compilers do not transform certain loops into calls to wcslen()
+ KBUILD_CFLAGS += -fno-builtin-wcslen
++CFLAGS_GCOV   := -fprofile-arcs -ftest-coverage
++ifdef CONFIG_CC_IS_GCC
++CFLAGS_GCOV   += -fno-tree-loop-im
++# Use atomic counter updates to avoid concurrent-access crashes in GCOV.
++# Only enable if -fprofile-update=prefer-atomic does not introduce new
++# undefined symbols (e.g. libatomic calls that the kernel cannot link).
++CFLAGS_GCOV   += $(call try-run,\
++      echo 'long long x; void f(void){x++;}' | \
++      $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \
++      -ftest-coverage -x c - -c -o "$$TMP.base" && \
++      echo 'long long x; void f(void){x++;}' | \
++      $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \
++      -ftest-coverage -fprofile-update=prefer-atomic \
++      -x c - -c -o "$$TMP" && \
++      $(NM) "$$TMP.base" | grep ' U ' > "$$TMP.ubase" || true ; \
++      $(NM) "$$TMP" | grep ' U ' > "$$TMP.utest" || true ; \
++      cmp -s "$$TMP.ubase" "$$TMP.utest",\
++      -fprofile-update=prefer-atomic)
++endif
++export CFLAGS_GCOV
++
+ # change __FILE__ to the relative path to the source directory
+ ifdef building_out_of_srctree
+ KBUILD_CPPFLAGS += -fmacro-prefix-map=$(srcroot)/=
diff --git a/queue-7.1/keys-fix-overflow-in-keyctl_pkey_params_get_2.patch b/queue-7.1/keys-fix-overflow-in-keyctl_pkey_params_get_2.patch
new file mode 100644 (file)
index 0000000..5236b82
--- /dev/null
@@ -0,0 +1,65 @@
+From cb481e59ea6cae3b7796ac1d7a22b6b24c3f3c0b Mon Sep 17 00:00:00 2001
+From: Jarkko Sakkinen <jarkko@kernel.org>
+Date: Mon, 1 Jun 2026 23:11:54 +0300
+Subject: KEYS: fix overflow in keyctl_pkey_params_get_2()
+
+From: Jarkko Sakkinen <jarkko@kernel.org>
+
+commit cb481e59ea6cae3b7796ac1d7a22b6b24c3f3c0b upstream.
+
+The length for the internal output buffer is calculated incorrectly, which
+can result overflow when a too small buffer is provided.
+
+Fix the bug by allocating internal output with the size of the maximum
+length of the cryptographic primitive instead of caller provided size.
+
+Link: https://lore.kernel.org/keyrings/20260531024914.3712130-1-jarkko@kernel.org/
+Cc: stable@vger.kernel.org # v4.20+
+Fixes: 00d60fd3b932 ("KEYS: Provide keyctls to drive the new key type ops for asymmetric keys [ver #2]")
+Reported-by: Alessandro Groppo <ale.grpp@gmail.com>
+Tested-by: Alessandro Groppo <ale.grpp@gmail.com>
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ security/keys/keyctl_pkey.c |    9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+--- a/security/keys/keyctl_pkey.c
++++ b/security/keys/keyctl_pkey.c
+@@ -138,28 +138,35 @@ static int keyctl_pkey_params_get_2(cons
+               if (uparams.in_len  > info.max_dec_size ||
+                   uparams.out_len > info.max_enc_size)
+                       return -EINVAL;
++
++              params->out_len = info.max_enc_size;
+               break;
+       case KEYCTL_PKEY_DECRYPT:
+               if (uparams.in_len  > info.max_enc_size ||
+                   uparams.out_len > info.max_dec_size)
+                       return -EINVAL;
++
++              params->out_len = info.max_dec_size;
+               break;
+       case KEYCTL_PKEY_SIGN:
+               if (uparams.in_len  > info.max_data_size ||
+                   uparams.out_len > info.max_sig_size)
+                       return -EINVAL;
++
++              params->out_len = info.max_sig_size;
+               break;
+       case KEYCTL_PKEY_VERIFY:
+               if (uparams.in_len  > info.max_data_size ||
+                   uparams.in2_len > info.max_sig_size)
+                       return -EINVAL;
++
++              params->out_len = info.max_sig_size;
+               break;
+       default:
+               BUG();
+       }
+       params->in_len  = uparams.in_len;
+-      params->out_len = uparams.out_len; /* Note: same as in2_len */
+       return 0;
+ }
diff --git a/queue-7.1/keys-pin-request_key_auth-payload-in-instantiate-paths.patch b/queue-7.1/keys-pin-request_key_auth-payload-in-instantiate-paths.patch
new file mode 100644 (file)
index 0000000..5dadcd2
--- /dev/null
@@ -0,0 +1,215 @@
+From fd15b457a86939c38aa12116adabd8ff686c5e51 Mon Sep 17 00:00:00 2001
+From: Shaomin Chen <eeesssooo020@gmail.com>
+Date: Wed, 10 Jun 2026 13:10:05 +0300
+Subject: keys: Pin request_key_auth payload in instantiate paths
+
+From: Shaomin Chen <eeesssooo020@gmail.com>
+
+commit fd15b457a86939c38aa12116adabd8ff686c5e51 upstream.
+
+A: request_key()       B: KEYCTL_INSTANTIATE_IOV
+================       =========================
+
+create auth key
+store rka in auth key
+wait for helper
+                       get auth key
+                       load rka from auth key
+                       copy user payload
+                       sleep on #PF
+
+helper completed
+detach and free rka
+destroy auth key
+                       wake up
+                       use rka->target_key
+                       **USE-AFTER-FREE**
+
+Give request_key_auth payloads a refcount.  Take a payload reference while
+authkey->sem stabilizes the payload and revocation state.  Hold that
+reference across the instantiate and reject paths.  Drop the auth key
+owning reference from revoke and destroy.
+
+[jarkko: Replaced the first two paragraphs of text with an actual
+ concurrency scenario.]
+Cc: stable@vger.kernel.org # v5.10+
+Fixes: b5f545c880a2 ("[PATCH] keys: Permit running process to instantiate keys")
+Reported-by: Shaomin Chen <eeesssooo020@gmail.com>
+Closes: https://lore.kernel.org/r/20260519144403.436694-1-eeesssooo020@gmail.com
+Signed-off-by: Shaomin Chen <eeesssooo020@gmail.com>
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/keys/request_key_auth-type.h |    2 ++
+ security/keys/internal.h             |    2 ++
+ security/keys/keyctl.c               |   24 ++++++++++++++++++------
+ security/keys/request_key_auth.c     |   33 +++++++++++++++++++++++++++++++--
+ 4 files changed, 53 insertions(+), 8 deletions(-)
+
+--- a/include/keys/request_key_auth-type.h
++++ b/include/keys/request_key_auth-type.h
+@@ -9,12 +9,14 @@
+ #define _KEYS_REQUEST_KEY_AUTH_TYPE_H
+ #include <linux/key.h>
++#include <linux/refcount.h>
+ /*
+  * Authorisation record for request_key().
+  */
+ struct request_key_auth {
+       struct rcu_head         rcu;
++      refcount_t              usage;
+       struct key              *target_key;
+       struct key              *dest_keyring;
+       const struct cred       *cred;
+--- a/security/keys/internal.h
++++ b/security/keys/internal.h
+@@ -208,6 +208,8 @@ extern struct key *request_key_auth_new(
+                                       const void *callout_info,
+                                       size_t callout_len,
+                                       struct key *dest_keyring);
++struct request_key_auth *request_key_auth_get(struct key *authkey);
++void request_key_auth_put(struct request_key_auth *rka);
+ extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
+--- a/security/keys/keyctl.c
++++ b/security/keys/keyctl.c
+@@ -1197,9 +1197,13 @@ static long keyctl_instantiate_key_commo
+       if (!instkey)
+               goto error;
+-      rka = instkey->payload.data[0];
+-      if (rka->target_key->serial != id)
++      rka = request_key_auth_get(instkey);
++      if (!rka) {
++              ret = -EKEYREVOKED;
+               goto error;
++      }
++      if (rka->target_key->serial != id)
++              goto error_put_rka;
+       /* pull the payload in if one was supplied */
+       payload = NULL;
+@@ -1208,7 +1212,7 @@ static long keyctl_instantiate_key_commo
+               ret = -ENOMEM;
+               payload = kvmalloc(plen, GFP_KERNEL);
+               if (!payload)
+-                      goto error;
++                      goto error_put_rka;
+               ret = -EFAULT;
+               if (!copy_from_iter_full(payload, plen, from))
+@@ -1234,6 +1238,8 @@ static long keyctl_instantiate_key_commo
+ error2:
+       kvfree_sensitive(payload, plen);
++error_put_rka:
++      request_key_auth_put(rka);
+ error:
+       return ret;
+ }
+@@ -1358,15 +1364,19 @@ long keyctl_reject_key(key_serial_t id,
+       if (!instkey)
+               goto error;
+-      rka = instkey->payload.data[0];
+-      if (rka->target_key->serial != id)
++      rka = request_key_auth_get(instkey);
++      if (!rka) {
++              ret = -EKEYREVOKED;
+               goto error;
++      }
++      if (rka->target_key->serial != id)
++              goto error_put_rka;
+       /* find the destination keyring if present (which must also be
+        * writable) */
+       ret = get_instantiation_keyring(ringid, rka, &dest_keyring);
+       if (ret < 0)
+-              goto error;
++              goto error_put_rka;
+       /* instantiate the key and link it into a keyring */
+       ret = key_reject_and_link(rka->target_key, timeout, error,
+@@ -1379,6 +1389,8 @@ long keyctl_reject_key(key_serial_t id,
+       if (ret == 0)
+               keyctl_change_reqkey_auth(NULL);
++error_put_rka:
++      request_key_auth_put(rka);
+ error:
+       return ret;
+ }
+--- a/security/keys/request_key_auth.c
++++ b/security/keys/request_key_auth.c
+@@ -23,6 +23,7 @@ static void request_key_auth_describe(co
+ static void request_key_auth_revoke(struct key *);
+ static void request_key_auth_destroy(struct key *);
+ static long request_key_auth_read(const struct key *, char *, size_t);
++static void request_key_auth_rcu_disposal(struct rcu_head *);
+ /*
+  * The request-key authorisation key type definition.
+@@ -116,6 +117,31 @@ static void free_request_key_auth(struct
+ }
+ /*
++ * Take a reference to the request-key authorisation payload so callers can
++ * drop authkey->sem before doing operations that may sleep.
++ */
++struct request_key_auth *request_key_auth_get(struct key *authkey)
++{
++      struct request_key_auth *rka;
++
++      down_read(&authkey->sem);
++      rka = dereference_key_locked(authkey);
++      if (rka && !test_bit(KEY_FLAG_REVOKED, &authkey->flags))
++              refcount_inc(&rka->usage);
++      else
++              rka = NULL;
++      up_read(&authkey->sem);
++
++      return rka;
++}
++
++void request_key_auth_put(struct request_key_auth *rka)
++{
++      if (rka && refcount_dec_and_test(&rka->usage))
++              call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
++}
++
++/*
+  * Dispose of the request_key_auth record under RCU conditions
+  */
+ static void request_key_auth_rcu_disposal(struct rcu_head *rcu)
+@@ -136,8 +162,10 @@ static void request_key_auth_revoke(stru
+       struct request_key_auth *rka = dereference_key_locked(key);
+       kenter("{%d}", key->serial);
++      if (!rka)
++              return;
+       rcu_assign_keypointer(key, NULL);
+-      call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
++      request_key_auth_put(rka);
+ }
+ /*
+@@ -150,7 +178,7 @@ static void request_key_auth_destroy(str
+       kenter("{%d}", key->serial);
+       if (rka) {
+               rcu_assign_keypointer(key, NULL);
+-              call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
++              request_key_auth_put(rka);
+       }
+ }
+@@ -174,6 +202,7 @@ struct key *request_key_auth_new(struct
+       rka = kzalloc_obj(*rka);
+       if (!rka)
+               goto error;
++      refcount_set(&rka->usage, 1);
+       rka->callout_info = kmemdup(callout_info, callout_len, GFP_KERNEL);
+       if (!rka->callout_info)
+               goto error_free_rka;
diff --git a/queue-7.1/kvm-arm64-omit-tag-sync-on-stage-2-mappings-of-the-zero-page.patch b/queue-7.1/kvm-arm64-omit-tag-sync-on-stage-2-mappings-of-the-zero-page.patch
new file mode 100644 (file)
index 0000000..9fd9efe
--- /dev/null
@@ -0,0 +1,44 @@
+From 2986a625740599fe6e7635b0586fed2a95bcd1f7 Mon Sep 17 00:00:00 2001
+From: Ard Biesheuvel <ardb@kernel.org>
+Date: Thu, 4 Jun 2026 17:11:56 +0200
+Subject: KVM: arm64: Omit tag sync on stage-2 mappings of the zero page
+
+From: Ard Biesheuvel <ardb@kernel.org>
+
+commit 2986a625740599fe6e7635b0586fed2a95bcd1f7 upstream.
+
+Commit
+
+   f620d66af316 ("arm64: mte: Do not flag the zero page as PG_mte_tagged")
+
+removed the PG_mte_tagged flag from the zero page, but missed a KVM code
+path that may set this flag on the zero page when it is used in a
+stage-2 CoW mapping of anonymous memory.
+
+So disregard the zero page explicitly in sanitise_mte_tags().
+
+Fixes: f620d66af316 ("arm64: mte: Do not flag the zero page as PG_mte_tagged")
+Cc: stable@vger.kernel.org # 5.10.x
+Suggested-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
+Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Will Deacon <will@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/arm64/kvm/mmu.c |    5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/arch/arm64/kvm/mmu.c
++++ b/arch/arm64/kvm/mmu.c
+@@ -1479,6 +1479,11 @@ static void sanitise_mte_tags(struct kvm
+       if (!kvm_has_mte(kvm))
+               return;
++      if (is_zero_pfn(pfn)) {
++              WARN_ON_ONCE(nr_pages != 1);
++              return;
++      }
++
+       if (folio_test_hugetlb(folio)) {
+               /* Hugetlb has MTE flags set on head page only */
+               if (folio_try_hugetlb_mte_tagging(folio)) {
diff --git a/queue-7.1/ntb-epf-avoid-pci_iounmap-with-offset-when-peer_spad-and-config-share-bar.patch b/queue-7.1/ntb-epf-avoid-pci_iounmap-with-offset-when-peer_spad-and-config-share-bar.patch
new file mode 100644 (file)
index 0000000..cf6df66
--- /dev/null
@@ -0,0 +1,56 @@
+From d876153680e3d721d385e554def919bce3d18c74 Mon Sep 17 00:00:00 2001
+From: Koichiro Den <den@valinux.co.jp>
+Date: Wed, 4 Mar 2026 11:05:27 +0900
+Subject: NTB: epf: Avoid pci_iounmap() with offset when PEER_SPAD and CONFIG share BAR
+
+From: Koichiro Den <den@valinux.co.jp>
+
+commit d876153680e3d721d385e554def919bce3d18c74 upstream.
+
+When BAR_PEER_SPAD and BAR_CONFIG share one PCI BAR, the module teardown
+path ends up calling pci_iounmap() on the same iomem with some offset,
+which is unnecessary and triggers a kernel warning like the following:
+
+  Trying to vunmap() nonexistent vm area (0000000069a5ffe8)
+  WARNING: mm/vmalloc.c:3470 at vunmap+0x58/0x68, CPU#5: modprobe/2937
+  [...]
+  Call trace:
+   vunmap+0x58/0x68 (P)
+   iounmap+0x34/0x48
+   pci_iounmap+0x2c/0x40
+   ntb_epf_pci_remove+0x44/0x80 [ntb_hw_epf]
+   pci_device_remove+0x48/0xf8
+   device_remove+0x50/0x88
+   device_release_driver_internal+0x1c8/0x228
+   driver_detach+0x50/0xb0
+   bus_remove_driver+0x74/0x100
+   driver_unregister+0x34/0x68
+   pci_unregister_driver+0x34/0xa0
+   ntb_epf_pci_driver_exit+0x14/0xfe0 [ntb_hw_epf]
+  [...]
+
+Fix it by unmapping only when PEER_SPAD and CONFIG use difference bars.
+
+Cc: stable@vger.kernel.org
+Fixes: e75d5ae8ab88 ("NTB: epf: Allow more flexibility in the memory BAR map method")
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Signed-off-by: Koichiro Den <den@valinux.co.jp>
+Reviewed-by: Dave Jiang <dave.jiang@intel.com>
+Signed-off-by: Jon Mason <jdmason@kudzu.us>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/ntb/hw/epf/ntb_hw_epf.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/ntb/hw/epf/ntb_hw_epf.c
++++ b/drivers/ntb/hw/epf/ntb_hw_epf.c
+@@ -646,7 +646,8 @@ static void ntb_epf_deinit_pci(struct nt
+       struct pci_dev *pdev = ndev->ntb.pdev;
+       pci_iounmap(pdev, ndev->ctrl_reg);
+-      pci_iounmap(pdev, ndev->peer_spad_reg);
++      if (ndev->barno_map[BAR_PEER_SPAD] != ndev->barno_map[BAR_CONFIG])
++              pci_iounmap(pdev, ndev->peer_spad_reg);
+       pci_iounmap(pdev, ndev->db_reg);
+       pci_release_regions(pdev);
index d2928bab6c16cb4b8bf953dcd6befcdcdf0ac161..11eda06db667123ea92a508c4a1e12dbffff3f30 100644 (file)
@@ -35,3 +35,24 @@ net-ip_gre-require-cap_net_admin-in-the-device-netns-for-changelink.patch
 pci-p2pdma-add-intel-qat-dsa-iaa-devices-to-whitelist.patch
 apparmor-mediate-the-implicit-connect-of-tcp-fast-open-sendmsg.patch
 apparmor-fix-use-after-free-in-rawdata-dedup-loop.patch
+ntb-epf-avoid-pci_iounmap-with-offset-when-peer_spad-and-config-share-bar.patch
+fbdev-fix-use-after-free-in-store_modes.patch
+fscrypt-fix-key-setup-in-edge-case-with-multiple-data-unit-sizes.patch
+block-invalidate-cached-plug-timestamp-after-task-switch.patch
+kvm-arm64-omit-tag-sync-on-stage-2-mappings-of-the-zero-page.patch
+err.h-use-__always_inline-on-all-error-pointer-helpers.patch
+gcov-use-atomic-counter-updates-to-fix-concurrent-access-crashes.patch
+keys-fix-overflow-in-keyctl_pkey_params_get_2.patch
+keys-pin-request_key_auth-payload-in-instantiate-paths.patch
+userfaultfd-ensure-mremap_userfaultfd_fail-releases-mmap_changing.patch
+userfaultfd-build-__vma_uffd_flags-from-config-gated-masks.patch
+wifi-mt76-mt76x2u-add-support-for-elecom-wdc-867su3s.patch
+wifi-mt76-mt7925-don-t-disable-ap-bss-when-removing-tdls-peer.patch
+wifi-ath11k-fix-warning-when-unbinding.patch
+wifi-rtl8xxxu-detect-the-maximum-supported-channel-width.patch
+wifi-rtlwifi-rtl8821ae-fix-c2h-bit-location-in-rx-descriptor.patch
+wifi-rtw88-increase-tx-report-timeout-to-fix-race-condition.patch
+wifi-rtw88-usb-fix-memory-leaks-on-usb-write-failures.patch
+wifi-iwlwifi-mvm-fix-race-condition-in-ptp-removal.patch
+wifi-iwlwifi-mld-fix-race-condition-in-ptp-removal.patch
+wifi-iwlwifi-mld-validate-sta_mask-before-ffs-in-ba-session-handlers.patch
diff --git a/queue-7.1/userfaultfd-build-__vma_uffd_flags-from-config-gated-masks.patch b/queue-7.1/userfaultfd-build-__vma_uffd_flags-from-config-gated-masks.patch
new file mode 100644 (file)
index 0000000..547e907
--- /dev/null
@@ -0,0 +1,125 @@
+From cc7a9f6e57c4f71e8e1fee3274b1ae8770f2a743 Mon Sep 17 00:00:00 2001
+From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
+Date: Fri, 29 May 2026 18:23:30 +0100
+Subject: userfaultfd: build __VMA_UFFD_FLAGS from config-gated masks
+
+From: Kiryl Shutsemau (Meta) <kas@kernel.org>
+
+commit cc7a9f6e57c4f71e8e1fee3274b1ae8770f2a743 upstream.
+
+The VMA flags bitmap is a single word today: NUM_VMA_FLAG_BITS is
+BITS_PER_LONG, so on 32-bit vma_flags_t holds only 32 bits.  (The bitmap
+type exists so this can grow past BITS_PER_LONG later; until it does,
+anything declared above the first word is out of range on 32-bit.) The bit
+enum nevertheless declares some bits unconditionally above BITS_PER_LONG
+-- VMA_UFFD_MINOR_BIT is 41, with VM_UFFD_MINOR == VM_NONE on 32-bit so no
+VMA actually carries the bit.
+
+__VMA_UFFD_FLAGS feeds VMA_UFFD_MINOR_BIT to mk_vma_flags()
+unconditionally.  On 32-bit that becomes __set_bit(41, &one_long), a write
+one word past the end of the single-word bitmap.  The compiler folds the
+out-of-bounds store with wraparound (1UL << (41 % 32) == bit 9) into the
+first word; bit 9 is already in __VMA_UFFD_FLAGS so the mask happens to
+come out right today, but it is an out-of-bounds write all the same, and
+any high-numbered bit whose mod-BITS_PER_LONG position is otherwise unused
+would silently OR an extra bit into the mask.
+
+Rather than feed bit numbers that may not exist on the current build to
+mk_vma_flags(), build the mask from whole per-mode masks that collapse to
+EMPTY_VMA_FLAGS when their feature is unavailable.  Add
+mk_vma_flags_from_masks() for that, and define VMA_UFFD_MISSING / _WP /
+_MINOR alongside the VM_UFFD_* flags, gating VMA_UFFD_MINOR on the same
+config as VM_UFFD_MINOR (which implies 64BIT, where bit 41 fits).  An
+out-of-range bit is then never materialised, on any arch, and the in-range
+fast path stays a compile-time constant.
+
+Link: https://lore.kernel.org/20260529172331.356655-7-kas@kernel.org
+Fixes: 9ea35a25d51b ("mm: introduce VMA flags bitmap type")
+Signed-off-by: Kiryl Shutsemau <kas@kernel.org>
+Reported-by: Sashiko AI review <sashiko-bot@kernel.org>
+Suggested-by: Lorenzo Stoakes <ljs@kernel.org>
+Reviewed-by: Lorenzo Stoakes <ljs@kernel.org>
+Assisted-by: Claude:claude-opus-4-8
+Cc: David Hildenbrand <david@kernel.org>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Peter Xu <peterx@redhat.com>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Vlastimil Babka <vbabka@kernel.org>
+Cc: Balbir Singh <balbirs@nvidia.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/mm.h            |   39 +++++++++++++++++++++++++++++++++++++++
+ include/linux/userfaultfd_k.h |    4 ++--
+ 2 files changed, 41 insertions(+), 2 deletions(-)
+
+--- a/include/linux/mm.h
++++ b/include/linux/mm.h
+@@ -496,6 +496,21 @@ enum {
+ #else
+ #define VM_UFFD_MINOR VM_NONE
+ #endif
++
++/*
++ * vma_flags_t masks for the userfaultfd VMA flags. VMA_UFFD_MINOR is gated on
++ * the same config as VM_UFFD_MINOR -- which implies 64BIT, where the bit fits
++ * -- so an out-of-range bit is never fed to mk_vma_flags() on a build whose
++ * bitmap cannot hold it.
++ */
++#define VMA_UFFD_MISSING      mk_vma_flags(VMA_UFFD_MISSING_BIT)
++#define VMA_UFFD_WP           mk_vma_flags(VMA_UFFD_WP_BIT)
++#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
++#define VMA_UFFD_MINOR                mk_vma_flags(VMA_UFFD_MINOR_BIT)
++#else
++#define VMA_UFFD_MINOR                EMPTY_VMA_FLAGS
++#endif
++
+ #ifdef CONFIG_64BIT
+ #define VM_ALLOW_ANY_UNCACHED INIT_VM_FLAG(ALLOW_ANY_UNCACHED)
+ #define VM_SEALED             INIT_VM_FLAG(SEALED)
+@@ -1238,6 +1253,30 @@ static __always_inline void vma_flags_se
+ #define vma_flags_set(flags, ...) \
+       vma_flags_set_mask(flags, mk_vma_flags(__VA_ARGS__))
++static __always_inline vma_flags_t __mk_vma_flags_from_masks(size_t count,
++              const vma_flags_t *masks)
++{
++      vma_flags_t flags = EMPTY_VMA_FLAGS;
++      size_t i;
++
++      for (i = 0; i < count; i++)
++              vma_flags_set_mask(&flags, masks[i]);
++      return flags;
++}
++
++/*
++ * Combine pre-computed vma_flags_t masks into one value, e.g.:
++ *
++ * vma_flags_t flags = mk_vma_flags_from_masks(VMA_UFFD_WP, VMA_UFFD_MINOR);
++ *
++ * Unlike mk_vma_flags(), which takes bit numbers, this takes whole masks --
++ * each of which may be EMPTY_VMA_FLAGS when its feature is unavailable -- so a
++ * bit that does not exist on the current build is never materialised.
++ */
++#define mk_vma_flags_from_masks(...)                                  \
++      __mk_vma_flags_from_masks(COUNT_ARGS(__VA_ARGS__),              \
++              (const vma_flags_t []){__VA_ARGS__})
++
+ /* Clear all of the to-clear flags in flags, non-atomically. */
+ static __always_inline void vma_flags_clear_mask(vma_flags_t *flags,
+               vma_flags_t to_clear)
+--- a/include/linux/userfaultfd_k.h
++++ b/include/linux/userfaultfd_k.h
+@@ -23,8 +23,8 @@
+ /* The set of all possible UFFD-related VM flags. */
+ #define __VM_UFFD_FLAGS (VM_UFFD_MISSING | VM_UFFD_WP | VM_UFFD_MINOR)
+-#define __VMA_UFFD_FLAGS mk_vma_flags(VMA_UFFD_MISSING_BIT, VMA_UFFD_WP_BIT, \
+-                                    VMA_UFFD_MINOR_BIT)
++#define __VMA_UFFD_FLAGS mk_vma_flags_from_masks(VMA_UFFD_MISSING, VMA_UFFD_WP, \
++                                               VMA_UFFD_MINOR)
+ /*
+  * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining
diff --git a/queue-7.1/userfaultfd-ensure-mremap_userfaultfd_fail-releases-mmap_changing.patch b/queue-7.1/userfaultfd-ensure-mremap_userfaultfd_fail-releases-mmap_changing.patch
new file mode 100644 (file)
index 0000000..baa7907
--- /dev/null
@@ -0,0 +1,48 @@
+From 0496a59745b0723ea74274db16fd5c8b1379b9a9 Mon Sep 17 00:00:00 2001
+From: "Mike Rapoport (Microsoft)" <rppt@kernel.org>
+Date: Wed, 13 May 2026 11:14:16 +0300
+Subject: userfaultfd: ensure mremap_userfaultfd_fail() releases mmap_changing
+
+From: Mike Rapoport (Microsoft) <rppt@kernel.org>
+
+commit 0496a59745b0723ea74274db16fd5c8b1379b9a9 upstream.
+
+Sashiko says:
+
+  mremap_userfaultfd_prep() increments ctx->mmap_changing to stall
+  concurrent operations, but mremap_userfaultfd_fail() does not
+  decrement it before dropping the context reference.
+
+If an mremap operation fails, ctx->mmap_changing remains elevated. This
+will causes subsequent userfaultfd operations like a UFFDIO_COPY to fail
+with -EAGAIN.
+
+Decrement ctx->mmap_changing in mremap_userfaultfd_fail().
+
+Link: https://sashiko.dev/#/patchset/20260430113512.115938-1-rppt@kernel.org
+Link: https://lore.kernel.org/20260513081416.495963-1-rppt@kernel.org
+Fixes: df2cc96e7701 ("userfaultfd: prevent non-cooperative events vs mcopy_atomic races")
+Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
+Reviewed-by: David Hildenbrand (Arm) <david@kernel.org>
+Cc: Al Viro <viro@zeniv.linux.org.uk>
+Cc: Christian Brauner <brauner@kernel.org>
+Cc: Jan Kara <jack@suse.cz>
+Cc: Peter Xu <peterx@redhat.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/userfaultfd.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/fs/userfaultfd.c
++++ b/fs/userfaultfd.c
+@@ -786,6 +786,8 @@ void mremap_userfaultfd_fail(struct vm_u
+       if (!ctx)
+               return;
++      atomic_dec(&ctx->mmap_changing);
++      VM_WARN_ON_ONCE(atomic_read(&ctx->mmap_changing) < 0);
+       userfaultfd_ctx_put(ctx);
+ }
diff --git a/queue-7.1/wifi-ath11k-fix-warning-when-unbinding.patch b/queue-7.1/wifi-ath11k-fix-warning-when-unbinding.patch
new file mode 100644 (file)
index 0000000..2d2d425
--- /dev/null
@@ -0,0 +1,49 @@
+From 8b7a26b6681922a38cd5a7829ace61f8e54df9b7 Mon Sep 17 00:00:00 2001
+From: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
+Date: Mon, 20 Apr 2026 13:01:29 +0200
+Subject: wifi: ath11k: fix warning when unbinding
+
+From: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
+
+commit 8b7a26b6681922a38cd5a7829ace61f8e54df9b7 upstream.
+
+If there is an error during some initialization related to firmware,
+the buffers dp->tx_ring[i].tx_status are released.
+However this is released again when the device is unbinded (ath11k_pci),
+and we get:
+WARNING: CPU: 0 PID: 6231 at mm/slub.c:4368 free_large_kmalloc+0x57/0x90
+Call Trace:
+free_large_kmalloc
+ath11k_dp_free
+ath11k_core_deinit
+ath11k_pci_remove
+...
+
+The issue is always reproducible from a VM because the MSI addressing
+initialization is failing.
+
+In order to fix the issue, just set the buffers to NULL after releasing in
+order to avoid the double free.
+
+Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
+Cc: stable@vger.kernel.org
+Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260420110130.509670-1-jtornosm@redhat.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/ath/ath11k/dp.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/wireless/ath/ath11k/dp.c
++++ b/drivers/net/wireless/ath/ath11k/dp.c
+@@ -1040,6 +1040,7 @@ void ath11k_dp_free(struct ath11k_base *
+               idr_destroy(&dp->tx_ring[i].txbuf_idr);
+               spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock);
+               kfree(dp->tx_ring[i].tx_status);
++              dp->tx_ring[i].tx_status = NULL;
+       }
+       /* Deinit any SOC level resource */
diff --git a/queue-7.1/wifi-iwlwifi-mld-fix-race-condition-in-ptp-removal.patch b/queue-7.1/wifi-iwlwifi-mld-fix-race-condition-in-ptp-removal.patch
new file mode 100644 (file)
index 0000000..afd2a1a
--- /dev/null
@@ -0,0 +1,46 @@
+From e1fc08598aa34b28359831e768076f56632720c1 Mon Sep 17 00:00:00 2001
+From: Junjie Cao <junjie.cao@intel.com>
+Date: Thu, 12 Feb 2026 20:50:35 +0800
+Subject: wifi: iwlwifi: mld: fix race condition in PTP removal
+
+From: Junjie Cao <junjie.cao@intel.com>
+
+commit e1fc08598aa34b28359831e768076f56632720c1 upstream.
+
+iwl_mld_ptp_remove() calls cancel_delayed_work_sync() only after
+ptp_clock_unregister() and clearing ptp_data state (ptp_clock,
+last_gp2, wrap_counter).
+
+This creates a race where the delayed work iwl_mld_ptp_work() can
+execute between ptp_clock_unregister() and cancel_delayed_work_sync(),
+observing partially cleared PTP state.
+
+Move cancel_delayed_work_sync() before ptp_clock_unregister() to
+ensure the delayed work is fully stopped before any PTP cleanup
+begins.
+
+Cc: stable@vger.kernel.org
+Reviewed-by: Simon Horman <horms@kernel.org>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: Junjie Cao <junjie.cao@intel.com>
+Link: https://patch.msgid.link/20260212125035.1345718-2-junjie.cao@intel.com
+Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/intel/iwlwifi/mld/ptp.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
++++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
+@@ -321,10 +321,10 @@ void iwl_mld_ptp_remove(struct iwl_mld *
+                              mld->ptp_data.ptp_clock_info.name,
+                              ptp_clock_index(mld->ptp_data.ptp_clock));
++              cancel_delayed_work_sync(&mld->ptp_data.dwork);
+               ptp_clock_unregister(mld->ptp_data.ptp_clock);
+               mld->ptp_data.ptp_clock = NULL;
+               mld->ptp_data.last_gp2 = 0;
+               mld->ptp_data.wrap_counter = 0;
+-              cancel_delayed_work_sync(&mld->ptp_data.dwork);
+       }
+ }
diff --git a/queue-7.1/wifi-iwlwifi-mld-validate-sta_mask-before-ffs-in-ba-session-handlers.patch b/queue-7.1/wifi-iwlwifi-mld-validate-sta_mask-before-ffs-in-ba-session-handlers.patch
new file mode 100644 (file)
index 0000000..47231a9
--- /dev/null
@@ -0,0 +1,59 @@
+From f056fc2b927448d37eca6b6cacc3d1b0f67b20d2 Mon Sep 17 00:00:00 2001
+From: Junrui Luo <moonafterrain@outlook.com>
+Date: Thu, 2 Apr 2026 14:48:07 +0800
+Subject: wifi: iwlwifi: mld: validate sta_mask before ffs() in BA session handlers
+
+From: Junrui Luo <moonafterrain@outlook.com>
+
+commit f056fc2b927448d37eca6b6cacc3d1b0f67b20d2 upstream.
+
+Three BA session handlers use ffs(ba_data->sta_mask) - 1 to derive a
+station ID without checking that sta_mask is non-zero. When sta_mask is
+zero, ffs() returns 0 and the subtraction wraps to 0xFFFFFFFF, causing
+an out-of-bounds access on fw_id_to_link_sta[].
+
+Add WARN_ON_ONCE(!ba_data->sta_mask) guards before each ffs() call,
+consistent with the existing check in iwl_mld_ampdu_rx_start().
+
+Reported-by: Yuhao Jiang <danisjiang@gmail.com>
+Cc: stable@vger.kernel.org
+Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
+Link: https://patch.msgid.link/SYBPR01MB788115C6CE873271A9A15A25AF51A@SYBPR01MB7881.ausprd01.prod.outlook.com
+Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/intel/iwlwifi/mld/agg.c |    9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c
++++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c
+@@ -64,6 +64,9 @@ static void iwl_mld_release_frames_from_
+       }
+       /* pick any STA ID to find the pointer */
++      if (WARN_ON_ONCE(!ba_data->sta_mask))
++              goto out_unlock;
++
+       sta_id = ffs(ba_data->sta_mask) - 1;
+       link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]);
+       if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta))
+@@ -166,6 +169,9 @@ void iwl_mld_del_ba(struct iwl_mld *mld,
+               goto out_unlock;
+       /* pick any STA ID to find the pointer */
++      if (WARN_ON_ONCE(!ba_data->sta_mask))
++              goto out_unlock;
++
+       sta_id = ffs(ba_data->sta_mask) - 1;
+       link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]);
+       if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta))
+@@ -347,6 +353,9 @@ static void iwl_mld_rx_agg_session_expir
+       }
+       /* timer expired, pick any STA ID to find the pointer */
++      if (WARN_ON_ONCE(!ba_data->sta_mask))
++              goto unlock;
++
+       sta_id = ffs(ba_data->sta_mask) - 1;
+       link_sta = rcu_dereference(ba_data->mld->fw_id_to_link_sta[sta_id]);
diff --git a/queue-7.1/wifi-iwlwifi-mvm-fix-race-condition-in-ptp-removal.patch b/queue-7.1/wifi-iwlwifi-mvm-fix-race-condition-in-ptp-removal.patch
new file mode 100644 (file)
index 0000000..5b918cc
--- /dev/null
@@ -0,0 +1,47 @@
+From 65150c9cc3e06ab54bc4e8134a47f6f5d095a4e3 Mon Sep 17 00:00:00 2001
+From: Junjie Cao <junjie.cao@intel.com>
+Date: Thu, 12 Feb 2026 20:50:34 +0800
+Subject: wifi: iwlwifi: mvm: fix race condition in PTP removal
+
+From: Junjie Cao <junjie.cao@intel.com>
+
+commit 65150c9cc3e06ab54bc4e8134a47f6f5d095a4e3 upstream.
+
+iwl_mvm_ptp_remove() calls cancel_delayed_work_sync() only after
+ptp_clock_unregister() and clearing ptp_data state (ptp_clock,
+ptp_clock_info, last_gp2).
+
+This creates a race where the delayed work iwl_mvm_ptp_work() can
+execute between ptp_clock_unregister() and cancel_delayed_work_sync(),
+observing partially cleared PTP state.
+
+Move cancel_delayed_work_sync() before ptp_clock_unregister() to
+ensure the delayed work is fully stopped before any PTP cleanup
+begins.
+
+Cc: stable@vger.kernel.org
+Reviewed-by: Simon Horman <horms@kernel.org>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: Junjie Cao <junjie.cao@intel.com>
+Link: https://patch.msgid.link/20260212125035.1345718-1-junjie.cao@intel.com
+Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/intel/iwlwifi/mvm/ptp.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
++++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
+@@ -325,11 +325,11 @@ void iwl_mvm_ptp_remove(struct iwl_mvm *
+                              mvm->ptp_data.ptp_clock_info.name,
+                              ptp_clock_index(mvm->ptp_data.ptp_clock));
++              cancel_delayed_work_sync(&mvm->ptp_data.dwork);
+               ptp_clock_unregister(mvm->ptp_data.ptp_clock);
+               mvm->ptp_data.ptp_clock = NULL;
+               memset(&mvm->ptp_data.ptp_clock_info, 0,
+                      sizeof(mvm->ptp_data.ptp_clock_info));
+               mvm->ptp_data.last_gp2 = 0;
+-              cancel_delayed_work_sync(&mvm->ptp_data.dwork);
+       }
+ }
diff --git a/queue-7.1/wifi-mt76-mt76x2u-add-support-for-elecom-wdc-867su3s.patch b/queue-7.1/wifi-mt76-mt76x2u-add-support-for-elecom-wdc-867su3s.patch
new file mode 100644 (file)
index 0000000..9b20946
--- /dev/null
@@ -0,0 +1,34 @@
+From f4ce0664e9f0387873b181777891741c33e19465 Mon Sep 17 00:00:00 2001
+From: Zenm Chen <zenmchen@gmail.com>
+Date: Tue, 7 Apr 2026 23:44:30 +0800
+Subject: wifi: mt76: mt76x2u: Add support for ELECOM WDC-867SU3S
+
+From: Zenm Chen <zenmchen@gmail.com>
+
+commit f4ce0664e9f0387873b181777891741c33e19465 upstream.
+
+Add the ID 056e:400a to the table to support an additional MT7612U
+adapter: ELECOM WDC-867SU3S.
+
+Compile tested only.
+
+Cc: stable@vger.kernel.org # 5.10.x
+Signed-off-by: Zenm Chen <zenmchen@gmail.com>
+Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://patch.msgid.link/20260407154430.9184-1-zenmchen@gmail.com
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/mediatek/mt76/mt76x2/usb.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
++++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
+@@ -16,6 +16,7 @@ static const struct usb_device_id mt76x2
+       { USB_DEVICE(0x0e8d, 0x7612) }, /* Aukey USBAC1200 - Alfa AWUS036ACM */
+       { USB_DEVICE(0x057c, 0x8503) }, /* Avm FRITZ!WLAN AC860 */
+       { USB_DEVICE(0x7392, 0xb711) }, /* Edimax EW 7722 UAC */
++      { USB_DEVICE(0x056e, 0x400a) }, /* ELECOM WDC-867SU3S */
+       { USB_DEVICE(0x0e8d, 0x7632) }, /* HC-M7662BU1 */
+       { USB_DEVICE(0x0471, 0x2126) }, /* LiteOn WN4516R module, nonstandard USB connector */
+       { USB_DEVICE(0x0471, 0x7600) }, /* LiteOn WN4519R module, nonstandard USB connector */
diff --git a/queue-7.1/wifi-mt76-mt7925-don-t-disable-ap-bss-when-removing-tdls-peer.patch b/queue-7.1/wifi-mt76-mt7925-don-t-disable-ap-bss-when-removing-tdls-peer.patch
new file mode 100644 (file)
index 0000000..2cb5741
--- /dev/null
@@ -0,0 +1,69 @@
+From 37d65384aa6f9cbe45f4052b13b378af1aab3e95 Mon Sep 17 00:00:00 2001
+From: ElXreno <elxreno@gmail.com>
+Date: Wed, 6 May 2026 04:39:16 +0300
+Subject: wifi: mt76: mt7925: don't disable AP BSS when removing TDLS peer
+
+From: ElXreno <elxreno@gmail.com>
+
+commit 37d65384aa6f9cbe45f4052b13b378af1aab3e95 upstream.
+
+On a STATION vif, removing a TDLS peer takes the mt7925_mac_sta_remove
+-> mt7925_mac_sta_remove_links path. The first loop in that function
+calls mt7925_mcu_add_bss_info(..., enable=false) for every link of the
+station being removed. For a non-MLO STATION vif there is exactly one
+link, link 0, whose bss_conf is the AP's. TDLS peers do not have their
+own bss_conf - they share the AP's BSS.
+
+The result is that every TDLS peer teardown sends a BSS_INFO_UPDATE
+with enable=0 for the AP's BSS to the firmware, which wipes the AP-side
+rate-control context. The connection stays associated and TX from the
+host still works at the negotiated rate, but the AP's downlink to us
+collapses to the lowest mandatory OFDM rate (HE-MCS 0 / 6 Mbit/s OFDM)
+and only slowly recovers as rate adaptation re-learns under sustained
+traffic. With brief or bursty traffic the link can stay at 6-72 Mbit/s
+indefinitely, requiring a manual reconnect.
+
+mt7925_mac_link_sta_remove() already guards its own
+mt7925_mcu_add_bss_info(..., false) call with
+"vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls".
+Add the equivalent guard at the top of the cleanup loop in
+mt7925_mac_sta_remove_links(), above the link_sta / link_conf /
+mlink / mconf lookups, so TDLS peer teardown skips the loop body
+entirely without doing the per-link work that would just be thrown
+away.
+
+Verified on mt7925e by triggering Samsung-S938B auto-TDLS via iperf3
+and watching iw rx bitrate after teardown:
+
+  Before: rx bitrate collapses to 6.0-72.0 Mbit/s, oscillates 17/72/
+          137/288/432 Mbit/s for 30+ seconds, no full recovery without
+          a manual reassoc.
+  After:  rx bitrate stays at 1200.9 Mbit/s HE-MCS 11 NSS 2 80 MHz
+          across the entire TDLS lifecycle.
+
+bpftrace confirms a single mt7925_mcu_add_bss_info(enable=0) call per
+teardown before the fix; zero such calls after.
+
+Fixes: 3878b4333602 ("wifi: mt76: mt7925: update mt7925_mac_link_sta_[add, assoc, remove] for MLO")
+Cc: stable@vger.kernel.org
+Signed-off-by: ElXreno <elxreno@gmail.com>
+Assisted-by: Claude:claude-opus-4-7 bpftrace
+Link: https://patch.msgid.link/20260506-mt7925-tdls-fixes-v2-2-46aa826ba8bb@gmail.com
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/mediatek/mt76/mt7925/main.c |    3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+@@ -1265,6 +1265,9 @@ mt7925_mac_sta_remove_links(struct mt792
+               if (vif->type == NL80211_IFTYPE_AP)
+                       break;
++              if (vif->type == NL80211_IFTYPE_STATION && sta->tdls)
++                      continue;
++
+               link_sta = mt792x_sta_to_link_sta(vif, sta, link_id);
+               if (!link_sta)
+                       continue;
diff --git a/queue-7.1/wifi-rtl8xxxu-detect-the-maximum-supported-channel-width.patch b/queue-7.1/wifi-rtl8xxxu-detect-the-maximum-supported-channel-width.patch
new file mode 100644 (file)
index 0000000..1b6ddf4
--- /dev/null
@@ -0,0 +1,266 @@
+From ef771eabc79d5f21b63689cca0e0fa5493fa0a8a Mon Sep 17 00:00:00 2001
+From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
+Date: Wed, 29 Apr 2026 15:02:48 +0300
+Subject: wifi: rtl8xxxu: Detect the maximum supported channel width
+
+From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
+
+commit ef771eabc79d5f21b63689cca0e0fa5493fa0a8a upstream.
+
+Some devices malfunction when connected to a network with 40 MHz channel
+width, because they don't support that.
+
+RTL8188FU, RTL8192FU, and RTL8710BU (RTL8188GU) have a way to signal
+this (and some other capabilities) to the driver. Get this information
+from the hardware and advertise 40 MHz support only when the hardware
+can handle it. We assume the other chips can always handle it.
+
+RTL8710BU needs a different way to retrieve this information, which will
+be implemented some other time.
+
+Fixes: dbf9b7bb0edf ("wifi: rtl8xxxu: Enable 40 MHz width by default")
+Cc: stable@vger.kernel.org
+Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221394
+Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
+Reviewed-by: Ping-Ke Shih <pkshih@realtek.com>
+Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
+Link: https://patch.msgid.link/c57de68e-5d57-4c26-898f-8a284bb25381@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/realtek/rtl8xxxu/8188e.c    |    1 
+ drivers/net/wireless/realtek/rtl8xxxu/8188f.c    |    1 
+ drivers/net/wireless/realtek/rtl8xxxu/8192c.c    |    1 
+ drivers/net/wireless/realtek/rtl8xxxu/8192e.c    |    1 
+ drivers/net/wireless/realtek/rtl8xxxu/8192f.c    |    1 
+ drivers/net/wireless/realtek/rtl8xxxu/8710b.c    |    1 
+ drivers/net/wireless/realtek/rtl8xxxu/8723a.c    |    1 
+ drivers/net/wireless/realtek/rtl8xxxu/8723b.c    |    1 
+ drivers/net/wireless/realtek/rtl8xxxu/core.c     |   64 +++++++++++++++++++++--
+ drivers/net/wireless/realtek/rtl8xxxu/regs.h     |    2 
+ drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h |    7 ++
+ 11 files changed, 76 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
++++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
+@@ -1866,6 +1866,7 @@ struct rtl8xxxu_fileops rtl8188eu_fops =
+       .has_tx_report = 1,
+       .init_reg_pkt_life_time = 1,
+       .gen2_thermal_meter = 1,
++      .hw_feature_report = 0,
+       .max_sec_cam_num = 32,
+       .adda_1t_init = 0x0b1b25a0,
+       .adda_1t_path_on = 0x0bdb25a0,
+--- a/drivers/net/wireless/realtek/rtl8xxxu/8188f.c
++++ b/drivers/net/wireless/realtek/rtl8xxxu/8188f.c
+@@ -1745,6 +1745,7 @@ struct rtl8xxxu_fileops rtl8188fu_fops =
+       .init_reg_rxfltmap = 1,
+       .init_reg_pkt_life_time = 1,
+       .init_reg_hmtfr = 1,
++      .hw_feature_report = 1,
+       .ampdu_max_time = 0x70,
+       .ustime_tsf_edca = 0x28,
+       .max_aggr_num = 0x0c14,
+--- a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c
++++ b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c
+@@ -723,6 +723,7 @@ struct rtl8xxxu_fileops rtl8192cu_fops =
+       .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32),
+       .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16),
+       .supports_ap = 1,
++      .hw_feature_report = 0,
+       .max_macid_num = 32,
+       .max_sec_cam_num = 32,
+       .adda_1t_init = 0x0b1b25a0,
+--- a/drivers/net/wireless/realtek/rtl8xxxu/8192e.c
++++ b/drivers/net/wireless/realtek/rtl8xxxu/8192e.c
+@@ -1751,6 +1751,7 @@ struct rtl8xxxu_fileops rtl8192eu_fops =
+       .has_s0s1 = 0,
+       .gen2_thermal_meter = 1,
+       .needs_full_init = 1,
++      .hw_feature_report = 0,
+       .supports_ap = 1,
+       .max_macid_num = 128,
+       .max_sec_cam_num = 64,
+--- a/drivers/net/wireless/realtek/rtl8xxxu/8192f.c
++++ b/drivers/net/wireless/realtek/rtl8xxxu/8192f.c
+@@ -2074,6 +2074,7 @@ struct rtl8xxxu_fileops rtl8192fu_fops =
+       .init_reg_rxfltmap = 1,
+       .init_reg_pkt_life_time = 1,
+       .init_reg_hmtfr = 1,
++      .hw_feature_report = 1,
+       .ampdu_max_time = 0x5e,
+       .ustime_tsf_edca = 0x50,
+       .max_aggr_num = 0x1f1f,
+--- a/drivers/net/wireless/realtek/rtl8xxxu/8710b.c
++++ b/drivers/net/wireless/realtek/rtl8xxxu/8710b.c
+@@ -1852,6 +1852,7 @@ struct rtl8xxxu_fileops rtl8710bu_fops =
+       .init_reg_rxfltmap = 1,
+       .init_reg_pkt_life_time = 1,
+       .init_reg_hmtfr = 1,
++      .hw_feature_report = 0, /* TODO, it's different */
+       .ampdu_max_time = 0x5e,
+       /*
+        * The RTL8710BU vendor driver uses 0x50 here and it works fine,
+--- a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c
++++ b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c
+@@ -632,6 +632,7 @@ struct rtl8xxxu_fileops rtl8723au_fops =
+       .rx_agg_buf_size = 16000,
+       .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32),
+       .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16),
++      .hw_feature_report = 0,
+       .max_sec_cam_num = 32,
+       .adda_1t_init = 0x0b1b25a0,
+       .adda_1t_path_on = 0x0bdb25a0,
+--- a/drivers/net/wireless/realtek/rtl8xxxu/8723b.c
++++ b/drivers/net/wireless/realtek/rtl8xxxu/8723b.c
+@@ -1746,6 +1746,7 @@ struct rtl8xxxu_fileops rtl8723bu_fops =
+       .gen2_thermal_meter = 1,
+       .needs_full_init = 1,
+       .init_reg_hmtfr = 1,
++      .hw_feature_report = 0,
+       .ampdu_max_time = 0x5e,
+       .ustime_tsf_edca = 0x50,
+       .max_aggr_num = 0x0c14,
+--- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
++++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
+@@ -14,6 +14,7 @@
+  */
+ #include <linux/firmware.h>
++#include <linux/iopoll.h>
+ #include "regs.h"
+ #include "rtl8xxxu.h"
+@@ -3915,6 +3916,46 @@ static inline u8 rtl8xxxu_get_macid(stru
+       return sta_info->macid;
+ }
++static void rtl8xxxu_request_hw_feature(struct rtl8xxxu_priv *priv)
++{
++      if (!priv->fops->hw_feature_report)
++              return;
++
++      rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, C2H_HW_FEATURE_DUMP);
++}
++
++static int rtl8xxxu_dump_hw_feature(struct rtl8xxxu_priv *priv)
++{
++      static const u8 bw_map[8] = { 0, 0, 160, 5, 10, 20, 40, 80 };
++      struct rtl8xxxu_hw_feature *hw_feature = &priv->hw_feature;
++      u8 feature[13];
++      int i, ret;
++      u8 id, bw;
++
++      if (!priv->fops->hw_feature_report) {
++              hw_feature->max_bw = 40;
++              return 0;
++      }
++
++      ret = read_poll_timeout(rtl8xxxu_read8, id,
++                              id == C2H_HW_FEATURE_REPORT,
++                              10000, 800000, false,
++                              priv, REG_C2HEVT_MSG_NORMAL);
++      if (ret)
++              return ret;
++
++      for (i = 0; i < ARRAY_SIZE(feature); i++)
++              feature[i] = rtl8xxxu_read8(priv, REG_C2HEVT_MSG_NORMAL + 2 + i);
++
++      rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, 0);
++
++      bw = u8_get_bits(feature[6], GENMASK(2, 0));
++
++      hw_feature->max_bw = bw_map[bw];
++
++      return 0;
++}
++
+ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
+ {
+       struct rtl8xxxu_priv *priv = hw->priv;
+@@ -3961,6 +4002,8 @@ static int rtl8xxxu_init_device(struct i
+        */
+       rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, fops->trxff_boundary);
++      rtl8xxxu_request_hw_feature(priv);
++
+       for (int retry = 5; retry >= 0 ; retry--) {
+               ret = rtl8xxxu_download_firmware(priv);
+               dev_dbg(dev, "%s: download_firmware %i\n", __func__, ret);
+@@ -3976,6 +4019,12 @@ static int rtl8xxxu_init_device(struct i
+       if (ret)
+               goto exit;
++      ret = rtl8xxxu_dump_hw_feature(priv);
++      if (ret) {
++              dev_err(dev, "failed to dump hw feature\n");
++              goto exit;
++      }
++
+       if (fops->phy_init_antenna_selection)
+               fops->phy_init_antenna_selection(priv);
+@@ -7835,15 +7884,20 @@ static int rtl8xxxu_probe(struct usb_int
+       sband->ht_cap.ht_supported = true;
+       sband->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+       sband->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
+-      sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
+-                          IEEE80211_HT_CAP_SUP_WIDTH_20_40;
++      sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20;
++
++      if (priv->hw_feature.max_bw >= 40) {
++              sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
++              sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
++      } else {
++              dev_info(&udev->dev, "hardware doesn't support HT40\n");
++      }
++
+       memset(&sband->ht_cap.mcs, 0, sizeof(sband->ht_cap.mcs));
+       sband->ht_cap.mcs.rx_mask[0] = 0xff;
+       sband->ht_cap.mcs.rx_mask[4] = 0x01;
+-      if (priv->rf_paths > 1) {
++      if (priv->rf_paths > 1)
+               sband->ht_cap.mcs.rx_mask[1] = 0xff;
+-              sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+-      }
+       sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+       hw->wiphy->bands[NL80211_BAND_2GHZ] = sband;
+--- a/drivers/net/wireless/realtek/rtl8xxxu/regs.h
++++ b/drivers/net/wireless/realtek/rtl8xxxu/regs.h
+@@ -447,6 +447,8 @@
+ /* 8188EU */
+ #define REG_32K_CTRL                  0x0194
+ #define REG_C2HEVT_MSG_NORMAL         0x01a0
++#define C2H_HW_FEATURE_REPORT         0x19
++#define C2H_HW_FEATURE_DUMP           0xfd
+ /* 8192EU/8723BU/8812 */
+ #define REG_C2HEVT_CMD_ID_8723B               0x01ae
+ #define REG_C2HEVT_CLEAR              0x01af
+--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
++++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+@@ -1789,11 +1789,17 @@ struct rtl8xxxu_cfo_tracking {
+ #define RTL8XXXU_BC_MC_MACID1 1
+ #define RTL8XXXU_MAX_SEC_CAM_NUM      64
++struct rtl8xxxu_hw_feature {
++      u8 max_bw;
++};
++
+ struct rtl8xxxu_priv {
+       struct ieee80211_hw *hw;
+       struct usb_device *udev;
+       struct rtl8xxxu_fileops *fops;
++      struct rtl8xxxu_hw_feature hw_feature;
++
+       spinlock_t tx_urb_lock;
+       struct list_head tx_urb_free_list;
+       int tx_urb_free_count;
+@@ -2009,6 +2015,7 @@ struct rtl8xxxu_fileops {
+       u8 init_reg_pkt_life_time:1;
+       u8 init_reg_hmtfr:1;
+       u8 supports_concurrent:1;
++      u8 hw_feature_report:1;
+       u8 ampdu_max_time;
+       u8 ustime_tsf_edca;
+       u16 max_aggr_num;
diff --git a/queue-7.1/wifi-rtlwifi-rtl8821ae-fix-c2h-bit-location-in-rx-descriptor.patch b/queue-7.1/wifi-rtlwifi-rtl8821ae-fix-c2h-bit-location-in-rx-descriptor.patch
new file mode 100644 (file)
index 0000000..1f59c9a
--- /dev/null
@@ -0,0 +1,39 @@
+From 83d38df6929118c3f996b9e3351c2d5014073d87 Mon Sep 17 00:00:00 2001
+From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
+Date: Sat, 25 Apr 2026 22:32:58 +0300
+Subject: wifi: rtlwifi: rtl8821ae: Fix C2H bit location in RX descriptor
+
+From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
+
+commit 83d38df6929118c3f996b9e3351c2d5014073d87 upstream.
+
+Bit 28 of double word 2 in the RX descriptor indicates if the packet is
+a normal 802.11 frame, or a message from the wifi firmware to the
+driver (Card 2 Host).
+
+Commit f5678bfe1cdc ("rtlwifi: rtl8821ae: Replace local bit manipulation
+macros") mistakenly made the driver look for this bit in double word 1,
+causing packet loss and Bluetooth coexistence problems.
+
+Fixes: f5678bfe1cdc ("rtlwifi: rtl8821ae: Replace local bit manipulation macros")
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
+Acked-by: Ping-Ke Shih <pkshih@realtek.com>
+Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
+Link: https://patch.msgid.link/04da7398-cedb-425a-a810-5772ab10139d@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h
++++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h
+@@ -291,7 +291,7 @@ static inline int get_rx_desc_paggr(__le
+ static inline int get_rx_status_desc_rpt_sel(__le32 *__pdesc)
+ {
+-      return le32_get_bits(*(__pdesc + 1), BIT(28));
++      return le32_get_bits(*(__pdesc + 2), BIT(28));
+ }
+ static inline int get_rx_desc_rxmcs(__le32 *__pdesc)
diff --git a/queue-7.1/wifi-rtw88-increase-tx-report-timeout-to-fix-race-condition.patch b/queue-7.1/wifi-rtw88-increase-tx-report-timeout-to-fix-race-condition.patch
new file mode 100644 (file)
index 0000000..8045054
--- /dev/null
@@ -0,0 +1,62 @@
+From c80788f7c5aed8d420366b821f867a8a353d83a5 Mon Sep 17 00:00:00 2001
+From: Luka Gejak <luka.gejak@linux.dev>
+Date: Mon, 18 May 2026 16:23:10 +0200
+Subject: wifi: rtw88: increase TX report timeout to fix race condition
+
+From: Luka Gejak <luka.gejak@linux.dev>
+
+commit c80788f7c5aed8d420366b821f867a8a353d83a5 upstream.
+
+The driver expects the firmware to report TX status within 500ms.
+However, a timeout can be triggered when the hardware performs
+background scans while under TX load. During these scans, the firmware
+stays off-channel for periods exceeding 500ms, delaying the delivery of
+TX reports back to the driver.
+
+When this occurs, the purge timer fires prematurely and drops the
+tracking skbs from the queue. This results in the host stack
+interpreting the missing status as packet loss, leading to TCP window
+collapse. In testing with iperf3, this causes throughput to drop from
+~90 Mbps to near-zero for approximately 2 seconds until the connection
+recovers.
+
+Increase RTW_TX_PROBE_TIMEOUT to 2500ms for RTL8723DU. This duration is
+sufficient to accommodate off-channel dwell time during full background
+scans, ensuring the purge timer only trips during genuine firmware
+lockups and preventing unnecessary TCP retransmission cycles.
+
+Fixes: a82dfd33d123 ("wifi: rtw88: Add common USB chip support")
+Cc: stable@vger.kernel.org
+Acked-by: Ping-Ke Shih <pkshih@realtek.com>
+Tested-by: Luka Gejak <luka.gejak@linux.dev>
+Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
+Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
+Link: https://patch.msgid.link/20260518142311.10328-1-luka.gejak@linux.dev
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/realtek/rtw88/tx.c |    7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/wireless/realtek/rtw88/tx.c
++++ b/drivers/net/wireless/realtek/rtw88/tx.c
+@@ -196,6 +196,7 @@ void rtw_tx_report_purge_timer(struct ti
+ void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn)
+ {
+       struct rtw_tx_report *tx_report = &rtwdev->tx_report;
++      unsigned long timeout = RTW_TX_PROBE_TIMEOUT;
+       unsigned long flags;
+       u8 *drv_data;
+@@ -207,7 +208,11 @@ void rtw_tx_report_enqueue(struct rtw_de
+       __skb_queue_tail(&tx_report->queue, skb);
+       spin_unlock_irqrestore(&tx_report->q_lock, flags);
+-      mod_timer(&tx_report->purge_timer, jiffies + RTW_TX_PROBE_TIMEOUT);
++      if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D &&
++          rtwdev->hci.type == RTW_HCI_TYPE_USB)
++              timeout = msecs_to_jiffies(2500);
++
++      mod_timer(&tx_report->purge_timer, jiffies + timeout);
+ }
+ EXPORT_SYMBOL(rtw_tx_report_enqueue);
diff --git a/queue-7.1/wifi-rtw88-usb-fix-memory-leaks-on-usb-write-failures.patch b/queue-7.1/wifi-rtw88-usb-fix-memory-leaks-on-usb-write-failures.patch
new file mode 100644 (file)
index 0000000..63b2fda
--- /dev/null
@@ -0,0 +1,79 @@
+From 6b964941bbfe6e0f18b1a5e008486dbb62df440a Mon Sep 17 00:00:00 2001
+From: Luka Gejak <luka.gejak@linux.dev>
+Date: Mon, 18 May 2026 16:23:11 +0200
+Subject: wifi: rtw88: usb: fix memory leaks on USB write failures
+
+From: Luka Gejak <luka.gejak@linux.dev>
+
+commit 6b964941bbfe6e0f18b1a5e008486dbb62df440a upstream.
+
+When rtw_usb_write_port() fails to submit a USB Request Block (URB)
+(e.g., due to device disconnect or ENOMEM), the completion callback is
+never executed.
+
+Currently, the driver ignores the return value of rtw_usb_write_port()
+in rtw_usb_write_data() and rtw_usb_tx_agg_skb(). Because these
+functions rely on the completion callback to free the socket buffers
+(skbs) and the transaction control block (txcb), a submission failure
+results in:
+1. A memory leak of the allocated skb in rtw_usb_write_data().
+2. A memory leak of the txcb structure and all aggregated skbs in
+   rtw_usb_tx_agg_skb().
+
+Fix this by checking the return value of rtw_usb_write_port(). If it
+fails, explicitly free the skb in rtw_usb_write_data(), and properly
+purge the tx_ack_queue and free the txcb in rtw_usb_tx_agg_skb().
+
+The issue was discovered in practice during device disconnect/reconnect
+scenarios and memory pressure conditions. Tested by verifying normal TX
+operation continues after the fix without regressions.
+
+Fixes: a82dfd33d123 ("wifi: rtw88: Add common USB chip support")
+Cc: stable@vger.kernel.org
+Acked-by: Ping-Ke Shih <pkshih@realtek.com>
+Tested-by: Luka Gejak <luka.gejak@linux.dev>
+Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
+Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
+Link: https://patch.msgid.link/20260518142311.10328-2-luka.gejak@linux.dev
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/realtek/rtw88/usb.c |   13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/wireless/realtek/rtw88/usb.c
++++ b/drivers/net/wireless/realtek/rtw88/usb.c
+@@ -399,6 +399,7 @@ static bool rtw_usb_tx_agg_skb(struct rt
+       int agg_num = 0;
+       unsigned int align_next = 0;
+       u8 qsel;
++      int ret;
+       if (skb_queue_empty(list))
+               return false;
+@@ -456,7 +457,13 @@ queue:
+       tx_desc = (struct rtw_tx_desc *)skb_head->data;
+       qsel = le32_get_bits(tx_desc->w1, RTW_TX_DESC_W1_QSEL);
+-      rtw_usb_write_port(rtwdev, qsel, skb_head, rtw_usb_write_port_tx_complete, txcb);
++      ret = rtw_usb_write_port(rtwdev, qsel, skb_head,
++                               rtw_usb_write_port_tx_complete, txcb);
++      if (ret) {
++              ieee80211_purge_tx_queue(rtwdev->hw, &txcb->tx_ack_queue);
++              kfree(txcb);
++              return false;
++      }
+       return true;
+ }
+@@ -518,8 +525,10 @@ static int rtw_usb_write_data(struct rtw
+       ret = rtw_usb_write_port(rtwdev, qsel, skb,
+                                rtw_usb_write_port_complete, skb);
+-      if (unlikely(ret))
++      if (unlikely(ret)) {
+               rtw_err(rtwdev, "failed to do USB write, ret=%d\n", ret);
++              dev_kfree_skb_any(skb);
++      }
+       return ret;
+ }