]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 24 Aug 2025 09:12:12 +0000 (11:12 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 24 Aug 2025 09:12:12 +0000 (11:12 +0200)
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

queue-5.4/bluetooth-fix-use-after-free-in-device_for_each_child.patch [new file with mode: 0644]
queue-5.4/cifs-fix-uaf-in-cifs_demultiplex_thread.patch [new file with mode: 0644]
queue-5.4/nfs-fix-uaf-in-direct-writes.patch [new file with mode: 0644]
queue-5.4/nfs-fix-up-commit-deadlocks.patch [new file with mode: 0644]
queue-5.4/series

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 (file)
index 0000000..7fa97ab
--- /dev/null
@@ -0,0 +1,149 @@
+From 27aabf27fd014ae037cc179c61b0bee7cff55b3d Mon Sep 17 00:00:00 2001
+From: Dmitry Antipov <dmantipov@yandex.ru>
+Date: Fri, 1 Nov 2024 14:44:10 +0300
+Subject: Bluetooth: fix use-after-free in device_for_each_child()
+
+From: Dmitry Antipov <dmantipov@yandex.ru>
+
+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:
+ <TASK>
+ 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
+ </TASK>
+
+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 <dmantipov@yandex.ru>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+[ 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 <chanho.min@lge.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..12a24b9
--- /dev/null
@@ -0,0 +1,251 @@
+From d527f51331cace562393a8038d870b3e9916686f Mon Sep 17 00:00:00 2001
+From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
+Date: Tue, 19 Sep 2023 13:38:04 -0500
+Subject: cifs: Fix UAF in cifs_demultiplex_thread()
+
+From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
+
+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:
+   <TASK>
+   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
+   </TASK>
+
+  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) <pc@manguebit.com>
+Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+[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 <zhe.he@windriver.com>
+Signed-off-by: Xiangyu Chen <xiangyu.chen@windriver.com>
+[ chanho: Backported to v5.4.y ]
+Signed-off-by: Chanho Min <chanho.min@lge.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..3bcee1e
--- /dev/null
@@ -0,0 +1,120 @@
+From 17f46b803d4f23c66cacce81db35fef3adb8f2af Mon Sep 17 00:00:00 2001
+From: Josef Bacik <josef@toxicpanda.com>
+Date: Fri, 1 Mar 2024 11:49:57 -0500
+Subject: nfs: fix UAF in direct writes
+
+From: Josef Bacik <josef@toxicpanda.com>
+
+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:
+ <TASK>
+ ? __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 <josef@toxicpanda.com>
+Cc: stable@vger.kernel.org
+Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
+[ chanho : Backports v5.4.y, commit 133a48abf6ec (NFS: Fix up commit deadlocks)
+  is needed to use nfs_commit_end ]
+Signed-off-by: Chanho Min <chanho.min@lge.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..ab39def
--- /dev/null
@@ -0,0 +1,79 @@
+From 133a48abf6ecc535d7eddc6da1c3e4c972445882 Mon Sep 17 00:00:00 2001
+From: Trond Myklebust <trond.myklebust@hammerspace.com>
+Date: Mon, 4 Oct 2021 15:37:42 -0400
+Subject: NFS: Fix up commit deadlocks
+
+From: Trond Myklebust <trond.myklebust@hammerspace.com>
+
+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 <trond.myklebust@hammerspace.com>
+Signed-off-by: Chanho Min <chanho.min@lge.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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)
index d7177f564be7bc5a2d6fb0147ef3a48d43a2fbda..23d586ce6bdc12c2cd8c68fb9fc6cbf37f267efd 100644 (file)
@@ -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