Thread (3 messages) 3 messages, 2 authors, 3d ago

[PATCH] smb: client: fix double free in cached_dir_offload_close()

From: Paulo Alcantara <pc@manguebit.org>
Date: 2026-05-19 23:55:26
Subsystem: common internet file system client (cifs and smb3), filesystems (vfs and infrastructure), the rest · Maintainers: Steve French, Alexander Viro, Christian Brauner, Linus Torvalds

Fix double free of @tcon->origin_fullpath where T1 and T3 are
concurrently tearing down @tcon:

  T1

  cifs_umount()
    cifs_put_tlink()
      cifs_put_tcon()
        --tcon->tc_count [refcount=0]
        tconInfoFree()
          free_cached_dirs()
            cancel_delayed_work_sync(&cfids->laundromat_work)
          ...
          kfree(tcon->origin_fullpath) [1]

  T2

  cifs_laundromat_worker()
    ++tcon->tc_count [refcount=1]
    queue_work(serverclose_wq, &cfid->close_work())
    ...

  T3

  cached_dir_offload_close()
    close_cached_dir()
    cifs_put_tcon()
      --tcon->tc_count [refcount=0]
      tconInfoFree()
        ...
        kfree(tcon->origin_fullpath) [2] -> double free

  kernel BUG at mm/slub.c:530!
  invalid opcode: 0000 [#1] PREEMPT SMP NOPTI
  CPU: 27 PID: 4144774 Comm: kworker/27:2 Kdump: loaded Tainted: G L X
  ------ --- 5.14.0-611.9.1.el9_7.x86_64 #1
  Hardware name: HPE ProLiant DL360 Gen11/ProLiant DL360 Gen11, BIOS
  2.50 04/22/2025
  Workqueue: serverclose cached_dir_offload_close [cifs]
  RIP: 0010:__slab_free+0x1d0/0x310
  ...
  Call Trace:
   <TASK>
   ? show_trace_log_lvl+0x1c4/0x2df
   ? show_trace_log_lvl+0x1c4/0x2df
   ? kfree+0x2a6/0x330
   ? __die_body.cold+0x8/0xd
   ? die+0x2b/0x50
   ? do_trap+0xcd/0x120
   ? __slab_free+0x1d0/0x310
   ? do_error_trap+0x65/0x80
   ? __slab_free+0x1d0/0x310
   ? exc_invalid_op+0x4e/0x70
   ? __slab_free+0x1d0/0x310
   ? asm_exc_invalid_op+0x16/0x20
   ? tconInfoFree+0x4b/0xf0 [cifs]
   ? __slab_free+0x1d0/0x310
   ? work_grab_pending+0x43/0xe0
   ? free_cached_dirs+0x1a8/0x1d0 [cifs]
   ? tconInfoFree+0x4b/0xf0 [cifs]
   kfree+0x2a6/0x330
   ? free_cached_dirs+0x1a8/0x1d0 [cifs]
   tconInfoFree+0x4b/0xf0 [cifs]
   cifs_put_tcon+0x169/0x370 [cifs]
   process_one_work+0x193/0x380
   worker_thread+0x281/0x3a0
   ? __pfx_worker_thread+0x10/0x10
   kthread+0x101/0x110
   ? __pfx_kthread+0x10/0x10
   ret_from_fork+0x28/0x50
   </TASK>

Check for TID_EXITING earlier in cifs_put_tcon() to guarantee that
thread which set it will be the only one responsible for tearing down
@tcon.

Make sure that any queued workers in @serverclose_wq and @cfid_put_wq
are processed before leaving free_cached_dirs().  Note that any
cifs_put_tcon() call from the workqueues will turn into a no-op due to
@tcon->status == TID_EXITING, so the refcount bumps won't matter.

Reported-by: Vaibhav Nagare <redacted>
Fixes: 3fa640d035e5 ("smb: During unmount, ensure all cached dir instances drop their dentry")
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
Cc: David Howells <dhowells@redhat.com>
Cc: Jay Shin <redacted>
Cc: linux-cifs@vger.kernel.org
---
 fs/smb/client/cached_dir.c | 4 ++++
 fs/smb/client/connect.c    | 5 +++++
 2 files changed, 9 insertions(+)
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
index 88d5e9a32f28..47a51b6b397e 100644
--- a/fs/smb/client/cached_dir.c
+++ b/fs/smb/client/cached_dir.c
@@ -691,9 +691,11 @@ bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
 			cfid->on_list = false;
 			cfids->num_entries--;
 
+			spin_lock(&tcon->tc_lock);
 			++tcon->tc_count;
 			trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
 					    netfs_trace_tcon_ref_get_cached_lease_break);
+			spin_unlock(&tcon->tc_lock);
 			queue_work(cfid_put_wq, &cfid->put_work);
 			spin_unlock(&cfids->cfid_list_lock);
 			return true;
@@ -851,7 +853,9 @@ void free_cached_dirs(struct cached_fids *cfids)
 	if (cfids == NULL)
 		return;
 
+	flush_workqueue(cfid_put_wq);
 	cancel_delayed_work_sync(&cfids->laundromat_work);
+	flush_workqueue(serverclose_wq);
 
 	spin_lock(&cfids->cfid_list_lock);
 	list_for_each_entry_safe(cfid, q, &cfids->entries, entry) {
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index dcde25da468d..c55b4c040a30 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -2605,6 +2605,11 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
 	cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
 	spin_lock(&cifs_tcp_ses_lock);
 	spin_lock(&tcon->tc_lock);
+	if (tcon->status == TID_EXITING) {
+		spin_unlock(&tcon->tc_lock);
+		spin_unlock(&cifs_tcp_ses_lock);
+		return;
+	}
 	trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count - 1, trace);
 	if (--tcon->tc_count > 0) {
 		spin_unlock(&tcon->tc_lock);
-- 
2.54.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help