aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
authorNamjae Jeon <linkinjeon@kernel.org>2026-06-12 08:00:00 +0900
committerSteve French <stfrench@microsoft.com>2026-06-16 18:57:22 -0500
commitbe6d26bf27499977c746abc163659915082348d8 (patch)
tree59d8df96b7f6bee40b08c9b3c4d904ed1b8fee7f /fs
parent388e4139db27a9e3612c9d356b826f5b1ff6a9e3 (diff)
downloadath-be6d26bf27499977c746abc163659915082348d8.tar.gz
ksmbd: serialize QUERY_DIRECTORY requests per file
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>
Diffstat (limited to 'fs')
-rw-r--r--fs/smb/server/smb2pdu.c4
-rw-r--r--fs/smb/server/vfs_cache.c1
-rw-r--r--fs/smb/server/vfs_cache.h2
3 files changed, 7 insertions, 0 deletions
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 32f568cea16a9..96dcb78cfb925 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -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:
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index ba3355a6057a2..4daccc77f9eca 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -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;
diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
index e6871266a94ba..7d547e1a74f7f 100644
--- a/fs/smb/server/vfs_cache.h
+++ b/fs/smb/server/vfs_cache.h
@@ -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;