[RFC PATCH 02/11] smb/server: validate notify requests

ChenXiaoSong <[email protected]> Thu, 23 Jul 2026 03:16:30 +0000
Newsgroups org.kernel.vger.linux-cifs
Message-ID <[email protected]>
From: ChenXiaoSong <[email protected]>

Validate the file ID, directory type, access rights and output buffer
length before handling a change notify request.

Signed-off-by: ChenXiaoSong <[email protected]>
---
 fs/smb/server/Makefile  |   2 +-
 fs/smb/server/notify.c  | 108 ++++++++++++++++++++++++++++++++++++++++
 fs/smb/server/notify.h  |  22 ++++++++
 fs/smb/server/smb2pdu.c |  12 +++--
 4 files changed, 138 insertions(+), 6 deletions(-)
 create mode 100644 fs/smb/server/notify.c
 create mode 100644 fs/smb/server/notify.h

diff --git a/fs/smb/server/Makefile b/fs/smb/server/Makefile
index a3e9306055e8..51d8972a0942 100644
--- a/fs/smb/server/Makefile
+++ b/fs/smb/server/Makefile
@@ -9,7 +9,7 @@ ksmbd-y :=	unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \
 		mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \
 		mgmt/tree_connect.o mgmt/user_session.o smb_common.o \
 		transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \
-		smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \
+		smb2ops.o smb2misc.o notify.o ksmbd_spnego_negtokeninit.asn1.o \
 		ksmbd_spnego_negtokentarg.asn1.o asn1.o compress.o
 
 $(obj)/asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.h $(obj)/ksmbd_spnego_negtokentarg.asn1.h
