From: Namjae Jeon <linkinjeon@kernel.org>
To: linux-cifs@vger.kernel.org
Cc: senozhatsky@chromium.org, tom@talpey.com,
atteh.mailbox@gmail.com, chenxiaosong@chenxiaosong.com,
chenxiaosong@kylinos.cn, Namjae Jeon <linkinjeon@kernel.org>,
Ronnie Sahlberg <ronniesahlberg@gmail.com>
Subject: [PATCH] ksmbd: inherit FileIds in related compound requests
Date: Mon, 19 Oct 2026 09:56:03 +0900 [thread overview]
Message-ID: <20261019005603.5813-1-linkinjeon@kernel.org> (raw)
MS-SMB2 specifies that subsequent related operations use the FileId from
the preceding operation. Track FileIds from handle-bearing requests so
related requests that use the all-ones placeholder resolve to the
inherited handle.
Return STATUS_INVALID_HANDLE when a related request refers to a FileId
but the preceding operation neither contained nor generated one.
Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
Reported-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
---
fs/smb/server/ksmbd_work.h | 9 ++-
fs/smb/server/server.c | 5 +-
fs/smb/server/smb2pdu.c | 150 ++++++++++++++++++++++++++++---------
fs/smb/server/smb2pdu.h | 1 +
4 files changed, 126 insertions(+), 39 deletions(-)
diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h
index b51dea69cb0e..1ab9ddd20506 100644
--- a/fs/smb/server/ksmbd_work.h
+++ b/fs/smb/server/ksmbd_work.h
@@ -54,10 +54,7 @@ struct ksmbd_work {
/* Current cmd hdr in compound rsp buf*/
int curr_smb2_rsp_hdr_off;
- /*
- * Current Local FID assigned compound response if SMB2 CREATE
- * command is present in compound request
- */
+ /* FileId generated or used by the previous operation in a compound. */
u64 compound_fid;
u64 compound_pfid;
u64 compound_sid;
@@ -93,6 +90,10 @@ struct ksmbd_work {
bool asynchronous:1;
bool need_invalidate_rkey:1;
bool request_open_chseq_tracked:1;
+ /* The previous command carries a FileId or is a CREATE. */
+ bool compound_fid_present:1;
+ /* A related command failed because there was no FileId to inherit. */
+ bool compound_fid_missing:1;
bool session_setup_reauth:1;
/* Retain the binding decision for response signing and preauth hashing. */
bool session_setup_binding:1;
diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index c836e75b4825..064f99427ca6 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -157,7 +157,10 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn,
}
}
- ret = cmds->proc(work);
+ if (smb2_check_compound_fid(work))
+ ret = -EBADF;
+ else
+ ret = cmds->proc(work);
if (conn->ops->inc_reqs) {
struct smb2_hdr *rsp = ksmbd_resp_buf_curr(work);
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 97b1e5ac5115..8de3a83e7341 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -456,6 +456,95 @@ int smb2_set_rsp_credits(struct ksmbd_work *work)
return 0;
}
+static bool smb2_get_file_ids(struct smb2_hdr *hdr, u64 *volatile_id,
+ u64 *persistent_id)
+{
+ unsigned int offset;
+
+ switch (hdr->Command) {
+ case SMB2_CLOSE:
+ offset = offsetof(struct smb2_close_req, PersistentFileId);
+ break;
+ case SMB2_FLUSH:
+ offset = offsetof(struct smb2_flush_req, PersistentFileId);
+ break;
+ case SMB2_READ:
+ offset = offsetof(struct smb2_read_req, PersistentFileId);
+ break;
+ case SMB2_WRITE:
+ offset = offsetof(struct smb2_write_req, PersistentFileId);
+ break;
+ case SMB2_LOCK:
+ offset = offsetof(struct smb2_lock_req, PersistentFileId);
+ break;
+ case SMB2_IOCTL: {
+ struct smb2_ioctl_req *req = (struct smb2_ioctl_req *)hdr;
+
+ switch (le32_to_cpu(req->CtlCode)) {
+ case FSCTL_DFS_GET_REFERRALS:
+ case FSCTL_DFS_GET_REFERRALS_EX:
+ case FSCTL_QUERY_NETWORK_INTERFACE_INFO:
+ case FSCTL_VALIDATE_NEGOTIATE_INFO:
+ case FSCTL_PIPE_WAIT:
+ return false;
+ }
+ offset = offsetof(struct smb2_ioctl_req, PersistentFileId);
+ break;
+ }
+ case SMB2_QUERY_DIRECTORY:
+ offset = offsetof(struct smb2_query_directory_req, PersistentFileId);
+ break;
+ case SMB2_CHANGE_NOTIFY:
+ offset = offsetof(struct smb2_change_notify_req, PersistentFileId);
+ break;
+ case SMB2_QUERY_INFO:
+ offset = offsetof(struct smb2_query_info_req, PersistentFileId);
+ break;
+ case SMB2_SET_INFO:
+ offset = offsetof(struct smb2_set_info_req, PersistentFileId);
+ break;
+ case SMB2_OPLOCK_BREAK:
+ if (((struct smb2_oplock_break *)hdr)->StructureSize !=
+ cpu_to_le16(OP_BREAK_STRUCT_SIZE_20))
+ return false;
+ offset = offsetof(struct smb2_oplock_break, PersistentFid);
+ break;
+ default:
+ return false;
+ }
+
+ /* FileId contains consecutive persistent and volatile opaque values. */
+ memcpy(persistent_id, (char *)hdr + offset, sizeof(*persistent_id));
+ memcpy(volatile_id, (char *)hdr + offset + sizeof(*persistent_id),
+ sizeof(*volatile_id));
+ return true;
+}
+
+/* Called after message validation and signature verification. */
+bool smb2_check_compound_fid(struct ksmbd_work *work)
+{
+ struct smb2_hdr *req = ksmbd_req_buf_next(work);
+ struct smb2_hdr *rsp = ksmbd_resp_buf_next(work);
+ u64 volatile_id, persistent_id;
+
+ if (!work->next_smb2_rcv_hdr_off ||
+ !(req->Flags & SMB2_FLAGS_RELATED_OPERATIONS))
+ return false;
+
+ if (!work->compound_fid_missing &&
+ !work->compound_fid_present &&
+ smb2_get_file_ids(req, &volatile_id, &persistent_id))
+ work->compound_fid_missing = true;
+
+ if (!work->compound_fid_missing)
+ return false;
+
+ /* MS-SMB2 3.3.5.2.7.2 requires all subsequent operations to fail too. */
+ rsp->Status = STATUS_INVALID_HANDLE;
+ smb2_set_err_rsp(work);
+ return true;
+}
+
/**
* init_chained_smb2_rsp() - initialize smb2 chained response
* @work: smb work containing smb response buffer
@@ -468,6 +557,21 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work)
struct smb2_hdr *rcv_hdr;
int next_hdr_offset = 0;
int len, new_len;
+ u64 volatile_id = KSMBD_NO_FID, persistent_id = KSMBD_NO_FID;
+ bool req_has_fid;
+
+ req_has_fid = smb2_get_file_ids(req, &volatile_id, &persistent_id);
+ /* A failed CREATE is handled through compound_status instead. */
+ work->compound_fid_present = req_has_fid || req->Command == SMB2_CREATE;
+ if (!work->compound_fid_present) {
+ work->compound_fid = KSMBD_NO_FID;
+ work->compound_pfid = KSMBD_NO_FID;
+ }
+
+ /* Related requests keep the inherited SessionId. */
+ if (!work->next_smb2_rcv_hdr_off ||
+ !(req->Flags & SMB2_FLAGS_RELATED_OPERATIONS))
+ work->compound_sid = le64_to_cpu(rsp->SessionId);
/* Len of this response = updated RFC len - offset of previous cmd
* in the compound rsp
@@ -479,48 +583,24 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work)
if (req->Command == SMB2_CREATE && rsp->Status == STATUS_SUCCESS) {
work->compound_fid = ((struct smb2_create_rsp *)rsp)->VolatileFileId;
work->compound_pfid = ((struct smb2_create_rsp *)rsp)->PersistentFileId;
- work->compound_sid = le64_to_cpu(rsp->SessionId);
work->compound_status = STATUS_SUCCESS;
- } else if ((req->Command == SMB2_FLUSH ||
- req->Command == SMB2_READ ||
- req->Command == SMB2_WRITE) &&
- rsp->Status == STATUS_SUCCESS) {
- u64 volatile_id = KSMBD_NO_FID;
- u64 persistent_id = KSMBD_NO_FID;
-
- if (req->Command == SMB2_FLUSH) {
- struct smb2_flush_req *flush_req =
- (struct smb2_flush_req *)req;
-
- volatile_id = flush_req->VolatileFileId;
- persistent_id = flush_req->PersistentFileId;
- } else if (req->Command == SMB2_READ) {
- struct smb2_read_req *read_req =
- (struct smb2_read_req *)req;
-
- volatile_id = read_req->VolatileFileId;
- persistent_id = read_req->PersistentFileId;
+ } else if (rsp->Status == STATUS_SUCCESS) {
+ if (req_has_fid && req->Command != SMB2_CLOSE) {
+ /* Preserve the inherited FID when the request uses all ones. */
+ if (has_file_id(volatile_id)) {
+ work->compound_fid = volatile_id;
+ work->compound_pfid = persistent_id;
+ }
} else {
- struct smb2_write_req *write_req =
- (struct smb2_write_req *)req;
-
- volatile_id = write_req->VolatileFileId;
- persistent_id = write_req->PersistentFileId;
- }
-
- if (has_file_id(volatile_id)) {
- work->compound_fid = volatile_id;
- work->compound_pfid = persistent_id;
- work->compound_sid = le64_to_cpu(rsp->SessionId);
- work->compound_status = STATUS_SUCCESS;
+ work->compound_fid = KSMBD_NO_FID;
+ work->compound_pfid = KSMBD_NO_FID;
}
+ work->compound_status = STATUS_SUCCESS;
} else if (req->Command == SMB2_CREATE) {
work->compound_fid = KSMBD_NO_FID;
work->compound_pfid = KSMBD_NO_FID;
- work->compound_sid = le64_to_cpu(rsp->SessionId);
work->compound_status = rsp->Status;
} else if (rsp->Status != STATUS_SUCCESS) {
- work->compound_sid = le64_to_cpu(rsp->SessionId);
/*
* Only carry the failed status forward when the failing command
* was itself part of the related chain. An unrelated command
@@ -559,6 +639,8 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work)
work->compound_fid = KSMBD_NO_FID;
work->compound_pfid = KSMBD_NO_FID;
work->compound_status = STATUS_SUCCESS;
+ work->compound_fid_present = false;
+ work->compound_fid_missing = false;
}
memset((char *)rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER;
diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h
index e706aa52352c..bd62e97938b8 100644
--- a/fs/smb/server/smb2pdu.h
+++ b/fs/smb/server/smb2pdu.h
@@ -421,6 +421,7 @@ bool is_smb2_rsp(struct ksmbd_work *work);
u16 get_smb2_cmd_val(struct ksmbd_work *work);
void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err);
int init_smb2_rsp_hdr(struct ksmbd_work *work);
+bool smb2_check_compound_fid(struct ksmbd_work *work);
int smb2_allocate_rsp_buf(struct ksmbd_work *work);
bool is_chained_smb2_message(struct ksmbd_work *work);
int init_smb2_neg_rsp(struct ksmbd_work *work);
--
2.25.1
reply other threads:[~2026-10-19 0:56 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261019005603.5813-1-linkinjeon@kernel.org \
--to=linkinjeon@kernel.org \
--cc=atteh.mailbox@gmail.com \
--cc=chenxiaosong@chenxiaosong.com \
--cc=chenxiaosong@kylinos.cn \
--cc=linux-cifs@vger.kernel.org \
--cc=ronniesahlberg@gmail.com \
--cc=senozhatsky@chromium.org \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox