From: Greg Kroah-Hartman Date: Sun, 24 Aug 2025 09:12:12 +0000 (+0200) Subject: 5.4-stable patches X-Git-Tag: v5.4.297~26 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0ab098ccab9680e4c8c578b3a0d616b94ef139f5;p=thirdparty%2Fkernel%2Fstable-queue.git 5.4-stable patches added patches: bluetooth-fix-use-after-free-in-device_for_each_child.patch cifs-fix-uaf-in-cifs_demultiplex_thread.patch nfs-fix-uaf-in-direct-writes.patch nfs-fix-up-commit-deadlocks.patch --- diff --git a/queue-5.4/bluetooth-fix-use-after-free-in-device_for_each_child.patch b/queue-5.4/bluetooth-fix-use-after-free-in-device_for_each_child.patch new file mode 100644 index 0000000000..7fa97abadb --- /dev/null +++ b/queue-5.4/bluetooth-fix-use-after-free-in-device_for_each_child.patch @@ -0,0 +1,149 @@ +From 27aabf27fd014ae037cc179c61b0bee7cff55b3d Mon Sep 17 00:00:00 2001 +From: Dmitry Antipov +Date: Fri, 1 Nov 2024 14:44:10 +0300 +Subject: Bluetooth: fix use-after-free in device_for_each_child() + +From: Dmitry Antipov + +commit 27aabf27fd014ae037cc179c61b0bee7cff55b3d upstream. + +Syzbot has reported the following KASAN splat: + +BUG: KASAN: slab-use-after-free in device_for_each_child+0x18f/0x1a0 +Read of size 8 at addr ffff88801f605308 by task kbnepd bnep0/4980 + +CPU: 0 UID: 0 PID: 4980 Comm: kbnepd bnep0 Not tainted 6.12.0-rc4-00161-gae90f6a6170d #1 +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-2.fc40 04/01/2014 +Call Trace: + + dump_stack_lvl+0x100/0x190 + ? device_for_each_child+0x18f/0x1a0 + print_report+0x13a/0x4cb + ? __virt_addr_valid+0x5e/0x590 + ? __phys_addr+0xc6/0x150 + ? device_for_each_child+0x18f/0x1a0 + kasan_report+0xda/0x110 + ? device_for_each_child+0x18f/0x1a0 + ? __pfx_dev_memalloc_noio+0x10/0x10 + device_for_each_child+0x18f/0x1a0 + ? __pfx_device_for_each_child+0x10/0x10 + pm_runtime_set_memalloc_noio+0xf2/0x180 + netdev_unregister_kobject+0x1ed/0x270 + unregister_netdevice_many_notify+0x123c/0x1d80 + ? __mutex_trylock_common+0xde/0x250 + ? __pfx_unregister_netdevice_many_notify+0x10/0x10 + ? trace_contention_end+0xe6/0x140 + ? __mutex_lock+0x4e7/0x8f0 + ? __pfx_lock_acquire.part.0+0x10/0x10 + ? rcu_is_watching+0x12/0xc0 + ? unregister_netdev+0x12/0x30 + unregister_netdevice_queue+0x30d/0x3f0 + ? __pfx_unregister_netdevice_queue+0x10/0x10 + ? __pfx_down_write+0x10/0x10 + unregister_netdev+0x1c/0x30 + bnep_session+0x1fb3/0x2ab0 + ? __pfx_bnep_session+0x10/0x10 + ? __pfx_lock_release+0x10/0x10 + ? __pfx_woken_wake_function+0x10/0x10 + ? __kthread_parkme+0x132/0x200 + ? __pfx_bnep_session+0x10/0x10 + ? kthread+0x13a/0x370 + ? __pfx_bnep_session+0x10/0x10 + kthread+0x2b7/0x370 + ? __pfx_kthread+0x10/0x10 + ret_from_fork+0x48/0x80 + ? __pfx_kthread+0x10/0x10 + ret_from_fork_asm+0x1a/0x30 + + +Allocated by task 4974: + kasan_save_stack+0x30/0x50 + kasan_save_track+0x14/0x30 + __kasan_kmalloc+0xaa/0xb0 + __kmalloc_noprof+0x1d1/0x440 + hci_alloc_dev_priv+0x1d/0x2820 + __vhci_create_device+0xef/0x7d0 + vhci_write+0x2c7/0x480 + vfs_write+0x6a0/0xfc0 + ksys_write+0x12f/0x260 + do_syscall_64+0xc7/0x250 + entry_SYSCALL_64_after_hwframe+0x77/0x7f + +Freed by task 4979: + kasan_save_stack+0x30/0x50 + kasan_save_track+0x14/0x30 + kasan_save_free_info+0x3b/0x60 + __kasan_slab_free+0x4f/0x70 + kfree+0x141/0x490 + hci_release_dev+0x4d9/0x600 + bt_host_release+0x6a/0xb0 + device_release+0xa4/0x240 + kobject_put+0x1ec/0x5a0 + put_device+0x1f/0x30 + vhci_release+0x81/0xf0 + __fput+0x3f6/0xb30 + task_work_run+0x151/0x250 + do_exit+0xa79/0x2c30 + do_group_exit+0xd5/0x2a0 + get_signal+0x1fcd/0x2210 + arch_do_signal_or_restart+0x93/0x780 + syscall_exit_to_user_mode+0x140/0x290 + do_syscall_64+0xd4/0x250 + entry_SYSCALL_64_after_hwframe+0x77/0x7f + +In 'hci_conn_del_sysfs()', 'device_unregister()' may be called when +an underlying (kobject) reference counter is greater than 1. This +means that reparenting (happened when the device is actually freed) +is delayed and, during that delay, parent controller device (hciX) +may be deleted. Since the latter may create a dangling pointer to +freed parent, avoid that scenario by reparenting to NULL explicitly. + +Reported-by: syzbot+6cf5652d3df49fae2e3f@syzkaller.appspotmail.com +Tested-by: syzbot+6cf5652d3df49fae2e3f@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=6cf5652d3df49fae2e3f +Fixes: a85fb91e3d72 ("Bluetooth: Fix double free in hci_conn_cleanup") +Signed-off-by: Dmitry Antipov +Signed-off-by: Luiz Augusto von Dentz +[ chanho: Backported from v5.10.y to v5.4.y. device_find_any_child() is not +supported in v5.4.y, so changed to use device_find_child() with __match_any ] +Signed-off-by: Chanho Min +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/hci_sysfs.c | 15 ++++++--------- + 1 file changed, 6 insertions(+), 9 deletions(-) + +--- a/net/bluetooth/hci_sysfs.c ++++ b/net/bluetooth/hci_sysfs.c +@@ -19,14 +19,9 @@ static const struct device_type bt_link + .release = bt_link_release, + }; + +-/* +- * The rfcomm tty device will possibly retain even when conn +- * is down, and sysfs doesn't support move zombie device, +- * so we should move the device before conn device is destroyed. +- */ +-static int __match_tty(struct device *dev, void *data) ++static int __match_any(struct device *dev, void *unused) + { +- return !strncmp(dev_name(dev), "rfcomm", 6); ++ return 1; + } + + void hci_conn_init_sysfs(struct hci_conn *conn) +@@ -71,10 +66,12 @@ void hci_conn_del_sysfs(struct hci_conn + return; + } + ++ /* If there are devices using the connection as parent reset it to NULL ++ * before unregistering the device. ++ */ + while (1) { + struct device *dev; +- +- dev = device_find_child(&conn->dev, NULL, __match_tty); ++ dev = device_find_child(&conn->dev, NULL, __match_any); + if (!dev) + break; + device_move(dev, NULL, DPM_ORDER_DEV_LAST); diff --git a/queue-5.4/cifs-fix-uaf-in-cifs_demultiplex_thread.patch b/queue-5.4/cifs-fix-uaf-in-cifs_demultiplex_thread.patch new file mode 100644 index 0000000000..12a24b9d27 --- /dev/null +++ b/queue-5.4/cifs-fix-uaf-in-cifs_demultiplex_thread.patch @@ -0,0 +1,251 @@ +From d527f51331cace562393a8038d870b3e9916686f Mon Sep 17 00:00:00 2001 +From: Zhang Xiaoxu +Date: Tue, 19 Sep 2023 13:38:04 -0500 +Subject: cifs: Fix UAF in cifs_demultiplex_thread() + +From: Zhang Xiaoxu + +commit d527f51331cace562393a8038d870b3e9916686f upstream. + +There is a UAF when xfstests on cifs: + + BUG: KASAN: use-after-free in smb2_is_network_name_deleted+0x27/0x160 + Read of size 4 at addr ffff88810103fc08 by task cifsd/923 + + CPU: 1 PID: 923 Comm: cifsd Not tainted 6.1.0-rc4+ #45 + ... + Call Trace: + + dump_stack_lvl+0x34/0x44 + print_report+0x171/0x472 + kasan_report+0xad/0x130 + kasan_check_range+0x145/0x1a0 + smb2_is_network_name_deleted+0x27/0x160 + cifs_demultiplex_thread.cold+0x172/0x5a4 + kthread+0x165/0x1a0 + ret_from_fork+0x1f/0x30 + + + Allocated by task 923: + kasan_save_stack+0x1e/0x40 + kasan_set_track+0x21/0x30 + __kasan_slab_alloc+0x54/0x60 + kmem_cache_alloc+0x147/0x320 + mempool_alloc+0xe1/0x260 + cifs_small_buf_get+0x24/0x60 + allocate_buffers+0xa1/0x1c0 + cifs_demultiplex_thread+0x199/0x10d0 + kthread+0x165/0x1a0 + ret_from_fork+0x1f/0x30 + + Freed by task 921: + kasan_save_stack+0x1e/0x40 + kasan_set_track+0x21/0x30 + kasan_save_free_info+0x2a/0x40 + ____kasan_slab_free+0x143/0x1b0 + kmem_cache_free+0xe3/0x4d0 + cifs_small_buf_release+0x29/0x90 + SMB2_negotiate+0x8b7/0x1c60 + smb2_negotiate+0x51/0x70 + cifs_negotiate_protocol+0xf0/0x160 + cifs_get_smb_ses+0x5fa/0x13c0 + mount_get_conns+0x7a/0x750 + cifs_mount+0x103/0xd00 + cifs_smb3_do_mount+0x1dd/0xcb0 + smb3_get_tree+0x1d5/0x300 + vfs_get_tree+0x41/0xf0 + path_mount+0x9b3/0xdd0 + __x64_sys_mount+0x190/0x1d0 + do_syscall_64+0x35/0x80 + entry_SYSCALL_64_after_hwframe+0x46/0xb0 + +The UAF is because: + + mount(pid: 921) | cifsd(pid: 923) +-------------------------------|------------------------------- + | cifs_demultiplex_thread +SMB2_negotiate | + cifs_send_recv | + compound_send_recv | + smb_send_rqst | + wait_for_response | + wait_event_state [1] | + | standard_receive3 + | cifs_handle_standard + | handle_mid + | mid->resp_buf = buf; [2] + | dequeue_mid [3] + KILL the process [4] | + resp_iov[i].iov_base = buf | + free_rsp_buf [5] | + | is_network_name_deleted [6] + | callback + +1. After send request to server, wait the response until + mid->mid_state != SUBMITTED; +2. Receive response from server, and set it to mid; +3. Set the mid state to RECEIVED; +4. Kill the process, the mid state already RECEIVED, get 0; +5. Handle and release the negotiate response; +6. UAF. + +It can be easily reproduce with add some delay in [3] - [6]. + +Only sync call has the problem since async call's callback is +executed in cifsd process. + +Add an extra state to mark the mid state to READY before wakeup the +waitter, then it can get the resp safely. + +Fixes: ec637e3ffb6b ("[CIFS] Avoid extra large buffer allocation (and memcpy) in cifs_readpages") +Reviewed-by: Paulo Alcantara (SUSE) +Signed-off-by: Zhang Xiaoxu +Signed-off-by: Steve French +[fs/cifs was moved to fs/smb/client since +38c8a9a52082 ("smb: move client and server files to common directory fs/smb"). +We apply the patch to fs/cifs with some minor context changes.] +Signed-off-by: He Zhe +Signed-off-by: Xiangyu Chen +[ chanho: Backported to v5.4.y ] +Signed-off-by: Chanho Min +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/cifsglob.h | 1 + + fs/cifs/transport.c | 34 +++++++++++++++++++++++----------- + 2 files changed, 24 insertions(+), 11 deletions(-) + +--- a/fs/cifs/cifsglob.h ++++ b/fs/cifs/cifsglob.h +@@ -1722,6 +1722,7 @@ static inline bool is_retryable_error(in + #define MID_RETRY_NEEDED 8 /* session closed while this request out */ + #define MID_RESPONSE_MALFORMED 0x10 + #define MID_SHUTDOWN 0x20 ++#define MID_RESPONSE_READY 0x40 /* ready for other process handle the rsp */ + + /* Flags */ + #define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */ +--- a/fs/cifs/transport.c ++++ b/fs/cifs/transport.c +@@ -47,6 +47,8 @@ + void + cifs_wake_up_task(struct mid_q_entry *mid) + { ++ if (mid->mid_state == MID_RESPONSE_RECEIVED) ++ mid->mid_state = MID_RESPONSE_READY; + wake_up_process(mid->callback_data); + } + +@@ -99,7 +101,8 @@ static void _cifs_mid_q_entry_release(st + struct TCP_Server_Info *server = midEntry->server; + + if (midEntry->resp_buf && (midEntry->mid_flags & MID_WAIT_CANCELLED) && +- midEntry->mid_state == MID_RESPONSE_RECEIVED && ++ (midEntry->mid_state == MID_RESPONSE_RECEIVED || ++ midEntry->mid_state == MID_RESPONSE_READY) && + server->ops->handle_cancelled_mid) + server->ops->handle_cancelled_mid(midEntry->resp_buf, server); + +@@ -737,7 +740,8 @@ wait_for_response(struct TCP_Server_Info + int error; + + error = wait_event_freezekillable_unsafe(server->response_q, +- midQ->mid_state != MID_REQUEST_SUBMITTED); ++ midQ->mid_state != MID_REQUEST_SUBMITTED && ++ midQ->mid_state != MID_RESPONSE_RECEIVED); + if (error < 0) + return -ERESTARTSYS; + +@@ -889,7 +893,7 @@ cifs_sync_mid_result(struct mid_q_entry + + spin_lock(&GlobalMid_Lock); + switch (mid->mid_state) { +- case MID_RESPONSE_RECEIVED: ++ case MID_RESPONSE_READY: + spin_unlock(&GlobalMid_Lock); + return rc; + case MID_RETRY_NEEDED: +@@ -987,6 +991,9 @@ cifs_compound_callback(struct mid_q_entr + credits.instance = server->reconnect_instance; + + add_credits(server, &credits, mid->optype); ++ ++ if (mid->mid_state == MID_RESPONSE_RECEIVED) ++ mid->mid_state = MID_RESPONSE_READY; + } + + static void +@@ -1150,7 +1157,8 @@ compound_send_recv(const unsigned int xi + send_cancel(server, &rqst[i], midQ[i]); + spin_lock(&GlobalMid_Lock); + midQ[i]->mid_flags |= MID_WAIT_CANCELLED; +- if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) { ++ if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED || ++ midQ[i]->mid_state == MID_RESPONSE_RECEIVED) { + midQ[i]->callback = cifs_cancelled_callback; + cancelled_mid[i] = true; + credits[i].value = 0; +@@ -1171,7 +1179,7 @@ compound_send_recv(const unsigned int xi + } + + if (!midQ[i]->resp_buf || +- midQ[i]->mid_state != MID_RESPONSE_RECEIVED) { ++ midQ[i]->mid_state != MID_RESPONSE_READY) { + rc = -EIO; + cifs_dbg(FYI, "Bad MID state?\n"); + goto out; +@@ -1348,7 +1356,8 @@ SendReceive(const unsigned int xid, stru + if (rc != 0) { + send_cancel(server, &rqst, midQ); + spin_lock(&GlobalMid_Lock); +- if (midQ->mid_state == MID_REQUEST_SUBMITTED) { ++ if (midQ->mid_state == MID_REQUEST_SUBMITTED || ++ midQ->mid_state == MID_RESPONSE_RECEIVED) { + /* no longer considered to be "in-flight" */ + midQ->callback = DeleteMidQEntry; + spin_unlock(&GlobalMid_Lock); +@@ -1365,7 +1374,7 @@ SendReceive(const unsigned int xid, stru + } + + if (!midQ->resp_buf || !out_buf || +- midQ->mid_state != MID_RESPONSE_RECEIVED) { ++ midQ->mid_state != MID_RESPONSE_READY) { + rc = -EIO; + cifs_server_dbg(VFS, "Bad MID state?\n"); + goto out; +@@ -1485,13 +1494,15 @@ SendReceiveBlockingLock(const unsigned i + + /* Wait for a reply - allow signals to interrupt. */ + rc = wait_event_interruptible(server->response_q, +- (!(midQ->mid_state == MID_REQUEST_SUBMITTED)) || ++ (!(midQ->mid_state == MID_REQUEST_SUBMITTED || ++ midQ->mid_state == MID_RESPONSE_RECEIVED)) || + ((server->tcpStatus != CifsGood) && + (server->tcpStatus != CifsNew))); + + /* Were we interrupted by a signal ? */ + if ((rc == -ERESTARTSYS) && +- (midQ->mid_state == MID_REQUEST_SUBMITTED) && ++ (midQ->mid_state == MID_REQUEST_SUBMITTED || ++ midQ->mid_state == MID_RESPONSE_RECEIVED) && + ((server->tcpStatus == CifsGood) || + (server->tcpStatus == CifsNew))) { + +@@ -1521,7 +1532,8 @@ SendReceiveBlockingLock(const unsigned i + if (rc) { + send_cancel(server, &rqst, midQ); + spin_lock(&GlobalMid_Lock); +- if (midQ->mid_state == MID_REQUEST_SUBMITTED) { ++ if (midQ->mid_state == MID_REQUEST_SUBMITTED || ++ midQ->mid_state == MID_RESPONSE_RECEIVED) { + /* no longer considered to be "in-flight" */ + midQ->callback = DeleteMidQEntry; + spin_unlock(&GlobalMid_Lock); +@@ -1539,7 +1551,7 @@ SendReceiveBlockingLock(const unsigned i + return rc; + + /* rcvd frame is ok */ +- if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) { ++ if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_READY) { + rc = -EIO; + cifs_tcon_dbg(VFS, "Bad MID state?\n"); + goto out; diff --git a/queue-5.4/nfs-fix-uaf-in-direct-writes.patch b/queue-5.4/nfs-fix-uaf-in-direct-writes.patch new file mode 100644 index 0000000000..3bcee1e1a4 --- /dev/null +++ b/queue-5.4/nfs-fix-uaf-in-direct-writes.patch @@ -0,0 +1,120 @@ +From 17f46b803d4f23c66cacce81db35fef3adb8f2af Mon Sep 17 00:00:00 2001 +From: Josef Bacik +Date: Fri, 1 Mar 2024 11:49:57 -0500 +Subject: nfs: fix UAF in direct writes + +From: Josef Bacik + +commit 17f46b803d4f23c66cacce81db35fef3adb8f2af upstream. + +In production we have been hitting the following warning consistently + +------------[ cut here ]------------ +refcount_t: underflow; use-after-free. +WARNING: CPU: 17 PID: 1800359 at lib/refcount.c:28 refcount_warn_saturate+0x9c/0xe0 +Workqueue: nfsiod nfs_direct_write_schedule_work [nfs] +RIP: 0010:refcount_warn_saturate+0x9c/0xe0 +PKRU: 55555554 +Call Trace: + + ? __warn+0x9f/0x130 + ? refcount_warn_saturate+0x9c/0xe0 + ? report_bug+0xcc/0x150 + ? handle_bug+0x3d/0x70 + ? exc_invalid_op+0x16/0x40 + ? asm_exc_invalid_op+0x16/0x20 + ? refcount_warn_saturate+0x9c/0xe0 + nfs_direct_write_schedule_work+0x237/0x250 [nfs] + process_one_work+0x12f/0x4a0 + worker_thread+0x14e/0x3b0 + ? ZSTD_getCParams_internal+0x220/0x220 + kthread+0xdc/0x120 + ? __btf_name_valid+0xa0/0xa0 + ret_from_fork+0x1f/0x30 + +This is because we're completing the nfs_direct_request twice in a row. + +The source of this is when we have our commit requests to submit, we +process them and send them off, and then in the completion path for the +commit requests we have + +if (nfs_commit_end(cinfo.mds)) + nfs_direct_write_complete(dreq); + +However since we're submitting asynchronous requests we sometimes have +one that completes before we submit the next one, so we end up calling +complete on the nfs_direct_request twice. + +The only other place we use nfs_generic_commit_list() is in +__nfs_commit_inode, which wraps this call in a + +nfs_commit_begin(); +nfs_commit_end(); + +Which is a common pattern for this style of completion handling, one +that is also repeated in the direct code with get_dreq()/put_dreq() +calls around where we process events as well as in the completion paths. + +Fix this by using the same pattern for the commit requests. + +Before with my 200 node rocksdb stress running this warning would pop +every 10ish minutes. With my patch the stress test has been running for +several hours without popping. + +Signed-off-by: Josef Bacik +Cc: stable@vger.kernel.org +Signed-off-by: Trond Myklebust +[ chanho : Backports v5.4.y, commit 133a48abf6ec (NFS: Fix up commit deadlocks) + is needed to use nfs_commit_end ] +Signed-off-by: Chanho Min +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/nfs/direct.c | 11 +++++++++-- + fs/nfs/write.c | 2 +- + include/linux/nfs_fs.h | 1 + + 3 files changed, 11 insertions(+), 3 deletions(-) + +--- a/fs/nfs/direct.c ++++ b/fs/nfs/direct.c +@@ -727,10 +727,17 @@ static void nfs_direct_commit_schedule(s + LIST_HEAD(mds_list); + + nfs_init_cinfo_from_dreq(&cinfo, dreq); ++ nfs_commit_begin(cinfo.mds); + nfs_scan_commit(dreq->inode, &mds_list, &cinfo); + res = nfs_generic_commit_list(dreq->inode, &mds_list, 0, &cinfo); +- if (res < 0) /* res == -ENOMEM */ +- nfs_direct_write_reschedule(dreq); ++ if (res < 0) { /* res == -ENOMEM */ ++ spin_lock(&dreq->lock); ++ if (dreq->flags == 0) ++ dreq->flags = NFS_ODIRECT_RESCHED_WRITES; ++ spin_unlock(&dreq->lock); ++ } ++ if (nfs_commit_end(cinfo.mds)) ++ nfs_direct_write_complete(dreq); + } + + static void nfs_direct_write_schedule_work(struct work_struct *work) +--- a/fs/nfs/write.c ++++ b/fs/nfs/write.c +@@ -1653,7 +1653,7 @@ static int wait_on_commit(struct nfs_mds + !atomic_read(&cinfo->rpcs_out)); + } + +-static void nfs_commit_begin(struct nfs_mds_commit_info *cinfo) ++void nfs_commit_begin(struct nfs_mds_commit_info *cinfo) + { + atomic_inc(&cinfo->rpcs_out); + } +--- a/include/linux/nfs_fs.h ++++ b/include/linux/nfs_fs.h +@@ -549,6 +549,7 @@ extern int nfs_wb_page_cancel(struct ino + extern int nfs_commit_inode(struct inode *, int); + extern struct nfs_commit_data *nfs_commitdata_alloc(bool never_fail); + extern void nfs_commit_free(struct nfs_commit_data *data); ++void nfs_commit_begin(struct nfs_mds_commit_info *cinfo); + bool nfs_commit_end(struct nfs_mds_commit_info *cinfo); + + static inline int diff --git a/queue-5.4/nfs-fix-up-commit-deadlocks.patch b/queue-5.4/nfs-fix-up-commit-deadlocks.patch new file mode 100644 index 0000000000..ab39defbd2 --- /dev/null +++ b/queue-5.4/nfs-fix-up-commit-deadlocks.patch @@ -0,0 +1,79 @@ +From 133a48abf6ecc535d7eddc6da1c3e4c972445882 Mon Sep 17 00:00:00 2001 +From: Trond Myklebust +Date: Mon, 4 Oct 2021 15:37:42 -0400 +Subject: NFS: Fix up commit deadlocks + +From: Trond Myklebust + +commit 133a48abf6ecc535d7eddc6da1c3e4c972445882 upstream. + +If O_DIRECT bumps the commit_info rpcs_out field, then that could lead +to fsync() hangs. The fix is to ensure that O_DIRECT calls +nfs_commit_end(). + +Fixes: 723c921e7dfc ("sched/wait, fs/nfs: Convert wait_on_atomic_t() usage to the new wait_var_event() API") +Signed-off-by: Trond Myklebust +Signed-off-by: Chanho Min +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/nfs/direct.c | 2 +- + fs/nfs/write.c | 9 ++++++--- + include/linux/nfs_fs.h | 1 + + 3 files changed, 8 insertions(+), 4 deletions(-) + +--- a/fs/nfs/direct.c ++++ b/fs/nfs/direct.c +@@ -700,7 +700,7 @@ static void nfs_direct_commit_complete(s + nfs_unlock_and_release_request(req); + } + +- if (atomic_dec_and_test(&cinfo.mds->rpcs_out)) ++ if (nfs_commit_end(cinfo.mds)) + nfs_direct_write_complete(dreq); + } + +--- a/fs/nfs/write.c ++++ b/fs/nfs/write.c +@@ -1658,10 +1658,13 @@ static void nfs_commit_begin(struct nfs_ + atomic_inc(&cinfo->rpcs_out); + } + +-static void nfs_commit_end(struct nfs_mds_commit_info *cinfo) ++bool nfs_commit_end(struct nfs_mds_commit_info *cinfo) + { +- if (atomic_dec_and_test(&cinfo->rpcs_out)) ++ if (atomic_dec_and_test(&cinfo->rpcs_out)) { + wake_up_var(&cinfo->rpcs_out); ++ return true; ++ } ++ return false; + } + + void nfs_commitdata_release(struct nfs_commit_data *data) +@@ -1756,6 +1759,7 @@ void nfs_init_commit(struct nfs_commit_d + data->res.fattr = &data->fattr; + data->res.verf = &data->verf; + nfs_fattr_init(&data->fattr); ++ nfs_commit_begin(cinfo->mds); + } + EXPORT_SYMBOL_GPL(nfs_init_commit); + +@@ -1801,7 +1805,6 @@ nfs_commit_list(struct inode *inode, str + + /* Set up the argument struct */ + nfs_init_commit(data, head, NULL, cinfo); +- atomic_inc(&cinfo->mds->rpcs_out); + return nfs_initiate_commit(NFS_CLIENT(inode), data, NFS_PROTO(inode), + data->mds_ops, how, 0); + } +--- a/include/linux/nfs_fs.h ++++ b/include/linux/nfs_fs.h +@@ -549,6 +549,7 @@ extern int nfs_wb_page_cancel(struct ino + extern int nfs_commit_inode(struct inode *, int); + extern struct nfs_commit_data *nfs_commitdata_alloc(bool never_fail); + extern void nfs_commit_free(struct nfs_commit_data *data); ++bool nfs_commit_end(struct nfs_mds_commit_info *cinfo); + + static inline int + nfs_have_writebacks(struct inode *inode) diff --git a/queue-5.4/series b/queue-5.4/series index d7177f564b..23d586ce6b 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -388,3 +388,7 @@ selftests-forwarding-tc_actions.sh-add-matchall-mirror-test.patch net-sched-act_mirred-refactor-the-handle-of-xmit.patch net-sched-act_mirred-better-wording-on-protection-against-excessive-stack-growth.patch act_mirred-use-the-backlog-for-nested-calls-to-mirred-ingress.patch +bluetooth-fix-use-after-free-in-device_for_each_child.patch +cifs-fix-uaf-in-cifs_demultiplex_thread.patch +nfs-fix-up-commit-deadlocks.patch +nfs-fix-uaf-in-direct-writes.patch