From 730aa0dcef26cc7a955dfaa1b8c2f082b0418a0f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 3 Dec 2024 11:55:00 +0100 Subject: [PATCH] 6.11-stable patches added patches: smb-client-handle-max-length-for-smb-symlinks.patch smb-don-t-leak-cfid-when-reconnect-races-with-open_cached_dir.patch smb-during-unmount-ensure-all-cached-dir-instances-drop-their-dentry.patch smb-prevent-use-after-free-due-to-open_cached_dir-error-paths.patch smb3-request-handle-caching-when-caching-directories.patch usb-dwc3-ep0-don-t-clear-ep0-dwc3_ep_transfer_started.patch usb-dwc3-gadget-fix-checking-for-number-of-trbs-left.patch usb-dwc3-gadget-fix-looping-of-queued-sg-entries.patch usb-misc-ljca-move-usb_autopm_put_interface-after-wait-for-response.patch usb-misc-ljca-set-small-runtime-autosuspend-delay.patch usb-musb-fix-hardware-lockup-on-first-rx-endpoint-request.patch --- queue-6.11/series | 11 + ...t-handle-max-length-for-smb-symlinks.patch | 64 +++ ...reconnect-races-with-open_cached_dir.patch | 226 +++++++++ ...ched-dir-instances-drop-their-dentry.patch | 449 ++++++++++++++++++ ...e-due-to-open_cached_dir-error-paths.patch | 246 ++++++++++ ...dle-caching-when-caching-directories.patch | 33 ++ ...t-clear-ep0-dwc3_ep_transfer_started.patch | 35 ++ ...fix-checking-for-number-of-trbs-left.patch | 53 +++ ...get-fix-looping-of-queued-sg-entries.patch | 49 ++ ...ut_interface-after-wait-for-response.patch | 59 +++ ...-set-small-runtime-autosuspend-delay.patch | 79 +++ ...-lockup-on-first-rx-endpoint-request.patch | 133 ++++++ 12 files changed, 1437 insertions(+) create mode 100644 queue-6.11/smb-client-handle-max-length-for-smb-symlinks.patch create mode 100644 queue-6.11/smb-don-t-leak-cfid-when-reconnect-races-with-open_cached_dir.patch create mode 100644 queue-6.11/smb-during-unmount-ensure-all-cached-dir-instances-drop-their-dentry.patch create mode 100644 queue-6.11/smb-prevent-use-after-free-due-to-open_cached_dir-error-paths.patch create mode 100644 queue-6.11/smb3-request-handle-caching-when-caching-directories.patch create mode 100644 queue-6.11/usb-dwc3-ep0-don-t-clear-ep0-dwc3_ep_transfer_started.patch create mode 100644 queue-6.11/usb-dwc3-gadget-fix-checking-for-number-of-trbs-left.patch create mode 100644 queue-6.11/usb-dwc3-gadget-fix-looping-of-queued-sg-entries.patch create mode 100644 queue-6.11/usb-misc-ljca-move-usb_autopm_put_interface-after-wait-for-response.patch create mode 100644 queue-6.11/usb-misc-ljca-set-small-runtime-autosuspend-delay.patch create mode 100644 queue-6.11/usb-musb-fix-hardware-lockup-on-first-rx-endpoint-request.patch diff --git a/queue-6.11/series b/queue-6.11/series index 5ef4e6466ea..cfb2d8fcfae 100644 --- a/queue-6.11/series +++ b/queue-6.11/series @@ -744,3 +744,14 @@ alsa-hda-realtek-update-alc225-depop-procedure.patch alsa-hda-realtek-set-pcbeep-to-default-value-for-alc274.patch alsa-hda-realtek-fix-internal-speaker-and-mic-boost-of-infinix-y4-max.patch alsa-hda-realtek-apply-quirk-for-medion-e15433.patch +smb3-request-handle-caching-when-caching-directories.patch +smb-client-handle-max-length-for-smb-symlinks.patch +smb-don-t-leak-cfid-when-reconnect-races-with-open_cached_dir.patch +smb-prevent-use-after-free-due-to-open_cached_dir-error-paths.patch +smb-during-unmount-ensure-all-cached-dir-instances-drop-their-dentry.patch +usb-misc-ljca-set-small-runtime-autosuspend-delay.patch +usb-misc-ljca-move-usb_autopm_put_interface-after-wait-for-response.patch +usb-dwc3-ep0-don-t-clear-ep0-dwc3_ep_transfer_started.patch +usb-musb-fix-hardware-lockup-on-first-rx-endpoint-request.patch +usb-dwc3-gadget-fix-checking-for-number-of-trbs-left.patch +usb-dwc3-gadget-fix-looping-of-queued-sg-entries.patch diff --git a/queue-6.11/smb-client-handle-max-length-for-smb-symlinks.patch b/queue-6.11/smb-client-handle-max-length-for-smb-symlinks.patch new file mode 100644 index 00000000000..ba142c986ed --- /dev/null +++ b/queue-6.11/smb-client-handle-max-length-for-smb-symlinks.patch @@ -0,0 +1,64 @@ +From 0812340811e45ec4039d409049be53056182a552 Mon Sep 17 00:00:00 2001 +From: Paulo Alcantara +Date: Mon, 18 Nov 2024 12:35:16 -0300 +Subject: smb: client: handle max length for SMB symlinks + +From: Paulo Alcantara + +commit 0812340811e45ec4039d409049be53056182a552 upstream. + +We can't use PATH_MAX for SMB symlinks because + + (1) Windows Server will fail FSCTL_SET_REPARSE_POINT with + STATUS_IO_REPARSE_DATA_INVALID when input buffer is larger than + 16K, as specified in MS-FSA 2.1.5.10.37. + + (2) The client won't be able to parse large SMB responses that + includes SMB symlink path within SMB2_CREATE or SMB2_IOCTL + responses. + +Fix this by defining a maximum length value (4060) for SMB symlinks +that both client and server can handle. + +Cc: David Howells +Cc: stable@vger.kernel.org +Signed-off-by: Paulo Alcantara (Red Hat) +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/reparse.c | 5 ++++- + fs/smb/client/reparse.h | 2 ++ + 2 files changed, 6 insertions(+), 1 deletion(-) + +--- a/fs/smb/client/reparse.c ++++ b/fs/smb/client/reparse.c +@@ -35,6 +35,9 @@ int smb2_create_reparse_symlink(const un + u16 len, plen; + int rc = 0; + ++ if (strlen(symname) > REPARSE_SYM_PATH_MAX) ++ return -ENAMETOOLONG; ++ + sym = kstrdup(symname, GFP_KERNEL); + if (!sym) + return -ENOMEM; +@@ -64,7 +67,7 @@ int smb2_create_reparse_symlink(const un + if (rc < 0) + goto out; + +- plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX); ++ plen = 2 * UniStrnlen((wchar_t *)path, REPARSE_SYM_PATH_MAX); + len = sizeof(*buf) + plen * 2; + buf = kzalloc(len, GFP_KERNEL); + if (!buf) { +--- a/fs/smb/client/reparse.h ++++ b/fs/smb/client/reparse.h +@@ -12,6 +12,8 @@ + #include "fs_context.h" + #include "cifsglob.h" + ++#define REPARSE_SYM_PATH_MAX 4060 ++ + /* + * Used only by cifs.ko to ignore reparse points from files when client or + * server doesn't support FSCTL_GET_REPARSE_POINT. diff --git a/queue-6.11/smb-don-t-leak-cfid-when-reconnect-races-with-open_cached_dir.patch b/queue-6.11/smb-don-t-leak-cfid-when-reconnect-races-with-open_cached_dir.patch new file mode 100644 index 00000000000..fa804019e1f --- /dev/null +++ b/queue-6.11/smb-don-t-leak-cfid-when-reconnect-races-with-open_cached_dir.patch @@ -0,0 +1,226 @@ +From 7afb86733685c64c604d32faf00fa4a1f22c2ab1 Mon Sep 17 00:00:00 2001 +From: Paul Aurich +Date: Mon, 18 Nov 2024 13:50:26 -0800 +Subject: smb: Don't leak cfid when reconnect races with open_cached_dir + +From: Paul Aurich + +commit 7afb86733685c64c604d32faf00fa4a1f22c2ab1 upstream. + +open_cached_dir() may either race with the tcon reconnection even before +compound_send_recv() or directly trigger a reconnection via +SMB2_open_init() or SMB_query_info_init(). + +The reconnection process invokes invalidate_all_cached_dirs() via +cifs_mark_open_files_invalid(), which removes all cfids from the +cfids->entries list but doesn't drop a ref if has_lease isn't true. This +results in the currently-being-constructed cfid not being on the list, +but still having a refcount of 2. It leaks if returned from +open_cached_dir(). + +Fix this by setting cfid->has_lease when the ref is actually taken; the +cfid will not be used by other threads until it has a valid time. + +Addresses these kmemleaks: + +unreferenced object 0xffff8881090c4000 (size 1024): + comm "bash", pid 1860, jiffies 4295126592 + hex dump (first 32 bytes): + 00 01 00 00 00 00 ad de 22 01 00 00 00 00 ad de ........"....... + 00 ca 45 22 81 88 ff ff f8 dc 4f 04 81 88 ff ff ..E"......O..... + backtrace (crc 6f58c20f): + [] __kmalloc_cache_noprof+0x2be/0x350 + [] open_cached_dir+0x993/0x1fb0 + [] cifs_readdir+0x15a0/0x1d50 + [] iterate_dir+0x28f/0x4b0 + [] __x64_sys_getdents64+0xfd/0x200 + [] do_syscall_64+0x95/0x1a0 + [] entry_SYSCALL_64_after_hwframe+0x76/0x7e +unreferenced object 0xffff8881044fdcf8 (size 8): + comm "bash", pid 1860, jiffies 4295126592 + hex dump (first 8 bytes): + 00 cc cc cc cc cc cc cc ........ + backtrace (crc 10c106a9): + [] __kmalloc_node_track_caller_noprof+0x363/0x480 + [] kstrdup+0x36/0x60 + [] open_cached_dir+0x9b0/0x1fb0 + [] cifs_readdir+0x15a0/0x1d50 + [] iterate_dir+0x28f/0x4b0 + [] __x64_sys_getdents64+0xfd/0x200 + [] do_syscall_64+0x95/0x1a0 + [] entry_SYSCALL_64_after_hwframe+0x76/0x7e + +And addresses these BUG splats when unmounting the SMB filesystem: + +BUG: Dentry ffff888140590ba0{i=1000000000080,n=/} still in use (2) [unmount of cifs cifs] +WARNING: CPU: 3 PID: 3433 at fs/dcache.c:1536 umount_check+0xd0/0x100 +Modules linked in: +CPU: 3 UID: 0 PID: 3433 Comm: bash Not tainted 6.12.0-rc4-g850925a8133c-dirty #49 +Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 11/12/2020 +RIP: 0010:umount_check+0xd0/0x100 +Code: 8d 7c 24 40 e8 31 5a f4 ff 49 8b 54 24 40 41 56 49 89 e9 45 89 e8 48 89 d9 41 57 48 89 de 48 c7 c7 80 e7 db ac e8 f0 72 9a ff <0f> 0b 58 31 c0 5a 5b 5d 41 5c 41 5d 41 5e 41 5f e9 2b e5 5d 01 41 +RSP: 0018:ffff88811cc27978 EFLAGS: 00010286 +RAX: 0000000000000000 RBX: ffff888140590ba0 RCX: ffffffffaaf20bae +RDX: dffffc0000000000 RSI: 0000000000000008 RDI: ffff8881f6fb6f40 +RBP: ffff8881462ec000 R08: 0000000000000001 R09: ffffed1023984ee3 +R10: ffff88811cc2771f R11: 00000000016cfcc0 R12: ffff888134383e08 +R13: 0000000000000002 R14: ffff8881462ec668 R15: ffffffffaceab4c0 +FS: 00007f23bfa98740(0000) GS:ffff8881f6f80000(0000) knlGS:0000000000000000 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 0000556de4a6f808 CR3: 0000000123c80000 CR4: 0000000000350ef0 +Call Trace: + + d_walk+0x6a/0x530 + shrink_dcache_for_umount+0x6a/0x200 + generic_shutdown_super+0x52/0x2a0 + kill_anon_super+0x22/0x40 + cifs_kill_sb+0x159/0x1e0 + deactivate_locked_super+0x66/0xe0 + cleanup_mnt+0x140/0x210 + task_work_run+0xfb/0x170 + syscall_exit_to_user_mode+0x29f/0x2b0 + do_syscall_64+0xa1/0x1a0 + entry_SYSCALL_64_after_hwframe+0x76/0x7e +RIP: 0033:0x7f23bfb93ae7 +Code: ff ff ff ff c3 66 0f 1f 44 00 00 48 8b 0d 11 93 0d 00 f7 d8 64 89 01 b8 ff ff ff ff eb bf 0f 1f 44 00 00 b8 50 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d e9 92 0d 00 f7 d8 64 89 01 48 +RSP: 002b:00007ffee9138598 EFLAGS: 00000246 ORIG_RAX: 0000000000000050 +RAX: 0000000000000000 RBX: 0000558f1803e9a0 RCX: 00007f23bfb93ae7 +RDX: 0000000000000000 RSI: 0000000000000004 RDI: 0000558f1803e9a0 +RBP: 0000558f1803e600 R08: 0000000000000007 R09: 0000558f17fab610 +R10: d91d5ec34ab757b0 R11: 0000000000000246 R12: 0000000000000001 +R13: 0000000000000000 R14: 0000000000000015 R15: 0000000000000000 + +irq event stamp: 1163486 +hardirqs last enabled at (1163485): [] _raw_spin_unlock_irqrestore+0x34/0x60 +hardirqs last disabled at (1163486): [] __schedule+0xc7c/0x19a0 +softirqs last enabled at (1163482): [] __smb_send_rqst+0x3de/0x990 +softirqs last disabled at (1163480): [] release_sock+0x21/0xf0 +---[ end trace 0000000000000000 ]--- + +VFS: Busy inodes after unmount of cifs (cifs) +------------[ cut here ]------------ +kernel BUG at fs/super.c:661! +Oops: invalid opcode: 0000 [#1] PREEMPT SMP KASAN NOPTI +CPU: 1 UID: 0 PID: 3433 Comm: bash Tainted: G W 6.12.0-rc4-g850925a8133c-dirty #49 +Tainted: [W]=WARN +Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 11/12/2020 +RIP: 0010:generic_shutdown_super+0x290/0x2a0 +Code: e8 15 7c f7 ff 48 8b 5d 28 48 89 df e8 09 7c f7 ff 48 8b 0b 48 89 ee 48 8d 95 68 06 00 00 48 c7 c7 80 7f db ac e8 00 69 af ff <0f> 0b 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 00 90 90 90 90 90 90 +RSP: 0018:ffff88811cc27a50 EFLAGS: 00010246 +RAX: 000000000000003e RBX: ffffffffae994420 RCX: 0000000000000027 +RDX: 0000000000000000 RSI: ffffffffab06180e RDI: ffff8881f6eb18c8 +RBP: ffff8881462ec000 R08: 0000000000000001 R09: ffffed103edd6319 +R10: ffff8881f6eb18cb R11: 00000000016d3158 R12: ffff8881462ec9c0 +R13: ffff8881462ec050 R14: 0000000000000001 R15: 0000000000000000 +FS: 00007f23bfa98740(0000) GS:ffff8881f6e80000(0000) knlGS:0000000000000000 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 00007f8364005d68 CR3: 0000000123c80000 CR4: 0000000000350ef0 +Call Trace: + + kill_anon_super+0x22/0x40 + cifs_kill_sb+0x159/0x1e0 + deactivate_locked_super+0x66/0xe0 + cleanup_mnt+0x140/0x210 + task_work_run+0xfb/0x170 + syscall_exit_to_user_mode+0x29f/0x2b0 + do_syscall_64+0xa1/0x1a0 + entry_SYSCALL_64_after_hwframe+0x76/0x7e +RIP: 0033:0x7f23bfb93ae7 + +Modules linked in: +---[ end trace 0000000000000000 ]--- +RIP: 0010:generic_shutdown_super+0x290/0x2a0 +Code: e8 15 7c f7 ff 48 8b 5d 28 48 89 df e8 09 7c f7 ff 48 8b 0b 48 89 ee 48 8d 95 68 06 00 00 48 c7 c7 80 7f db ac e8 00 69 af ff <0f> 0b 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 00 90 90 90 90 90 90 +RSP: 0018:ffff88811cc27a50 EFLAGS: 00010246 +RAX: 000000000000003e RBX: ffffffffae994420 RCX: 0000000000000027 +RDX: 0000000000000000 RSI: ffffffffab06180e RDI: ffff8881f6eb18c8 +RBP: ffff8881462ec000 R08: 0000000000000001 R09: ffffed103edd6319 +R10: ffff8881f6eb18cb R11: 00000000016d3158 R12: ffff8881462ec9c0 +R13: ffff8881462ec050 R14: 0000000000000001 R15: 0000000000000000 +FS: 00007f23bfa98740(0000) GS:ffff8881f6e80000(0000) knlGS:0000000000000000 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 00007f8364005d68 CR3: 0000000123c80000 CR4: 0000000000350ef0 + +This reproduces eventually with an SMB mount and two shells running +these loops concurrently + +- while true; do + cd ~; sleep 1; + for i in {1..3}; do cd /mnt/test/subdir; + echo $PWD; sleep 1; cd ..; echo $PWD; sleep 1; + done; + echo ...; + done +- while true; do + iptables -F OUTPUT; mount -t cifs -a; + for _ in {0..2}; do ls /mnt/test/subdir/ | wc -l; done; + iptables -I OUTPUT -p tcp --dport 445 -j DROP; + sleep 10 + echo "unmounting"; umount -l -t cifs -a; echo "done unmounting"; + sleep 20 + echo "recovering"; iptables -F OUTPUT; + sleep 10; + done + +Fixes: ebe98f1447bb ("cifs: enable caching of directories for which a lease is held") +Fixes: 5c86919455c1 ("smb: client: fix use-after-free in smb2_query_info_compound()") +Cc: stable@vger.kernel.org +Signed-off-by: Paul Aurich +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/cached_dir.c | 27 ++++++++++++++------------- + 1 file changed, 14 insertions(+), 13 deletions(-) + +--- a/fs/smb/client/cached_dir.c ++++ b/fs/smb/client/cached_dir.c +@@ -59,6 +59,16 @@ static struct cached_fid *find_or_create + list_add(&cfid->entry, &cfids->entries); + cfid->on_list = true; + kref_get(&cfid->refcount); ++ /* ++ * Set @cfid->has_lease to true during construction so that the lease ++ * reference can be put in cached_dir_lease_break() due to a potential ++ * lease break right after the request is sent or while @cfid is still ++ * being cached, or if a reconnection is triggered during construction. ++ * Concurrent processes won't be to use it yet due to @cfid->time being ++ * zero. ++ */ ++ cfid->has_lease = true; ++ + spin_unlock(&cfids->cfid_list_lock); + return cfid; + } +@@ -176,12 +186,12 @@ replay_again: + return -ENOENT; + } + /* +- * Return cached fid if it has a lease. Otherwise, it is either a new +- * entry or laundromat worker removed it from @cfids->entries. Caller +- * will put last reference if the latter. ++ * Return cached fid if it is valid (has a lease and has a time). ++ * Otherwise, it is either a new entry or laundromat worker removed it ++ * from @cfids->entries. Caller will put last reference if the latter. + */ + spin_lock(&cfids->cfid_list_lock); +- if (cfid->has_lease) { ++ if (cfid->has_lease && cfid->time) { + spin_unlock(&cfids->cfid_list_lock); + *ret_cfid = cfid; + kfree(utf16_path); +@@ -267,15 +277,6 @@ replay_again: + + smb2_set_related(&rqst[1]); + +- /* +- * Set @cfid->has_lease to true before sending out compounded request so +- * its lease reference can be put in cached_dir_lease_break() due to a +- * potential lease break right after the request is sent or while @cfid +- * is still being cached. Concurrent processes won't be to use it yet +- * due to @cfid->time being zero. +- */ +- cfid->has_lease = true; +- + if (retries) { + smb2_set_replay(server, &rqst[0]); + smb2_set_replay(server, &rqst[1]); diff --git a/queue-6.11/smb-during-unmount-ensure-all-cached-dir-instances-drop-their-dentry.patch b/queue-6.11/smb-during-unmount-ensure-all-cached-dir-instances-drop-their-dentry.patch new file mode 100644 index 00000000000..755946bb003 --- /dev/null +++ b/queue-6.11/smb-during-unmount-ensure-all-cached-dir-instances-drop-their-dentry.patch @@ -0,0 +1,449 @@ +From 3fa640d035e5ae526769615c35cb9ed4be6e3662 Mon Sep 17 00:00:00 2001 +From: Paul Aurich +Date: Mon, 18 Nov 2024 13:50:28 -0800 +Subject: smb: During unmount, ensure all cached dir instances drop their dentry + +From: Paul Aurich + +commit 3fa640d035e5ae526769615c35cb9ed4be6e3662 upstream. + +The unmount process (cifs_kill_sb() calling close_all_cached_dirs()) can +race with various cached directory operations, which ultimately results +in dentries not being dropped and these kernel BUGs: + +BUG: Dentry ffff88814f37e358{i=1000000000080,n=/} still in use (2) [unmount of cifs cifs] +VFS: Busy inodes after unmount of cifs (cifs) +------------[ cut here ]------------ +kernel BUG at fs/super.c:661! + +This happens when a cfid is in the process of being cleaned up when, and +has been removed from the cfids->entries list, including: + +- Receiving a lease break from the server +- Server reconnection triggers invalidate_all_cached_dirs(), which + removes all the cfids from the list +- The laundromat thread decides to expire an old cfid. + +To solve these problems, dropping the dentry is done in queued work done +in a newly-added cfid_put_wq workqueue, and close_all_cached_dirs() +flushes that workqueue after it drops all the dentries of which it's +aware. This is a global workqueue (rather than scoped to a mount), but +the queued work is minimal. + +The final cleanup work for cleaning up a cfid is performed via work +queued in the serverclose_wq workqueue; this is done separate from +dropping the dentries so that close_all_cached_dirs() doesn't block on +any server operations. + +Both of these queued works expect to invoked with a cfid reference and +a tcon reference to avoid those objects from being freed while the work +is ongoing. + +While we're here, add proper locking to close_all_cached_dirs(), and +locking around the freeing of cfid->dentry. + +Fixes: ebe98f1447bb ("cifs: enable caching of directories for which a lease is held") +Cc: stable@vger.kernel.org +Signed-off-by: Paul Aurich +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/cached_dir.c | 156 ++++++++++++++++++++++++++++++++++++--------- + fs/smb/client/cached_dir.h | 6 + + fs/smb/client/cifsfs.c | 12 +++ + fs/smb/client/cifsglob.h | 3 + fs/smb/client/inode.c | 3 + fs/smb/client/trace.h | 3 + 6 files changed, 147 insertions(+), 36 deletions(-) + +--- a/fs/smb/client/cached_dir.c ++++ b/fs/smb/client/cached_dir.c +@@ -17,6 +17,11 @@ static void free_cached_dir(struct cache + static void smb2_close_cached_fid(struct kref *ref); + static void cfids_laundromat_worker(struct work_struct *work); + ++struct cached_dir_dentry { ++ struct list_head entry; ++ struct dentry *dentry; ++}; ++ + static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids, + const char *path, + bool lookup_only, +@@ -470,7 +475,10 @@ void close_all_cached_dirs(struct cifs_s + struct cifs_tcon *tcon; + struct tcon_link *tlink; + struct cached_fids *cfids; ++ struct cached_dir_dentry *tmp_list, *q; ++ LIST_HEAD(entry); + ++ spin_lock(&cifs_sb->tlink_tree_lock); + for (node = rb_first(root); node; node = rb_next(node)) { + tlink = rb_entry(node, struct tcon_link, tl_rbnode); + tcon = tlink_tcon(tlink); +@@ -479,11 +487,30 @@ void close_all_cached_dirs(struct cifs_s + cfids = tcon->cfids; + if (cfids == NULL) + continue; ++ spin_lock(&cfids->cfid_list_lock); + list_for_each_entry(cfid, &cfids->entries, entry) { +- dput(cfid->dentry); ++ tmp_list = kmalloc(sizeof(*tmp_list), GFP_ATOMIC); ++ if (tmp_list == NULL) ++ break; ++ spin_lock(&cfid->fid_lock); ++ tmp_list->dentry = cfid->dentry; + cfid->dentry = NULL; ++ spin_unlock(&cfid->fid_lock); ++ ++ list_add_tail(&tmp_list->entry, &entry); + } ++ spin_unlock(&cfids->cfid_list_lock); ++ } ++ spin_unlock(&cifs_sb->tlink_tree_lock); ++ ++ list_for_each_entry_safe(tmp_list, q, &entry, entry) { ++ list_del(&tmp_list->entry); ++ dput(tmp_list->dentry); ++ kfree(tmp_list); + } ++ ++ /* Flush any pending work that will drop dentries */ ++ flush_workqueue(cfid_put_wq); + } + + /* +@@ -494,14 +521,18 @@ void invalidate_all_cached_dirs(struct c + { + struct cached_fids *cfids = tcon->cfids; + struct cached_fid *cfid, *q; +- LIST_HEAD(entry); + + if (cfids == NULL) + return; + ++ /* ++ * Mark all the cfids as closed, and move them to the cfids->dying list. ++ * They'll be cleaned up later by cfids_invalidation_worker. Take ++ * a reference to each cfid during this process. ++ */ + spin_lock(&cfids->cfid_list_lock); + list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { +- list_move(&cfid->entry, &entry); ++ list_move(&cfid->entry, &cfids->dying); + cfids->num_entries--; + cfid->is_open = false; + cfid->on_list = false; +@@ -514,26 +545,47 @@ void invalidate_all_cached_dirs(struct c + } else + kref_get(&cfid->refcount); + } ++ /* ++ * Queue dropping of the dentries once locks have been dropped ++ */ ++ if (!list_empty(&cfids->dying)) ++ queue_work(cfid_put_wq, &cfids->invalidation_work); + spin_unlock(&cfids->cfid_list_lock); +- +- list_for_each_entry_safe(cfid, q, &entry, entry) { +- list_del(&cfid->entry); +- cancel_work_sync(&cfid->lease_break); +- /* +- * Drop the ref-count from above, either the lease-ref (if there +- * was one) or the extra one acquired. +- */ +- kref_put(&cfid->refcount, smb2_close_cached_fid); +- } + } + + static void +-smb2_cached_lease_break(struct work_struct *work) ++cached_dir_offload_close(struct work_struct *work) + { + struct cached_fid *cfid = container_of(work, +- struct cached_fid, lease_break); ++ struct cached_fid, close_work); ++ struct cifs_tcon *tcon = cfid->tcon; ++ ++ WARN_ON(cfid->on_list); + + kref_put(&cfid->refcount, smb2_close_cached_fid); ++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cached_close); ++} ++ ++/* ++ * Release the cached directory's dentry, and then queue work to drop cached ++ * directory itself (closing on server if needed). ++ * ++ * Must be called with a reference to the cached_fid and a reference to the ++ * tcon. ++ */ ++static void cached_dir_put_work(struct work_struct *work) ++{ ++ struct cached_fid *cfid = container_of(work, struct cached_fid, ++ put_work); ++ struct dentry *dentry; ++ ++ spin_lock(&cfid->fid_lock); ++ dentry = cfid->dentry; ++ cfid->dentry = NULL; ++ spin_unlock(&cfid->fid_lock); ++ ++ dput(dentry); ++ queue_work(serverclose_wq, &cfid->close_work); + } + + int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]) +@@ -560,8 +612,10 @@ int cached_dir_lease_break(struct cifs_t + cfid->on_list = false; + cfids->num_entries--; + +- queue_work(cifsiod_wq, +- &cfid->lease_break); ++ ++tcon->tc_count; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_get_cached_lease_break); ++ queue_work(cfid_put_wq, &cfid->put_work); + spin_unlock(&cfids->cfid_list_lock); + return true; + } +@@ -583,7 +637,8 @@ static struct cached_fid *init_cached_di + return NULL; + } + +- INIT_WORK(&cfid->lease_break, smb2_cached_lease_break); ++ INIT_WORK(&cfid->close_work, cached_dir_offload_close); ++ INIT_WORK(&cfid->put_work, cached_dir_put_work); + INIT_LIST_HEAD(&cfid->entry); + INIT_LIST_HEAD(&cfid->dirents.entries); + mutex_init(&cfid->dirents.de_mutex); +@@ -596,6 +651,9 @@ static void free_cached_dir(struct cache + { + struct cached_dirent *dirent, *q; + ++ WARN_ON(work_pending(&cfid->close_work)); ++ WARN_ON(work_pending(&cfid->put_work)); ++ + dput(cfid->dentry); + cfid->dentry = NULL; + +@@ -613,10 +671,30 @@ static void free_cached_dir(struct cache + kfree(cfid); + } + ++static void cfids_invalidation_worker(struct work_struct *work) ++{ ++ struct cached_fids *cfids = container_of(work, struct cached_fids, ++ invalidation_work); ++ struct cached_fid *cfid, *q; ++ LIST_HEAD(entry); ++ ++ spin_lock(&cfids->cfid_list_lock); ++ /* move cfids->dying to the local list */ ++ list_cut_before(&entry, &cfids->dying, &cfids->dying); ++ spin_unlock(&cfids->cfid_list_lock); ++ ++ list_for_each_entry_safe(cfid, q, &entry, entry) { ++ list_del(&cfid->entry); ++ /* Drop the ref-count acquired in invalidate_all_cached_dirs */ ++ kref_put(&cfid->refcount, smb2_close_cached_fid); ++ } ++} ++ + static void cfids_laundromat_worker(struct work_struct *work) + { + struct cached_fids *cfids; + struct cached_fid *cfid, *q; ++ struct dentry *dentry; + LIST_HEAD(entry); + + cfids = container_of(work, struct cached_fids, laundromat_work.work); +@@ -642,18 +720,28 @@ static void cfids_laundromat_worker(stru + + list_for_each_entry_safe(cfid, q, &entry, entry) { + list_del(&cfid->entry); +- /* +- * Cancel and wait for the work to finish in case we are racing +- * with it. +- */ +- cancel_work_sync(&cfid->lease_break); +- /* +- * Drop the ref-count from above, either the lease-ref (if there +- * was one) or the extra one acquired. +- */ +- kref_put(&cfid->refcount, smb2_close_cached_fid); ++ ++ spin_lock(&cfid->fid_lock); ++ dentry = cfid->dentry; ++ cfid->dentry = NULL; ++ spin_unlock(&cfid->fid_lock); ++ ++ dput(dentry); ++ if (cfid->is_open) { ++ spin_lock(&cifs_tcp_ses_lock); ++ ++cfid->tcon->tc_count; ++ trace_smb3_tcon_ref(cfid->tcon->debug_id, cfid->tcon->tc_count, ++ netfs_trace_tcon_ref_get_cached_laundromat); ++ spin_unlock(&cifs_tcp_ses_lock); ++ queue_work(serverclose_wq, &cfid->close_work); ++ } else ++ /* ++ * Drop the ref-count from above, either the lease-ref (if there ++ * was one) or the extra one acquired. ++ */ ++ kref_put(&cfid->refcount, smb2_close_cached_fid); + } +- queue_delayed_work(cifsiod_wq, &cfids->laundromat_work, ++ queue_delayed_work(cfid_put_wq, &cfids->laundromat_work, + dir_cache_timeout * HZ); + } + +@@ -666,9 +754,11 @@ struct cached_fids *init_cached_dirs(voi + return NULL; + spin_lock_init(&cfids->cfid_list_lock); + INIT_LIST_HEAD(&cfids->entries); ++ INIT_LIST_HEAD(&cfids->dying); + ++ INIT_WORK(&cfids->invalidation_work, cfids_invalidation_worker); + INIT_DELAYED_WORK(&cfids->laundromat_work, cfids_laundromat_worker); +- queue_delayed_work(cifsiod_wq, &cfids->laundromat_work, ++ queue_delayed_work(cfid_put_wq, &cfids->laundromat_work, + dir_cache_timeout * HZ); + + return cfids; +@@ -687,12 +777,18 @@ void free_cached_dirs(struct cached_fids + return; + + cancel_delayed_work_sync(&cfids->laundromat_work); ++ cancel_work_sync(&cfids->invalidation_work); + + spin_lock(&cfids->cfid_list_lock); + list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { + cfid->on_list = false; + cfid->is_open = false; + list_move(&cfid->entry, &entry); ++ } ++ list_for_each_entry_safe(cfid, q, &cfids->dying, entry) { ++ cfid->on_list = false; ++ cfid->is_open = false; ++ list_move(&cfid->entry, &entry); + } + spin_unlock(&cfids->cfid_list_lock); + +--- a/fs/smb/client/cached_dir.h ++++ b/fs/smb/client/cached_dir.h +@@ -44,7 +44,8 @@ struct cached_fid { + spinlock_t fid_lock; + struct cifs_tcon *tcon; + struct dentry *dentry; +- struct work_struct lease_break; ++ struct work_struct put_work; ++ struct work_struct close_work; + struct smb2_file_all_info file_all_info; + struct cached_dirents dirents; + }; +@@ -53,10 +54,13 @@ struct cached_fid { + struct cached_fids { + /* Must be held when: + * - accessing the cfids->entries list ++ * - accessing the cfids->dying list + */ + spinlock_t cfid_list_lock; + int num_entries; + struct list_head entries; ++ struct list_head dying; ++ struct work_struct invalidation_work; + struct delayed_work laundromat_work; + }; + +--- a/fs/smb/client/cifsfs.c ++++ b/fs/smb/client/cifsfs.c +@@ -157,6 +157,7 @@ struct workqueue_struct *fileinfo_put_wq + struct workqueue_struct *cifsoplockd_wq; + struct workqueue_struct *deferredclose_wq; + struct workqueue_struct *serverclose_wq; ++struct workqueue_struct *cfid_put_wq; + __u32 cifs_lock_secret; + + /* +@@ -1895,9 +1896,16 @@ init_cifs(void) + goto out_destroy_deferredclose_wq; + } + ++ cfid_put_wq = alloc_workqueue("cfid_put_wq", ++ WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); ++ if (!cfid_put_wq) { ++ rc = -ENOMEM; ++ goto out_destroy_serverclose_wq; ++ } ++ + rc = cifs_init_inodecache(); + if (rc) +- goto out_destroy_serverclose_wq; ++ goto out_destroy_cfid_put_wq; + + rc = cifs_init_netfs(); + if (rc) +@@ -1965,6 +1973,8 @@ out_destroy_netfs: + cifs_destroy_netfs(); + out_destroy_inodecache: + cifs_destroy_inodecache(); ++out_destroy_cfid_put_wq: ++ destroy_workqueue(cfid_put_wq); + out_destroy_serverclose_wq: + destroy_workqueue(serverclose_wq); + out_destroy_deferredclose_wq: +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -1982,7 +1982,7 @@ require use of the stronger protocol */ + * cifsInodeInfo->lock_sem cifsInodeInfo->llist cifs_init_once + * ->can_cache_brlcks + * cifsInodeInfo->deferred_lock cifsInodeInfo->deferred_closes cifsInodeInfo_alloc +- * cached_fid->fid_mutex cifs_tcon->crfid tcon_info_alloc ++ * cached_fids->cfid_list_lock cifs_tcon->cfids->entries init_cached_dirs + * cifsFileInfo->fh_mutex cifsFileInfo cifs_new_fileinfo + * cifsFileInfo->file_info_lock cifsFileInfo->count cifs_new_fileinfo + * ->invalidHandle initiate_cifs_search +@@ -2070,6 +2070,7 @@ extern struct workqueue_struct *fileinfo + extern struct workqueue_struct *cifsoplockd_wq; + extern struct workqueue_struct *deferredclose_wq; + extern struct workqueue_struct *serverclose_wq; ++extern struct workqueue_struct *cfid_put_wq; + extern __u32 cifs_lock_secret; + + extern mempool_t *cifs_sm_req_poolp; +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -2433,13 +2433,10 @@ cifs_dentry_needs_reval(struct dentry *d + return true; + + if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) { +- spin_lock(&cfid->fid_lock); + if (cfid->time && cifs_i->time > cfid->time) { +- spin_unlock(&cfid->fid_lock); + close_cached_dir(cfid); + return false; + } +- spin_unlock(&cfid->fid_lock); + close_cached_dir(cfid); + } + /* +--- a/fs/smb/client/trace.h ++++ b/fs/smb/client/trace.h +@@ -44,6 +44,8 @@ + EM(netfs_trace_tcon_ref_free_ipc, "FRE Ipc ") \ + EM(netfs_trace_tcon_ref_free_ipc_fail, "FRE Ipc-F ") \ + EM(netfs_trace_tcon_ref_free_reconnect_server, "FRE Reconn") \ ++ EM(netfs_trace_tcon_ref_get_cached_laundromat, "GET Ch-Lau") \ ++ EM(netfs_trace_tcon_ref_get_cached_lease_break, "GET Ch-Lea") \ + EM(netfs_trace_tcon_ref_get_cancelled_close, "GET Cn-Cls") \ + EM(netfs_trace_tcon_ref_get_dfs_refer, "GET DfsRef") \ + EM(netfs_trace_tcon_ref_get_find, "GET Find ") \ +@@ -52,6 +54,7 @@ + EM(netfs_trace_tcon_ref_new, "NEW ") \ + EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \ + EM(netfs_trace_tcon_ref_new_reconnect_server, "NEW Reconn") \ ++ EM(netfs_trace_tcon_ref_put_cached_close, "PUT Ch-Cls") \ + EM(netfs_trace_tcon_ref_put_cancelled_close, "PUT Cn-Cls") \ + EM(netfs_trace_tcon_ref_put_cancelled_close_fid, "PUT Cn-Fid") \ + EM(netfs_trace_tcon_ref_put_cancelled_mid, "PUT Cn-Mid") \ diff --git a/queue-6.11/smb-prevent-use-after-free-due-to-open_cached_dir-error-paths.patch b/queue-6.11/smb-prevent-use-after-free-due-to-open_cached_dir-error-paths.patch new file mode 100644 index 00000000000..69338440e59 --- /dev/null +++ b/queue-6.11/smb-prevent-use-after-free-due-to-open_cached_dir-error-paths.patch @@ -0,0 +1,246 @@ +From a9685b409a03b73d2980bbfa53eb47555802d0a9 Mon Sep 17 00:00:00 2001 +From: Paul Aurich +Date: Mon, 18 Nov 2024 13:50:27 -0800 +Subject: smb: prevent use-after-free due to open_cached_dir error paths + +From: Paul Aurich + +commit a9685b409a03b73d2980bbfa53eb47555802d0a9 upstream. + +If open_cached_dir() encounters an error parsing the lease from the +server, the error handling may race with receiving a lease break, +resulting in open_cached_dir() freeing the cfid while the queued work is +pending. + +Update open_cached_dir() to drop refs rather than directly freeing the +cfid. + +Have cached_dir_lease_break(), cfids_laundromat_worker(), and +invalidate_all_cached_dirs() clear has_lease immediately while still +holding cfids->cfid_list_lock, and then use this to also simplify the +reference counting in cfids_laundromat_worker() and +invalidate_all_cached_dirs(). + +Fixes this KASAN splat (which manually injects an error and lease break +in open_cached_dir()): + +================================================================== +BUG: KASAN: slab-use-after-free in smb2_cached_lease_break+0x27/0xb0 +Read of size 8 at addr ffff88811cc24c10 by task kworker/3:1/65 + +CPU: 3 UID: 0 PID: 65 Comm: kworker/3:1 Not tainted 6.12.0-rc6-g255cf264e6e5-dirty #87 +Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 11/12/2020 +Workqueue: cifsiod smb2_cached_lease_break +Call Trace: + + dump_stack_lvl+0x77/0xb0 + print_report+0xce/0x660 + kasan_report+0xd3/0x110 + smb2_cached_lease_break+0x27/0xb0 + process_one_work+0x50a/0xc50 + worker_thread+0x2ba/0x530 + kthread+0x17c/0x1c0 + ret_from_fork+0x34/0x60 + ret_from_fork_asm+0x1a/0x30 + + +Allocated by task 2464: + kasan_save_stack+0x33/0x60 + kasan_save_track+0x14/0x30 + __kasan_kmalloc+0xaa/0xb0 + open_cached_dir+0xa7d/0x1fb0 + smb2_query_path_info+0x43c/0x6e0 + cifs_get_fattr+0x346/0xf10 + cifs_get_inode_info+0x157/0x210 + cifs_revalidate_dentry_attr+0x2d1/0x460 + cifs_getattr+0x173/0x470 + vfs_statx_path+0x10f/0x160 + vfs_statx+0xe9/0x150 + vfs_fstatat+0x5e/0xc0 + __do_sys_newfstatat+0x91/0xf0 + do_syscall_64+0x95/0x1a0 + entry_SYSCALL_64_after_hwframe+0x76/0x7e + +Freed by task 2464: + kasan_save_stack+0x33/0x60 + kasan_save_track+0x14/0x30 + kasan_save_free_info+0x3b/0x60 + __kasan_slab_free+0x51/0x70 + kfree+0x174/0x520 + open_cached_dir+0x97f/0x1fb0 + smb2_query_path_info+0x43c/0x6e0 + cifs_get_fattr+0x346/0xf10 + cifs_get_inode_info+0x157/0x210 + cifs_revalidate_dentry_attr+0x2d1/0x460 + cifs_getattr+0x173/0x470 + vfs_statx_path+0x10f/0x160 + vfs_statx+0xe9/0x150 + vfs_fstatat+0x5e/0xc0 + __do_sys_newfstatat+0x91/0xf0 + do_syscall_64+0x95/0x1a0 + entry_SYSCALL_64_after_hwframe+0x76/0x7e + +Last potentially related work creation: + kasan_save_stack+0x33/0x60 + __kasan_record_aux_stack+0xad/0xc0 + insert_work+0x32/0x100 + __queue_work+0x5c9/0x870 + queue_work_on+0x82/0x90 + open_cached_dir+0x1369/0x1fb0 + smb2_query_path_info+0x43c/0x6e0 + cifs_get_fattr+0x346/0xf10 + cifs_get_inode_info+0x157/0x210 + cifs_revalidate_dentry_attr+0x2d1/0x460 + cifs_getattr+0x173/0x470 + vfs_statx_path+0x10f/0x160 + vfs_statx+0xe9/0x150 + vfs_fstatat+0x5e/0xc0 + __do_sys_newfstatat+0x91/0xf0 + do_syscall_64+0x95/0x1a0 + entry_SYSCALL_64_after_hwframe+0x76/0x7e + +The buggy address belongs to the object at ffff88811cc24c00 + which belongs to the cache kmalloc-1k of size 1024 +The buggy address is located 16 bytes inside of + freed 1024-byte region [ffff88811cc24c00, ffff88811cc25000) + +Cc: stable@vger.kernel.org +Signed-off-by: Paul Aurich +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/cached_dir.c | 70 ++++++++++++++++++--------------------------- + 1 file changed, 29 insertions(+), 41 deletions(-) + +--- a/fs/smb/client/cached_dir.c ++++ b/fs/smb/client/cached_dir.c +@@ -348,6 +348,7 @@ oshr_free: + SMB2_query_info_free(&rqst[1]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); ++out: + if (rc) { + spin_lock(&cfids->cfid_list_lock); + if (cfid->on_list) { +@@ -359,23 +360,14 @@ oshr_free: + /* + * We are guaranteed to have two references at this + * point. One for the caller and one for a potential +- * lease. Release the Lease-ref so that the directory +- * will be closed when the caller closes the cached +- * handle. ++ * lease. Release one here, and the second below. + */ + cfid->has_lease = false; +- spin_unlock(&cfids->cfid_list_lock); + kref_put(&cfid->refcount, smb2_close_cached_fid); +- goto out; + } + spin_unlock(&cfids->cfid_list_lock); +- } +-out: +- if (rc) { +- if (cfid->is_open) +- SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, +- cfid->fid.volatile_fid); +- free_cached_dir(cfid); ++ ++ kref_put(&cfid->refcount, smb2_close_cached_fid); + } else { + *ret_cfid = cfid; + atomic_inc(&tcon->num_remote_opens); +@@ -513,25 +505,24 @@ void invalidate_all_cached_dirs(struct c + cfids->num_entries--; + cfid->is_open = false; + cfid->on_list = false; +- /* To prevent race with smb2_cached_lease_break() */ +- kref_get(&cfid->refcount); ++ if (cfid->has_lease) { ++ /* ++ * The lease was never cancelled from the server, ++ * so steal that reference. ++ */ ++ cfid->has_lease = false; ++ } else ++ kref_get(&cfid->refcount); + } + spin_unlock(&cfids->cfid_list_lock); + + list_for_each_entry_safe(cfid, q, &entry, entry) { + list_del(&cfid->entry); + cancel_work_sync(&cfid->lease_break); +- if (cfid->has_lease) { +- /* +- * We lease was never cancelled from the server so we +- * need to drop the reference. +- */ +- spin_lock(&cfids->cfid_list_lock); +- cfid->has_lease = false; +- spin_unlock(&cfids->cfid_list_lock); +- kref_put(&cfid->refcount, smb2_close_cached_fid); +- } +- /* Drop the extra reference opened above*/ ++ /* ++ * Drop the ref-count from above, either the lease-ref (if there ++ * was one) or the extra one acquired. ++ */ + kref_put(&cfid->refcount, smb2_close_cached_fid); + } + } +@@ -542,9 +533,6 @@ smb2_cached_lease_break(struct work_stru + struct cached_fid *cfid = container_of(work, + struct cached_fid, lease_break); + +- spin_lock(&cfid->cfids->cfid_list_lock); +- cfid->has_lease = false; +- spin_unlock(&cfid->cfids->cfid_list_lock); + kref_put(&cfid->refcount, smb2_close_cached_fid); + } + +@@ -562,6 +550,7 @@ int cached_dir_lease_break(struct cifs_t + !memcmp(lease_key, + cfid->fid.lease_key, + SMB2_LEASE_KEY_SIZE)) { ++ cfid->has_lease = false; + cfid->time = 0; + /* + * We found a lease remove it from the list +@@ -639,8 +628,14 @@ static void cfids_laundromat_worker(stru + cfid->on_list = false; + list_move(&cfid->entry, &entry); + cfids->num_entries--; +- /* To prevent race with smb2_cached_lease_break() */ +- kref_get(&cfid->refcount); ++ if (cfid->has_lease) { ++ /* ++ * Our lease has not yet been cancelled from the ++ * server. Steal that reference. ++ */ ++ cfid->has_lease = false; ++ } else ++ kref_get(&cfid->refcount); + } + } + spin_unlock(&cfids->cfid_list_lock); +@@ -652,17 +647,10 @@ static void cfids_laundromat_worker(stru + * with it. + */ + cancel_work_sync(&cfid->lease_break); +- if (cfid->has_lease) { +- /* +- * Our lease has not yet been cancelled from the server +- * so we need to drop the reference. +- */ +- spin_lock(&cfids->cfid_list_lock); +- cfid->has_lease = false; +- spin_unlock(&cfids->cfid_list_lock); +- kref_put(&cfid->refcount, smb2_close_cached_fid); +- } +- /* Drop the extra reference opened above */ ++ /* ++ * Drop the ref-count from above, either the lease-ref (if there ++ * was one) or the extra one acquired. ++ */ + kref_put(&cfid->refcount, smb2_close_cached_fid); + } + queue_delayed_work(cifsiod_wq, &cfids->laundromat_work, diff --git a/queue-6.11/smb3-request-handle-caching-when-caching-directories.patch b/queue-6.11/smb3-request-handle-caching-when-caching-directories.patch new file mode 100644 index 00000000000..7985770f3ed --- /dev/null +++ b/queue-6.11/smb3-request-handle-caching-when-caching-directories.patch @@ -0,0 +1,33 @@ +From 9ed9d83a51a9636d367c796252409e7b2f4de4d4 Mon Sep 17 00:00:00 2001 +From: Steve French +Date: Mon, 18 Nov 2024 12:19:46 -0600 +Subject: smb3: request handle caching when caching directories + +From: Steve French + +commit 9ed9d83a51a9636d367c796252409e7b2f4de4d4 upstream. + +This client was only requesting READ caching, not READ and HANDLE caching +in the LeaseState on the open requests we send for directories. To +delay closing a handle (e.g. for caching directory contents) we should +be requesting HANDLE as well as READ (as we already do for deferred +close of files). See MS-SMB2 3.3.1.4 e.g. + +Cc: stable@vger.kernel.org +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/smb2ops.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -4078,7 +4078,7 @@ map_oplock_to_lease(u8 oplock) + if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) + return SMB2_LEASE_WRITE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE; + else if (oplock == SMB2_OPLOCK_LEVEL_II) +- return SMB2_LEASE_READ_CACHING_LE; ++ return SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE; + else if (oplock == SMB2_OPLOCK_LEVEL_BATCH) + return SMB2_LEASE_HANDLE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE; diff --git a/queue-6.11/usb-dwc3-ep0-don-t-clear-ep0-dwc3_ep_transfer_started.patch b/queue-6.11/usb-dwc3-ep0-don-t-clear-ep0-dwc3_ep_transfer_started.patch new file mode 100644 index 00000000000..4fea2b561fa --- /dev/null +++ b/queue-6.11/usb-dwc3-ep0-don-t-clear-ep0-dwc3_ep_transfer_started.patch @@ -0,0 +1,35 @@ +From 5d2fb074dea289c41f5aaf2c3f68286bee370634 Mon Sep 17 00:00:00 2001 +From: Thinh Nguyen +Date: Thu, 14 Nov 2024 01:02:06 +0000 +Subject: usb: dwc3: ep0: Don't clear ep0 DWC3_EP_TRANSFER_STARTED + +From: Thinh Nguyen + +commit 5d2fb074dea289c41f5aaf2c3f68286bee370634 upstream. + +The driver cannot issue the End Transfer command to the SETUP transfer. +Don't clear DWC3_EP_TRANSFER_STARTED flag to make sure that the driver +won't send Start Transfer command again, which can cause no-resource +error. For example this can occur if the host issues a reset to the +device. + +Cc: stable@vger.kernel.org +Fixes: 76cb323f80ac ("usb: dwc3: ep0: clear all EP0 flags") +Signed-off-by: Thinh Nguyen +Link: https://lore.kernel.org/r/d3d618185fd614bb7426352a9fc1199641d3b5f5.1731545781.git.Thinh.Nguyen@synopsys.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/dwc3/ep0.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/dwc3/ep0.c ++++ b/drivers/usb/dwc3/ep0.c +@@ -232,7 +232,7 @@ void dwc3_ep0_stall_and_restart(struct d + /* stall is always issued on EP0 */ + dep = dwc->eps[0]; + __dwc3_gadget_ep_set_halt(dep, 1, false); +- dep->flags &= DWC3_EP_RESOURCE_ALLOCATED; ++ dep->flags &= DWC3_EP_RESOURCE_ALLOCATED | DWC3_EP_TRANSFER_STARTED; + dep->flags |= DWC3_EP_ENABLED; + dwc->delayed_status = false; + diff --git a/queue-6.11/usb-dwc3-gadget-fix-checking-for-number-of-trbs-left.patch b/queue-6.11/usb-dwc3-gadget-fix-checking-for-number-of-trbs-left.patch new file mode 100644 index 00000000000..342b9312c50 --- /dev/null +++ b/queue-6.11/usb-dwc3-gadget-fix-checking-for-number-of-trbs-left.patch @@ -0,0 +1,53 @@ +From 02a6982b0ccfcdc39e20016f5fc9a1b7826a6ee7 Mon Sep 17 00:00:00 2001 +From: Thinh Nguyen +Date: Thu, 14 Nov 2024 01:02:12 +0000 +Subject: usb: dwc3: gadget: Fix checking for number of TRBs left + +From: Thinh Nguyen + +commit 02a6982b0ccfcdc39e20016f5fc9a1b7826a6ee7 upstream. + +The check whether the TRB ring is full or empty in dwc3_calc_trbs_left() +is insufficient. It assumes there are active TRBs if there's any request +in the started_list. However, that's not the case for requests with a +large SG list. + +That is, if we have a single usb request that requires more TRBs than +the total TRBs in the TRB ring, the queued TRBs will be available when +all the TRBs in the ring are completed. But the request is only +partially completed and remains in the started_list. With the current +logic, the TRB ring is empty, but dwc3_calc_trbs_left() returns 0. + +Fix this by additionally checking for the request->num_trbs for active +TRB count. + +Cc: stable@vger.kernel.org +Fixes: 51f1954ad853 ("usb: dwc3: gadget: Fix dwc3_calc_trbs_left()") +Signed-off-by: Thinh Nguyen +Link: https://lore.kernel.org/r/708dc62b56b77da1f704cc2ae9b6ddb1f2dbef1f.1731545781.git.Thinh.Nguyen@synopsys.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/dwc3/gadget.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -1177,11 +1177,14 @@ static u32 dwc3_calc_trbs_left(struct dw + * pending to be processed by the driver. + */ + if (dep->trb_enqueue == dep->trb_dequeue) { ++ struct dwc3_request *req; ++ + /* +- * If there is any request remained in the started_list at +- * this point, that means there is no TRB available. ++ * If there is any request remained in the started_list with ++ * active TRBs at this point, then there is no TRB available. + */ +- if (!list_empty(&dep->started_list)) ++ req = next_request(&dep->started_list); ++ if (req && req->num_trbs) + return 0; + + return DWC3_TRB_NUM - 1; diff --git a/queue-6.11/usb-dwc3-gadget-fix-looping-of-queued-sg-entries.patch b/queue-6.11/usb-dwc3-gadget-fix-looping-of-queued-sg-entries.patch new file mode 100644 index 00000000000..50238a3a53b --- /dev/null +++ b/queue-6.11/usb-dwc3-gadget-fix-looping-of-queued-sg-entries.patch @@ -0,0 +1,49 @@ +From b7fc65f5141c24785dc8c19249ca4efcf71b3524 Mon Sep 17 00:00:00 2001 +From: Thinh Nguyen +Date: Thu, 14 Nov 2024 01:02:18 +0000 +Subject: usb: dwc3: gadget: Fix looping of queued SG entries + +From: Thinh Nguyen + +commit b7fc65f5141c24785dc8c19249ca4efcf71b3524 upstream. + +The dwc3_request->num_queued_sgs is decremented on completion. If a +partially completed request is handled, then the +dwc3_request->num_queued_sgs no longer reflects the total number of +num_queued_sgs (it would be cleared). + +Correctly check the number of request SG entries remained to be prepare +and queued. Failure to do this may cause null pointer dereference when +accessing non-existent SG entry. + +Cc: stable@vger.kernel.org +Fixes: c96e6725db9d ("usb: dwc3: gadget: Correct the logic for queuing sgs") +Signed-off-by: Thinh Nguyen +Link: https://lore.kernel.org/r/d07a7c4aa0fcf746cdca0515150dbe5c52000af7.1731545781.git.Thinh.Nguyen@synopsys.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/dwc3/gadget.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -1417,8 +1417,8 @@ static int dwc3_prepare_trbs_sg(struct d + struct scatterlist *s; + int i; + unsigned int length = req->request.length; +- unsigned int remaining = req->request.num_mapped_sgs +- - req->num_queued_sgs; ++ unsigned int remaining = req->num_pending_sgs; ++ unsigned int num_queued_sgs = req->request.num_mapped_sgs - remaining; + unsigned int num_trbs = req->num_trbs; + bool needs_extra_trb = dwc3_needs_extra_trb(dep, req); + +@@ -1426,7 +1426,7 @@ static int dwc3_prepare_trbs_sg(struct d + * If we resume preparing the request, then get the remaining length of + * the request and resume where we left off. + */ +- for_each_sg(req->request.sg, s, req->num_queued_sgs, i) ++ for_each_sg(req->request.sg, s, num_queued_sgs, i) + length -= sg_dma_len(s); + + for_each_sg(sg, s, remaining, i) { diff --git a/queue-6.11/usb-misc-ljca-move-usb_autopm_put_interface-after-wait-for-response.patch b/queue-6.11/usb-misc-ljca-move-usb_autopm_put_interface-after-wait-for-response.patch new file mode 100644 index 00000000000..9c0a6c39ecc --- /dev/null +++ b/queue-6.11/usb-misc-ljca-move-usb_autopm_put_interface-after-wait-for-response.patch @@ -0,0 +1,59 @@ +From 5c5d8eb8af06df615e8b1dc88e5847196c846045 Mon Sep 17 00:00:00 2001 +From: Stanislaw Gruszka +Date: Tue, 12 Nov 2024 08:55:12 +0100 +Subject: usb: misc: ljca: move usb_autopm_put_interface() after wait for response + +From: Stanislaw Gruszka + +commit 5c5d8eb8af06df615e8b1dc88e5847196c846045 upstream. + +Do not mark interface as ready to suspend when we are still waiting +for response messages from the device. + +Fixes: acd6199f195d ("usb: Add support for Intel LJCA device") +Cc: stable@vger.kernel.org +Reviewed-by: Hans de Goede +Tested-by: Hans de Goede # ThinkPad X1 Yoga Gen 8, ov2740 +Acked-by: Sakari Ailus +Signed-off-by: Stanislaw Gruszka +Link: https://lore.kernel.org/r/20241112075514.680712-1-stanislaw.gruszka@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/misc/usb-ljca.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +--- a/drivers/usb/misc/usb-ljca.c ++++ b/drivers/usb/misc/usb-ljca.c +@@ -332,14 +332,11 @@ static int ljca_send(struct ljca_adapter + + ret = usb_bulk_msg(adap->usb_dev, adap->tx_pipe, header, + msg_len, &transferred, LJCA_WRITE_TIMEOUT_MS); +- +- usb_autopm_put_interface(adap->intf); +- + if (ret < 0) +- goto out; ++ goto out_put; + if (transferred != msg_len) { + ret = -EIO; +- goto out; ++ goto out_put; + } + + if (ack) { +@@ -347,11 +344,14 @@ static int ljca_send(struct ljca_adapter + timeout); + if (!ret) { + ret = -ETIMEDOUT; +- goto out; ++ goto out_put; + } + } + ret = adap->actual_length; + ++out_put: ++ usb_autopm_put_interface(adap->intf); ++ + out: + spin_lock_irqsave(&adap->lock, flags); + adap->ex_buf = NULL; diff --git a/queue-6.11/usb-misc-ljca-set-small-runtime-autosuspend-delay.patch b/queue-6.11/usb-misc-ljca-set-small-runtime-autosuspend-delay.patch new file mode 100644 index 00000000000..8e81509cb0d --- /dev/null +++ b/queue-6.11/usb-misc-ljca-set-small-runtime-autosuspend-delay.patch @@ -0,0 +1,79 @@ +From 2481af79671a6603fce201cbbc48f31e488e9fae Mon Sep 17 00:00:00 2001 +From: Stanislaw Gruszka +Date: Tue, 12 Nov 2024 08:55:13 +0100 +Subject: usb: misc: ljca: set small runtime autosuspend delay + +From: Stanislaw Gruszka + +commit 2481af79671a6603fce201cbbc48f31e488e9fae upstream. + +On some Lenovo platforms, the patch works around problems with ov2740 +sensor initialization, which manifest themself like below: + +[ 4.540476] ov2740 i2c-INT3474:01: error -EIO: failed to find sensor +[ 4.542066] ov2740 i2c-INT3474:01: probe with driver ov2740 failed with error -5 + +or + +[ 7.742633] ov2740 i2c-INT3474:01: chip id mismatch: 2740 != 0 +[ 7.742638] ov2740 i2c-INT3474:01: error -ENXIO: failed to find sensor + +and also by random failures of video stream start. + +Issue can be reproduced by this script: + +n=0 +k=0 +while [ $n -lt 50 ] ; do + sudo modprobe -r ov2740 + sleep `expr $RANDOM % 5` + sudo modprobe ov2740 + if media-ctl -p | grep -q ov2740 ; then + let k++ + fi + let n++ +done +echo Success rate $k/$n + +Without the patch, success rate is approximately 15 or 50 tries. +With the patch it does not fail. + +This problem is some hardware or firmware malfunction, that can not be +easy debug and fix. While setting small autosuspend delay is not perfect +workaround as user can configure it to any value, it will prevent +the failures by default. + +Additionally setting small autosuspend delay should have positive effect +on power consumption as for most ljca workloads device is used for just +a few milliseconds flowed by long periods of at least 100ms of inactivity +(usually more). + +Fixes: acd6199f195d ("usb: Add support for Intel LJCA device") +Cc: stable@vger.kernel.org +Reviewed-by: Hans de Goede +Tested-by: Hans de Goede # ThinkPad X1 Yoga Gen 8, ov2740 +Acked-by: Sakari Ailus +Signed-off-by: Stanislaw Gruszka +Link: https://lore.kernel.org/r/20241112075514.680712-2-stanislaw.gruszka@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/misc/usb-ljca.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/usb/misc/usb-ljca.c ++++ b/drivers/usb/misc/usb-ljca.c +@@ -811,6 +811,14 @@ static int ljca_probe(struct usb_interfa + if (ret) + goto err_free; + ++ /* ++ * This works around problems with ov2740 initialization on some ++ * Lenovo platforms. The autosuspend delay, has to be smaller than ++ * the delay after setting the reset_gpio line in ov2740_resume(). ++ * Otherwise the sensor randomly fails to initialize. ++ */ ++ pm_runtime_set_autosuspend_delay(&usb_dev->dev, 10); ++ + usb_enable_autosuspend(usb_dev); + + return 0; diff --git a/queue-6.11/usb-musb-fix-hardware-lockup-on-first-rx-endpoint-request.patch b/queue-6.11/usb-musb-fix-hardware-lockup-on-first-rx-endpoint-request.patch new file mode 100644 index 00000000000..f2343f7032d --- /dev/null +++ b/queue-6.11/usb-musb-fix-hardware-lockup-on-first-rx-endpoint-request.patch @@ -0,0 +1,133 @@ +From 3fc137386c4620305bbc2a216868c53f9245670a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Hubert=20Wi=C5=9Bniewski?= + +Date: Sun, 10 Nov 2024 18:21:48 +0100 +Subject: usb: musb: Fix hardware lockup on first Rx endpoint request +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Hubert Wiśniewski + +commit 3fc137386c4620305bbc2a216868c53f9245670a upstream. + +There is a possibility that a request's callback could be invoked from +usb_ep_queue() (call trace below, supplemented with missing calls): + +req->complete from usb_gadget_giveback_request + (drivers/usb/gadget/udc/core.c:999) +usb_gadget_giveback_request from musb_g_giveback + (drivers/usb/musb/musb_gadget.c:147) +musb_g_giveback from rxstate + (drivers/usb/musb/musb_gadget.c:784) +rxstate from musb_ep_restart + (drivers/usb/musb/musb_gadget.c:1169) +musb_ep_restart from musb_ep_restart_resume_work + (drivers/usb/musb/musb_gadget.c:1176) +musb_ep_restart_resume_work from musb_queue_resume_work + (drivers/usb/musb/musb_core.c:2279) +musb_queue_resume_work from musb_gadget_queue + (drivers/usb/musb/musb_gadget.c:1241) +musb_gadget_queue from usb_ep_queue + (drivers/usb/gadget/udc/core.c:300) + +According to the docstring of usb_ep_queue(), this should not happen: + +"Note that @req's ->complete() callback must never be called from within +usb_ep_queue() as that can create deadlock situations." + +In fact, a hardware lockup might occur in the following sequence: + +1. The gadget is initialized using musb_gadget_enable(). +2. Meanwhile, a packet arrives, and the RXPKTRDY flag is set, raising an + interrupt. +3. If IRQs are enabled, the interrupt is handled, but musb_g_rx() finds an + empty queue (next_request() returns NULL). The interrupt flag has + already been cleared by the glue layer handler, but the RXPKTRDY flag + remains set. +4. The first request is enqueued using usb_ep_queue(), leading to the call + of req->complete(), as shown in the call trace above. +5. If the callback enables IRQs and another packet is waiting, step (3) + repeats. The request queue is empty because usb_g_giveback() removes the + request before invoking the callback. +6. The endpoint remains locked up, as the interrupt triggered by hardware + setting the RXPKTRDY flag has been handled, but the flag itself remains + set. + +For this scenario to occur, it is only necessary for IRQs to be enabled at +some point during the complete callback. This happens with the USB Ethernet +gadget, whose rx_complete() callback calls netif_rx(). If called in the +task context, netif_rx() disables the bottom halves (BHs). When the BHs are +re-enabled, IRQs are also enabled to allow soft IRQs to be processed. The +gadget itself is initialized at module load (or at boot if built-in), but +the first request is enqueued when the network interface is brought up, +triggering rx_complete() in the task context via ioctl(). If a packet +arrives while the interface is down, it can prevent the interface from +receiving any further packets from the USB host. + +The situation is quite complicated with many parties involved. This +particular issue can be resolved in several possible ways: + +1. Ensure that callbacks never enable IRQs. This would be difficult to + enforce, as discovering how netif_rx() interacts with interrupts was + already quite challenging and u_ether is not the only function driver. + Similar "bugs" could be hidden in other drivers as well. +2. Disable MUSB interrupts in musb_g_giveback() before calling the callback + and re-enable them afterwars (by calling musb_{dis,en}able_interrupts(), + for example). This would ensure that MUSB interrupts are not handled + during the callback, even if IRQs are enabled. In fact, it would allow + IRQs to be enabled when releasing the lock. However, this feels like an + inelegant hack. +3. Modify the interrupt handler to clear the RXPKTRDY flag if the request + queue is empty. While this approach also feels like a hack, it wastes + CPU time by attempting to handle incoming packets when the software is + not ready to process them. +4. Flush the Rx FIFO instead of calling rxstate() in musb_ep_restart(). + This ensures that the hardware can receive packets when there is at + least one request in the queue. Once IRQs are enabled, the interrupt + handler will be able to correctly process the next incoming packet + (eventually calling rxstate()). This approach may cause one or two + packets to be dropped (two if double buffering is enabled), but this + seems to be a minor issue, as packet loss can occur when the software is + not yet ready to process them. Additionally, this solution makes the + gadget driver compliant with the rule mentioned in the docstring of + usb_ep_queue(). + +There may be additional solutions, but from these four, the last one has +been chosen as it seems to be the most appropriate, as it addresses the +"bad" behavior of the driver. + +Fixes: baebdf48c360 ("net: dev: Makes sure netif_rx() can be invoked in any context.") +Cc: stable@vger.kernel.org +Signed-off-by: Hubert Wiśniewski +Link: https://lore.kernel.org/r/4ee1ead4525f78fb5909a8cbf99513ad0082ad21.camel@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/musb/musb_gadget.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +--- a/drivers/usb/musb/musb_gadget.c ++++ b/drivers/usb/musb/musb_gadget.c +@@ -1161,12 +1161,19 @@ void musb_free_request(struct usb_ep *ep + */ + void musb_ep_restart(struct musb *musb, struct musb_request *req) + { ++ u16 csr; ++ void __iomem *epio = req->ep->hw_ep->regs; ++ + trace_musb_req_start(req); + musb_ep_select(musb->mregs, req->epnum); +- if (req->tx) ++ if (req->tx) { + txstate(musb, req); +- else +- rxstate(musb, req); ++ } else { ++ csr = musb_readw(epio, MUSB_RXCSR); ++ csr |= MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_P_WZC_BITS; ++ musb_writew(epio, MUSB_RXCSR, csr); ++ musb_writew(epio, MUSB_RXCSR, csr); ++ } + } + + static int musb_ep_restart_resume_work(struct musb *musb, void *data) -- 2.47.3