From: Greg Kroah-Hartman Date: Thu, 2 Jul 2026 13:03:55 +0000 (+0200) Subject: 7.1-stable patches X-Git-Tag: v5.10.260~41 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8ed04fc39459a2310aa57e80cbeccc593d491903;p=thirdparty%2Fkernel%2Fstable-queue.git 7.1-stable patches 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 --- 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 index 0000000000..40fa3614fd --- /dev/null +++ b/queue-7.1/block-invalidate-cached-plug-timestamp-after-task-switch.patch @@ -0,0 +1,109 @@ +From fad156c2af227f42ca796cbb20ddc354a6dd9932 Mon Sep 17 00:00:00 2001 +From: Usama Arif +Date: Tue, 16 Jun 2026 07:15:18 -0700 +Subject: block: invalidate cached plug timestamp after task switch + +From: Usama Arif + +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 +Link: https://patch.msgid.link/20260616141604.328820-3-usama.arif@linux.dev +Signed-off-by: Jens Axboe +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..6f0e3b0b8a --- /dev/null +++ b/queue-7.1/err.h-use-__always_inline-on-all-error-pointer-helpers.patch @@ -0,0 +1,96 @@ +From 94bfc7f3b0c7c33331ba4ff6cc64ff309dfcbce8 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Tue, 26 May 2026 12:18:41 +0200 +Subject: err.h: use __always_inline on all error pointer helpers + +From: Arnd Bergmann + +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 +Reviewed-by: Alexander Lobakin +Reviewed-by: Nathan Chancellor +Tested-by: Tamir Duberstein +Cc: Alexander Gordeev +Cc: Andriy Shevchenko +Cc: Ansuel Smith +Cc: Bjorn Andersson +Cc: Heiko Carstens +Cc: Vasily Gorbik +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..83eacd1983 --- /dev/null +++ b/queue-7.1/fbdev-fix-use-after-free-in-store_modes.patch @@ -0,0 +1,73 @@ +From 2c1c805c65fb7dc7524e20376d6987721e73a0b1 Mon Sep 17 00:00:00 2001 +From: Ian Bridges +Date: Thu, 25 Jun 2026 23:50:48 -0500 +Subject: fbdev: fix use-after-free in store_modes() + +From: Ian Bridges + +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 +Signed-off-by: Helge Deller +Signed-off-by: Greg Kroah-Hartman +--- + 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 + + #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 index 0000000000..22f53f76d0 --- /dev/null +++ b/queue-7.1/fscrypt-fix-key-setup-in-edge-case-with-multiple-data-unit-sizes.patch @@ -0,0 +1,420 @@ +From dd015b566d505d698386103e9c80b739c7336eb8 Mon Sep 17 00:00:00 2001 +From: Eric Biggers +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 + +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 +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..4206ac78b9 --- /dev/null +++ b/queue-7.1/gcov-use-atomic-counter-updates-to-fix-concurrent-access-crashes.patch @@ -0,0 +1,125 @@ +From 56cb9b7d96b28a1173a510ab25354b6599ad3a33 Mon Sep 17 00:00:00 2001 +From: Konstantin Khorenko +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 + +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 +Tested-by: Arnd Bergmann +Tested-by: Peter Oberparleiter +Reviewed-by: Peter Oberparleiter +Cc: Masahiro Yamada +Cc: Miguel Ojeda +Cc: Mikhail Zaslonko +Cc: Nathan Chancellor +Cc: Pavel Tikhomirov +Cc: Thomas Weißschuh +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..5236b820bb --- /dev/null +++ b/queue-7.1/keys-fix-overflow-in-keyctl_pkey_params_get_2.patch @@ -0,0 +1,65 @@ +From cb481e59ea6cae3b7796ac1d7a22b6b24c3f3c0b Mon Sep 17 00:00:00 2001 +From: Jarkko Sakkinen +Date: Mon, 1 Jun 2026 23:11:54 +0300 +Subject: KEYS: fix overflow in keyctl_pkey_params_get_2() + +From: Jarkko Sakkinen + +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 +Tested-by: Alessandro Groppo +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..5dadcd2da7 --- /dev/null +++ b/queue-7.1/keys-pin-request_key_auth-payload-in-instantiate-paths.patch @@ -0,0 +1,215 @@ +From fd15b457a86939c38aa12116adabd8ff686c5e51 Mon Sep 17 00:00:00 2001 +From: Shaomin Chen +Date: Wed, 10 Jun 2026 13:10:05 +0300 +Subject: keys: Pin request_key_auth payload in instantiate paths + +From: Shaomin Chen + +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 +Closes: https://lore.kernel.org/r/20260519144403.436694-1-eeesssooo020@gmail.com +Signed-off-by: Shaomin Chen +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Greg Kroah-Hartman +--- + 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 ++#include + + /* + * 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 index 0000000000..9fd9efee32 --- /dev/null +++ b/queue-7.1/kvm-arm64-omit-tag-sync-on-stage-2-mappings-of-the-zero-page.patch @@ -0,0 +1,44 @@ +From 2986a625740599fe6e7635b0586fed2a95bcd1f7 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel +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 + +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 +Signed-off-by: Ard Biesheuvel +Reviewed-by: Catalin Marinas +Signed-off-by: Will Deacon +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..cf6df664f9 --- /dev/null +++ b/queue-7.1/ntb-epf-avoid-pci_iounmap-with-offset-when-peer_spad-and-config-share-bar.patch @@ -0,0 +1,56 @@ +From d876153680e3d721d385e554def919bce3d18c74 Mon Sep 17 00:00:00 2001 +From: Koichiro Den +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 + +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 +Signed-off-by: Koichiro Den +Reviewed-by: Dave Jiang +Signed-off-by: Jon Mason +Signed-off-by: Greg Kroah-Hartman +--- + 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); diff --git a/queue-7.1/series b/queue-7.1/series index d2928bab6c..11eda06db6 100644 --- a/queue-7.1/series +++ b/queue-7.1/series @@ -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 index 0000000000..547e907ad1 --- /dev/null +++ b/queue-7.1/userfaultfd-build-__vma_uffd_flags-from-config-gated-masks.patch @@ -0,0 +1,125 @@ +From cc7a9f6e57c4f71e8e1fee3274b1ae8770f2a743 Mon Sep 17 00:00:00 2001 +From: "Kiryl Shutsemau (Meta)" +Date: Fri, 29 May 2026 18:23:30 +0100 +Subject: userfaultfd: build __VMA_UFFD_FLAGS from config-gated masks + +From: Kiryl Shutsemau (Meta) + +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 +Reported-by: Sashiko AI review +Suggested-by: Lorenzo Stoakes +Reviewed-by: Lorenzo Stoakes +Assisted-by: Claude:claude-opus-4-8 +Cc: David Hildenbrand +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Peter Xu +Cc: Suren Baghdasaryan +Cc: Vlastimil Babka +Cc: Balbir Singh +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..baa7907c1a --- /dev/null +++ b/queue-7.1/userfaultfd-ensure-mremap_userfaultfd_fail-releases-mmap_changing.patch @@ -0,0 +1,48 @@ +From 0496a59745b0723ea74274db16fd5c8b1379b9a9 Mon Sep 17 00:00:00 2001 +From: "Mike Rapoport (Microsoft)" +Date: Wed, 13 May 2026 11:14:16 +0300 +Subject: userfaultfd: ensure mremap_userfaultfd_fail() releases mmap_changing + +From: Mike Rapoport (Microsoft) + +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) +Reviewed-by: David Hildenbrand (Arm) +Cc: Al Viro +Cc: Christian Brauner +Cc: Jan Kara +Cc: Peter Xu +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..2d2d42569b --- /dev/null +++ b/queue-7.1/wifi-ath11k-fix-warning-when-unbinding.patch @@ -0,0 +1,49 @@ +From 8b7a26b6681922a38cd5a7829ace61f8e54df9b7 Mon Sep 17 00:00:00 2001 +From: Jose Ignacio Tornos Martinez +Date: Mon, 20 Apr 2026 13:01:29 +0200 +Subject: wifi: ath11k: fix warning when unbinding + +From: Jose Ignacio Tornos Martinez + +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 +Reviewed-by: Baochen Qiang +Reviewed-by: Rameshkumar Sundaram +Link: https://patch.msgid.link/20260420110130.509670-1-jtornosm@redhat.com +Signed-off-by: Jeff Johnson +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..afd2a1a43b --- /dev/null +++ b/queue-7.1/wifi-iwlwifi-mld-fix-race-condition-in-ptp-removal.patch @@ -0,0 +1,46 @@ +From e1fc08598aa34b28359831e768076f56632720c1 Mon Sep 17 00:00:00 2001 +From: Junjie Cao +Date: Thu, 12 Feb 2026 20:50:35 +0800 +Subject: wifi: iwlwifi: mld: fix race condition in PTP removal + +From: Junjie Cao + +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 +Reviewed-by: Vadim Fedorenko +Signed-off-by: Junjie Cao +Link: https://patch.msgid.link/20260212125035.1345718-2-junjie.cao@intel.com +Signed-off-by: Miri Korenblit +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..47231a90db --- /dev/null +++ b/queue-7.1/wifi-iwlwifi-mld-validate-sta_mask-before-ffs-in-ba-session-handlers.patch @@ -0,0 +1,59 @@ +From f056fc2b927448d37eca6b6cacc3d1b0f67b20d2 Mon Sep 17 00:00:00 2001 +From: Junrui Luo +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 + +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 +Cc: stable@vger.kernel.org +Signed-off-by: Junrui Luo +Link: https://patch.msgid.link/SYBPR01MB788115C6CE873271A9A15A25AF51A@SYBPR01MB7881.ausprd01.prod.outlook.com +Signed-off-by: Miri Korenblit +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..5b918cc16a --- /dev/null +++ b/queue-7.1/wifi-iwlwifi-mvm-fix-race-condition-in-ptp-removal.patch @@ -0,0 +1,47 @@ +From 65150c9cc3e06ab54bc4e8134a47f6f5d095a4e3 Mon Sep 17 00:00:00 2001 +From: Junjie Cao +Date: Thu, 12 Feb 2026 20:50:34 +0800 +Subject: wifi: iwlwifi: mvm: fix race condition in PTP removal + +From: Junjie Cao + +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 +Reviewed-by: Vadim Fedorenko +Signed-off-by: Junjie Cao +Link: https://patch.msgid.link/20260212125035.1345718-1-junjie.cao@intel.com +Signed-off-by: Miri Korenblit +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..9b20946419 --- /dev/null +++ b/queue-7.1/wifi-mt76-mt76x2u-add-support-for-elecom-wdc-867su3s.patch @@ -0,0 +1,34 @@ +From f4ce0664e9f0387873b181777891741c33e19465 Mon Sep 17 00:00:00 2001 +From: Zenm Chen +Date: Tue, 7 Apr 2026 23:44:30 +0800 +Subject: wifi: mt76: mt76x2u: Add support for ELECOM WDC-867SU3S + +From: Zenm Chen + +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 +Acked-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260407154430.9184-1-zenmchen@gmail.com +Signed-off-by: Felix Fietkau +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..2cb5741303 --- /dev/null +++ b/queue-7.1/wifi-mt76-mt7925-don-t-disable-ap-bss-when-removing-tdls-peer.patch @@ -0,0 +1,69 @@ +From 37d65384aa6f9cbe45f4052b13b378af1aab3e95 Mon Sep 17 00:00:00 2001 +From: ElXreno +Date: Wed, 6 May 2026 04:39:16 +0300 +Subject: wifi: mt76: mt7925: don't disable AP BSS when removing TDLS peer + +From: ElXreno + +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 +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 +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..1b6ddf410f --- /dev/null +++ b/queue-7.1/wifi-rtl8xxxu-detect-the-maximum-supported-channel-width.patch @@ -0,0 +1,266 @@ +From ef771eabc79d5f21b63689cca0e0fa5493fa0a8a Mon Sep 17 00:00:00 2001 +From: Bitterblue Smith +Date: Wed, 29 Apr 2026 15:02:48 +0300 +Subject: wifi: rtl8xxxu: Detect the maximum supported channel width + +From: Bitterblue Smith + +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 +Reviewed-by: Ping-Ke Shih +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/c57de68e-5d57-4c26-898f-8a284bb25381@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 ++#include + #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 index 0000000000..1f59c9a2d0 --- /dev/null +++ b/queue-7.1/wifi-rtlwifi-rtl8821ae-fix-c2h-bit-location-in-rx-descriptor.patch @@ -0,0 +1,39 @@ +From 83d38df6929118c3f996b9e3351c2d5014073d87 Mon Sep 17 00:00:00 2001 +From: Bitterblue Smith +Date: Sat, 25 Apr 2026 22:32:58 +0300 +Subject: wifi: rtlwifi: rtl8821ae: Fix C2H bit location in RX descriptor + +From: Bitterblue Smith + +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: +Signed-off-by: Bitterblue Smith +Acked-by: Ping-Ke Shih +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/04da7398-cedb-425a-a810-5772ab10139d@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..8045054fbb --- /dev/null +++ b/queue-7.1/wifi-rtw88-increase-tx-report-timeout-to-fix-race-condition.patch @@ -0,0 +1,62 @@ +From c80788f7c5aed8d420366b821f867a8a353d83a5 Mon Sep 17 00:00:00 2001 +From: Luka Gejak +Date: Mon, 18 May 2026 16:23:10 +0200 +Subject: wifi: rtw88: increase TX report timeout to fix race condition + +From: Luka Gejak + +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 +Tested-by: Luka Gejak +Signed-off-by: Luka Gejak +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/20260518142311.10328-1-luka.gejak@linux.dev +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..63b2fda990 --- /dev/null +++ b/queue-7.1/wifi-rtw88-usb-fix-memory-leaks-on-usb-write-failures.patch @@ -0,0 +1,79 @@ +From 6b964941bbfe6e0f18b1a5e008486dbb62df440a Mon Sep 17 00:00:00 2001 +From: Luka Gejak +Date: Mon, 18 May 2026 16:23:11 +0200 +Subject: wifi: rtw88: usb: fix memory leaks on USB write failures + +From: Luka Gejak + +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 +Tested-by: Luka Gejak +Signed-off-by: Luka Gejak +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/20260518142311.10328-2-luka.gejak@linux.dev +Signed-off-by: Greg Kroah-Hartman +--- + 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; + }