From 5ce5227cd60b7213359e91837727ed1b6d457d49 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 29 Jul 2026 21:24:54 +0900 Subject: [PATCH] ksmbd: recognize replayed SMB2 lock sequences A server returns success without processing a lock request when a valid LockSequenceArray entry contains the same sequence number. The current verifier only invalidates mismatched entries, so matching requests are submitted to the VFS again and recorded as duplicate locks. Make the verifier report matching sequences and skip lock processing for those replays. Also correct the field comment to describe the sequence and index bit layout used by the implementation and the protocol. Use the capabilities advertised by the server when deciding whether lock sequence verification applies to a multichannel connection. Signed-off-by: Namjae Jeon --- fs/smb/common/smb2pdu.h | 4 ++-- fs/smb/server/smb2pdu.c | 34 +++++++++++++++++++++++----------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h index 6826f7bed1a4..c1414a1ffe30 100644 --- a/fs/smb/common/smb2pdu.h +++ b/fs/smb/common/smb2pdu.h @@ -847,8 +847,8 @@ struct smb2_lock_req { __le16 StructureSize; /* Must be 48 */ __le16 LockCount; /* - * The least significant four bits are the index, the other 28 bits are - * the lock sequence number (0 to 64). See MS-SMB2 2.2.26 + * The least significant four bits are the lock sequence number. The + * other 28 bits are the index (0 to 64). See MS-SMB2 2.2.26. */ __le32 LockSequenceNumber; __u64 PersistentFileId; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 8dca4aab05c6..d334f8a8807a 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -787,31 +787,38 @@ static bool smb2_lock_sequence_applicable(struct ksmbd_work *work, { return fp->is_resilient || fp->is_durable || fp->is_persistent || (work->conn->dialect >= SMB30_PROT_ID && - (work->conn->cli_cap & SMB2_GLOBAL_CAP_MULTI_CHANNEL)); + (work->conn->vals->req_capabilities & + SMB2_GLOBAL_CAP_MULTI_CHANNEL)); } -static void smb2_verify_lock_sequence(struct ksmbd_work *work, - struct ksmbd_file *fp, - struct smb2_lock_req *req) +static bool smb2_verify_lock_sequence(struct ksmbd_work *work, + struct ksmbd_file *fp, + struct smb2_lock_req *req) { u32 val, index; u8 sequence; + bool replay = false; if (work->conn->dialect == SMB20_PROT_ID || !smb2_lock_sequence_applicable(work, fp)) - return; + return false; val = le32_to_cpu(req->LockSequenceNumber); sequence = val & 0xf; index = val >> 4; if (!index || index > KSMBD_LOCK_SEQ_ARRAY_SIZE) - return; + return false; spin_lock(&fp->f_lock); - if (fp->lock_seq[index - 1].valid && - fp->lock_seq[index - 1].sequence != sequence) - fp->lock_seq[index - 1].valid = false; + if (fp->lock_seq[index - 1].valid) { + if (fp->lock_seq[index - 1].sequence == sequence) + replay = true; + else + fp->lock_seq[index - 1].valid = false; + } spin_unlock(&fp->f_lock); + + return replay; } static void smb2_update_lock_sequence(struct ksmbd_work *work, @@ -9194,6 +9201,7 @@ int smb2_lock(struct ksmbd_work *work) LIST_HEAD(rollback_list); int prior_lock = 0, bkt; unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + bool lock_replayed; WORK_BUFFERS(work, req, rsp); @@ -9226,7 +9234,9 @@ int smb2_lock(struct ksmbd_work *work) if (err) goto out2; - smb2_verify_lock_sequence(work, fp, req); + lock_replayed = smb2_verify_lock_sequence(work, fp, req); + if (lock_replayed) + goto lock_success; filp = fp->filp; lock_count = le16_to_cpu(req->LockCount); @@ -9493,6 +9503,7 @@ int smb2_lock(struct ksmbd_work *work) if (atomic_read(&fp->f_ci->op_count) > 1) smb_break_all_oplock(work, fp); +lock_success: rsp->StructureSize = cpu_to_le16(4); ksmbd_debug(SMB, "successful in taking lock\n"); rsp->hdr.Status = STATUS_SUCCESS; @@ -9500,7 +9511,8 @@ int smb2_lock(struct ksmbd_work *work) err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lock_rsp)); if (err) goto out; - smb2_update_lock_sequence(work, fp, req); + if (!lock_replayed) + smb2_update_lock_sequence(work, fp, req); ksmbd_fd_put(work, fp); return 0;