]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ksmbd: serialize QUERY_DIRECTORY requests per file
authorNamjae Jeon <linkinjeon@kernel.org>
Thu, 11 Jun 2026 23:00:00 +0000 (08:00 +0900)
committerSteve French <stfrench@microsoft.com>
Tue, 16 Jun 2026 23:57:22 +0000 (18:57 -0500)
smb2_query_dir() stores a pointer to its stack-allocated private data in
the ksmbd_file readdir_data. Concurrent QUERY_DIRECTORY requests using the
same file handle can overwrite this pointer while an iterate_dir() callback
is still using it, resulting in a stack use-after-free.

Add a per-file mutex and hold it while accessing the shared directory
enumeration state. The lock covers scan restart, dot entry state,
readdir_data setup and iteration, and response construction. This prevents
another request from replacing readdir_data.private before the current
request has finished using it and also serializes the shared file position.

Cc: stable@vger.kernel.org
Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-30527
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/server/smb2pdu.c
fs/smb/server/vfs_cache.c
fs/smb/server/vfs_cache.h

index 32f568cea16a9a75bfc512b745b5709b5f97f652..96dcb78cfb925d3813ea5009dc7baee5ca8c8251 100644 (file)
@@ -4569,6 +4569,8 @@ int smb2_query_dir(struct ksmbd_work *work)
                ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr);
        }
 
+       mutex_lock(&dir_fp->readdir_lock);
+
        if (srch_flag & SMB2_REOPEN || srch_flag & SMB2_RESTART_SCANS) {
                ksmbd_debug(SMB, "Restart directory scan\n");
                generic_file_llseek(dir_fp->filp, 0, SEEK_SET);
@@ -4673,6 +4675,7 @@ no_buf_len:
                        goto err_out;
        }
 
+       mutex_unlock(&dir_fp->readdir_lock);
        kfree(srch_ptr);
        ksmbd_fd_put(work, dir_fp);
        ksmbd_revert_fsids(work);
@@ -4680,6 +4683,7 @@ no_buf_len:
 
 err_out:
        pr_err("error while processing smb2 query dir rc = %d\n", rc);
+       mutex_unlock(&dir_fp->readdir_lock);
        kfree(srch_ptr);
 
 err_out2:
index ba3355a6057a268b4c480d250282821c313e2087..4daccc77f9eca1357a3809e35f207dad9cbb978a 100644 (file)
@@ -790,6 +790,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
        INIT_LIST_HEAD(&fp->node);
        INIT_LIST_HEAD(&fp->lock_list);
        spin_lock_init(&fp->f_lock);
+       mutex_init(&fp->readdir_lock);
        atomic_set(&fp->refcount, 1);
 
        fp->filp                = filp;
index e6871266a94badc7cc4136638bf7cf2fc5d22f40..7d547e1a74f7f25b6a7c0aa9474510af15385e34 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/file.h>
 #include <linux/fs.h>
+#include <linux/mutex.h>
 #include <linux/rwsem.h>
 #include <linux/spinlock.h>
 #include <linux/idr.h>
@@ -113,6 +114,7 @@ struct ksmbd_file {
 
        /* if ls is happening on directory, below is valid*/
        struct ksmbd_readdir_data       readdir_data;
+       struct mutex                    readdir_lock;
        int                             dot_dotdot[2];
        unsigned int                    f_state;
        bool                            reserve_lease_break;