diff --git a/fs/smb/server/notify.c b/fs/smb/server/notify.c
new file mode 100644
index 000000000000..ad812fde5879
--- /dev/null
+++ b/fs/smb/server/notify.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ *   SMB2 CHANGE_NOTIFY
+ *
+ *   Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved.
+ *   Author(s): ChenXiaoSong <[email protected]>
+ *
+ */
+
+#include "glob.h"
+#include "../common/smb2status.h"
+#include "connection.h"
+#include "ksmbd_work.h"
+#include "notify.h"
+#include "smb_common.h"
+#include "smb2pdu.h"
+#include "vfs_cache.h"
+
+static struct ksmbd_file *
+ksmbd_notify_validate_req(struct ksmbd_work *work,
+			  struct smb2_change_notify_req *req,
+			  struct smb2_change_notify_rsp *rsp)
+{
+	struct ksmbd_file *fp;
+	int err;
+
+	fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId,
+				  req->PersistentFileId);
+	if (!fp) {
+		pr_err("Invalid file id for notify, fid %llu:%llu\n",
+		       le64_to_cpu(req->PersistentFileId),
+		       le64_to_cpu(req->VolatileFileId));
+		rsp->hdr.Status = STATUS_FILE_CLOSED;
+		return ERR_PTR(-ENOENT);
+	}
+
+	ksmbd_debug(NOTIFY,
+		    "fid %llu:%llu, handle notify request, filter 0x%x, flags 0x%x\n",
+		    fp->persistent_id, fp->volatile_id,
+		    le32_to_cpu(req->CompletionFilter), le16_to_cpu(req->Flags));
+
+	if (!S_ISDIR(file_inode(fp->filp)->i_mode)) {
+		pr_err("Notify file id is not a directory, fid %llu:%llu\n",
+		       fp->persistent_id, fp->volatile_id);
+		rsp->hdr.Status = STATUS_NOT_A_DIRECTORY;
+		err = -ENOTDIR;
+		goto err_put_fp;
+	}
+
+	if (!(fp->daccess & FILE_LIST_DIRECTORY_LE)) {
+		pr_err("No permission to monitor directory, fid %llu:%llu\n",
+		       fp->persistent_id, fp->volatile_id);
+		rsp->hdr.Status = STATUS_ACCESS_DENIED;
+		err = -EACCES;
+		goto err_put_fp;
+	}
+
+	if (le32_to_cpu(req->OutputBufferLength) >
+	    work->conn->vals->max_trans_size) {
+		pr_err("Notify output buffer length %u exceeds maximum %u\n",
+		       le32_to_cpu(req->OutputBufferLength),
+		       work->conn->vals->max_trans_size);
+		rsp->hdr.Status = STATUS_INVALID_PARAMETER;
+		err = -EINVAL;
+		goto err_put_fp;
+	}
+
+	return fp;
+
+err_put_fp:
+	ksmbd_fd_put(work, fp);
+	return ERR_PTR(err);
+}
+
+/**
+ * ksmbd_handle_notify() - handle an SMB2 change notify request
+ * @work: smb work containing notify command buffer
+ * @req: SMB2 change notify request
+ * @rsp: SMB2 change notify response
+ *
+ * Return: 0 on success, otherwise error
+ */
+int ksmbd_handle_notify(struct ksmbd_work *work,
+			struct smb2_change_notify_req *req,
+			struct smb2_change_notify_rsp *rsp)
+{
+	struct ksmbd_file *fp = NULL;
+	int err = 0;
+
+	fp = ksmbd_notify_validate_req(work, req, rsp);
+	if (IS_ERR(fp)) {
+		err = PTR_ERR(fp);
+		goto out;
+	}
+
+	ksmbd_fd_put(work, fp);
+	rsp->hdr.Status = STATUS_NOT_IMPLEMENTED;
+	err = -EOPNOTSUPP;
+
+out:
+	if (err)
+		pr_err("Failed to handle notify request: %d, status: 0x%x\n",
+		       err, le32_to_cpu(rsp->hdr.Status));
+	if (rsp->hdr.Status != STATUS_SUCCESS)
+		smb2_set_err_rsp(work);
+	return err;
+}
diff --git a/fs/smb/server/notify.h b/fs/smb/server/notify.h
new file mode 100644
index 000000000000..3118ad09c841
--- /dev/null
+++ b/fs/smb/server/notify.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ *   SMB2 CHANGE_NOTIFY
+ *
+ *   Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved.
+ *   Author(s): ChenXiaoSong <[email protected]>
+ *
+ */
+
+#ifndef __SMB_SERVER_NOTIFY_H__
+#define __SMB_SERVER_NOTIFY_H__
+
+struct ksmbd_work;
+struct smb2_change_notify_req;
+struct smb2_change_notify_rsp;
+
+int ksmbd_handle_notify(struct ksmbd_work *work,
+			struct smb2_change_notify_req *req,
+			struct smb2_change_notify_rsp *rsp);
+
+#endif /* __SMB_SERVER_NOTIFY_H__ */
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 0680222a8da4..fe1b6c9f71c5 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -29,6 +29,7 @@
 #include "vfs.h"
 #include "vfs_cache.h"
 #include "misc.h"
+#include "notify.h"
 
 #include "server.h"
 #include "smb_common.h"
@@ -10860,22 +10861,23 @@ int smb2_notify(struct ksmbd_work *work)
 	struct smb2_change_notify_req *req;
 	struct smb2_change_notify_rsp *rsp;
 
-	ksmbd_debug(SMB, "Received smb2 notify\n");
+	ksmbd_debug(NOTIFY, "Received smb2 notify\n");
 
 	WORK_BUFFERS(work, req, rsp);
 
-	if (smb2_compound_has_failed(work, &rsp->hdr))
+	if (smb2_compound_has_failed(work, &rsp->hdr)) {
+		pr_err("Failed compound notify request\n");
 		return -EACCES;
+	}
 
 	if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) {
+		pr_err("Notify request is not the last compound command\n");
 		rsp->hdr.Status = STATUS_INTERNAL_ERROR;
 		smb2_set_err_rsp(work);
 		return -EIO;
 	}
 
-	smb2_set_err_rsp(work);
-	rsp->hdr.Status = STATUS_NOT_IMPLEMENTED;
-	return -EOPNOTSUPP;
+	return ksmbd_handle_notify(work, req, rsp);
 }
 
 /**
-- 
2.54.0