FAILED: patch "[PATCH] ksmbd: validate owner of durable handle on reconnect" failed to apply to 6.18-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x 49110a8ce654bbe56bef7c5e44cce31f4b102b8a
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026042009-snarl-stagnant-e64b@gregkh' --subject-prefix 'PATCH 6.18.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 49110a8ce654bbe56bef7c5e44cce31f4b102b8a Mon Sep 17 00:00:00 2001
From: Namjae Jeon <[email protected]>
Date: Sun, 12 Apr 2026 22:16:16 +0900
Subject: [PATCH] ksmbd: validate owner of durable handle on reconnect
Currently, ksmbd does not verify if the user attempting to reconnect to a durable handle is the same user who originally opened the file. This allows any authenticated user to hijack an orphaned durable handle by predicting or brute-forcing the persistent ID.
According to MS-SMB2, the server MUST verify that the SecurityContext of the reconnect request matches the SecurityContext associated with the existing open.
Add a durable_owner structure to ksmbd_file to store the original opener's UID, GID, and account name. and catpure the owner information when a file handle becomes orphaned. and implementing ksmbd_vfs_compare_durable_owner() to validate the identity of the requester during SMB2_CREATE (DHnC).
fs/smb/server/mgmt/user_session.c +3 -5
@@ -382,12 +382,10 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)return ;delete_proc_session (sess); - +ksmbd_tree_conn_session_logoff (sess); +ksmbd_destroy_file_table (sess);if (sess->user)ksmbd_free_user (sess->user); - -ksmbd_tree_conn_session_logoff (sess); -ksmbd_destroy_file_table (&sess->file_table);ksmbd_launch_ksmbd_durable_scavenger ();ksmbd_session_rpc_clear_list (sess);free_channel_list (sess);
@@ -618,7 +616,7 @@ void destroy_previous_session(struct ksmbd_conn *conn, goto out; } -ksmbd_destroy_file_table (&prev_sess->file_table); +ksmbd_destroy_file_table (prev_sess); prev_sess->state =SMB2_SESSION_EXPIRED ;ksmbd_all_conn_set_status (id,KSMBD_SESS_NEED_SETUP );ksmbd_launch_ksmbd_durable_scavenger ();
fs/smb/server/oplock.c +7 -0
@@ -1841,6 +1841,7 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn,struct ksmbd_share_config *share, struct ksmbd_file *fp, struct lease_ctx_info *lctx, + struct ksmbd_user *user, char *name) { struct oplock_info *opinfo = opinfo_get(fp);
@@ -1849,6 +1850,12 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn,if (!opinfo)return 0 ; +if (ksmbd_vfs_compare_durable_owner (fp, user) == false) { +ksmbd_debug (SMB ,"Durable handle reconnect failed: owner mismatch\n" ); + ret = -EBADF ; + goto out; + } +if (opinfo->is_lease == false) {if (lctx) {pr_err ("create context include lease\n" );
fs/smb/server/oplock.h +1 -0
@@ -126,5 +126,6 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn,struct ksmbd_share_config *share, struct ksmbd_file *fp, struct lease_ctx_info *lctx, + struct ksmbd_user *user, char *name);#endif /* __KSMBD_OPLOCK_H */
fs/smb/server/smb2pdu.c +2 -1
@@ -3013,7 +3013,8 @@ int smb2_open(struct ksmbd_work *work) }if (dh_info.reconnected == true) { - rc =smb2_check_durable_oplock (conn, share, dh_info.fp, lc, name); + rc =smb2_check_durable_oplock (conn, share, dh_info.fp, + lc, sess->user, name);if (rc) {ksmbd_put_durable_fd (dh_info.fp); goto err_out2;
fs/smb/server/vfs_cache.c +78 -9
161 lines not on this page · raw message
fs/smb/server/vfs_cache.h +11 -1
@@ -68,6 +68,13 @@ enum {FP_CLOSED }; +/* Owner information for durable handle reconnect */ +struct durable_owner { +unsigned int uid; +unsigned int gid; +char *name; +}; +struct ksmbd_file {struct file *filp;u64 persistent_id;
@@ -114,6 +121,7 @@ struct ksmbd_file {bool is_resilient;bool is_posix_ctxt; +struct durable_owner owner; }; static inline void set_ctx_actor(struct dir_context *ctx,
@@ -140,7 +148,7 @@ static inline bool ksmbd_stream_fd(struct ksmbd_file *fp) }int ksmbd_init_file_table (struct ksmbd_file_table *ft); -void ksmbd_destroy_file_table (struct ksmbd_file_table *ft); +void ksmbd_destroy_file_table (struct ksmbd_session *sess);int ksmbd_close_fd (struct ksmbd_work *work,u64 id);struct ksmbd_file *ksmbd_lookup_fd_fast (struct ksmbd_work *work,u64 id);struct ksmbd_file *ksmbd_lookup_foreign_fd (struct ksmbd_work *work,u64 id);
@@ -166,6 +174,8 @@ void ksmbd_free_global_file_table(void);void ksmbd_set_fd_limit (unsigned long limit);void ksmbd_update_fstate (struct ksmbd_file_table *ft,struct ksmbd_file *fp,unsigned int state); +bool ksmbd_vfs_compare_durable_owner (struct ksmbd_file *fp, +struct ksmbd_user *user);/* * INODE hash
Sasha Levin
maintainerPATCH 1/2 ksmbd: fix use-after-free in __ksmbd_close_fd() via durable scavenger
From: Namjae Jeon <[email protected]>
[ Upstream commit 235e32320a470fcd3998fb3774f2290a0eb302a1 ]
When a durable file handle survives session disconnect (TCP close without SMB2_LOGOFF), session_fd_check() sets fp->conn = NULL to preserve the handle for later reconnection. However, it did not clean up the byte-range locks on fp->lock_list.
Later, when the durable scavenger thread times out and calls __ksmbd_close_fd(NULL, fp), the lock cleanup loop did:
spin_lock(&fp->conn->llist_lock);
This caused a slab use-after-free because fp->conn was NULL and the original connection object had already been freed by ksmbd_tcp_disconnect().
The root cause is asymmetric cleanup: lock entries (smb_lock->clist) were left dangling on the freed conn->lock_list while fp->conn was nulled out.
To fix this issue properly, we need to handle the lifetime of smb_lock->clist across three paths:
- Safely skip clist deletion when list is empty and fp->conn is NULL.
- Remove the lock from the old connection's lock_list in session_fd_check()
- Re-add the lock to the new connection's lock_list in ksmbd_reopen_durable_fd().
Stable-dep-of: 49110a8ce654 ("ksmbd: validate owner of durable handle on reconnect")
--- fs/smb/server/vfs_cache.c | 41 ++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-)
fs/smb/server/vfs_cache.c +30 -11
@@ -370,9 +370,11 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)* there are not accesses to fp->lock_list. */ list_for_each_entry_safe (smb_lock, tmp_lock, &fp->lock_list, flist) { -spin_lock (&fp->conn->llist_lock); -list_del (&smb_lock->clist); -spin_unlock (&fp->conn->llist_lock); +if (!list_empty (&smb_lock->clist) && fp->conn) { +spin_lock (&fp->conn->llist_lock); +list_del (&smb_lock->clist); +spin_unlock (&fp->conn->llist_lock); + }list_del (&smb_lock->flist);locks_free_lock (smb_lock->fl);
@@ -902,6 +904,7 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon,struct ksmbd_inode *ci;struct oplock_info *op;struct ksmbd_conn *conn; +struct ksmbd_lock *smb_lock, *tmp_lock;if (!is_reconnectable (fp))return false;
@@ -918,6 +921,12 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, }up_write (&ci->m_lock); +list_for_each_entry_safe (smb_lock, tmp_lock, &fp->lock_list, flist) { +spin_lock (&fp->conn->llist_lock); +list_del_init (&smb_lock->clist); +spin_unlock (&fp->conn->llist_lock); + } + fp->conn =NULL ; fp->tcon =NULL ; fp->volatile_id =KSMBD_NO_FID ;
@@ -996,6 +1005,9 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) {struct ksmbd_inode *ci;struct oplock_info *op; +struct ksmbd_conn *conn = work->conn; +struct ksmbd_lock *smb_lock; +unsigned int old_f_state;if (!fp->is_durable || fp->conn || fp->tcon) {pr_err ("Invalid durable fd [%p:%p]\n" , fp->conn, fp->tcon);
@@ -1007,9 +1019,23 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)return -EBADF ; } - fp->conn = work->conn; + old_f_state = fp->f_state; + fp->f_state =FP_NEW ; +__open_id (&work->sess->file_table, fp,OPEN_ID_TYPE_VOLATILE_ID ); +if (!has_file_id (fp->volatile_id)) { + fp->f_state = old_f_state; +return -EBADF ; + } + + fp->conn = conn; fp->tcon = work->tcon; +list_for_each_entry (smb_lock, &fp->lock_list, flist) { +spin_lock (&conn->llist_lock); +list_add_tail (&smb_lock->clist, &conn->lock_list); +spin_unlock (&conn->llist_lock); + } + ci = fp->f_ci;down_write (&ci->m_lock);list_for_each_entry_rcu (op, &ci->m_op_list, op_entry) {
@@ -1020,13 +1046,6 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) }up_write (&ci->m_lock); - fp->f_state =FP_NEW ; -__open_id (&work->sess->file_table, fp,OPEN_ID_TYPE_VOLATILE_ID ); -if (!has_file_id (fp->volatile_id)) { - fp->conn =NULL ; - fp->tcon =NULL ; -return -EBADF ; - }return 0 ; }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 ksmbd: validate owner of durable handle on reconnect
From: Namjae Jeon <[email protected]>
[ Upstream commit 49110a8ce654bbe56bef7c5e44cce31f4b102b8a ]
Currently, ksmbd does not verify if the user attempting to reconnect to a durable handle is the same user who originally opened the file. This allows any authenticated user to hijack an orphaned durable handle by predicting or brute-forcing the persistent ID.
According to MS-SMB2, the server MUST verify that the SecurityContext of the reconnect request matches the SecurityContext associated with the existing open.
Add a durable_owner structure to ksmbd_file to store the original opener's UID, GID, and account name. and catpure the owner information when a file handle becomes orphaned. and implementing ksmbd_vfs_compare_durable_owner() to validate the identity of the requester during SMB2_CREATE (DHnC).
--- fs/smb/server/mgmt/user_session.c | 7 ++- fs/smb/server/oplock.c | 7 +++ fs/smb/server/oplock.h | 1 + fs/smb/server/smb2pdu.c | 3 +- fs/smb/server/vfs_cache.c | 87 +++++++++++++++++++++++++++---- fs/smb/server/vfs_cache.h | 12 ++++- 6 files changed, 102 insertions(+), 15 deletions(-)
fs/smb/server/mgmt/user_session.c +3 -4
@@ -161,11 +161,10 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)if (!sess)return ; +ksmbd_tree_conn_session_logoff (sess); +ksmbd_destroy_file_table (sess);if (sess->user)ksmbd_free_user (sess->user); - -ksmbd_tree_conn_session_logoff (sess); -ksmbd_destroy_file_table (&sess->file_table);ksmbd_launch_ksmbd_durable_scavenger ();ksmbd_session_rpc_clear_list (sess);free_channel_list (sess);
@@ -396,7 +395,7 @@ void destroy_previous_session(struct ksmbd_conn *conn, goto out; } -ksmbd_destroy_file_table (&prev_sess->file_table); +ksmbd_destroy_file_table (prev_sess); prev_sess->state =SMB2_SESSION_EXPIRED ;ksmbd_all_conn_set_status (id,KSMBD_SESS_NEED_SETUP );ksmbd_launch_ksmbd_durable_scavenger ();
fs/smb/server/oplock.c +7 -0
@@ -1841,6 +1841,7 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn,struct ksmbd_share_config *share, struct ksmbd_file *fp, struct lease_ctx_info *lctx, + struct ksmbd_user *user, char *name) { struct oplock_info *opinfo = opinfo_get(fp);
@@ -1849,6 +1850,12 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn,if (!opinfo)return 0 ; +if (ksmbd_vfs_compare_durable_owner (fp, user) == false) { +ksmbd_debug (SMB ,"Durable handle reconnect failed: owner mismatch\n" ); + ret = -EBADF ; + goto out; + } +if (opinfo->is_lease == false) {if (lctx) {pr_err ("create context include lease\n" );
fs/smb/server/oplock.h +1 -0
@@ -126,5 +126,6 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn,struct ksmbd_share_config *share, struct ksmbd_file *fp, struct lease_ctx_info *lctx, + struct ksmbd_user *user, char *name);#endif /* __KSMBD_OPLOCK_H */
fs/smb/server/smb2pdu.c +2 -1
@@ -3022,7 +3022,8 @@ int smb2_open(struct ksmbd_work *work) }if (dh_info.reconnected == true) { - rc =smb2_check_durable_oplock (conn, share, dh_info.fp, lc, name); + rc =smb2_check_durable_oplock (conn, share, dh_info.fp, + lc, sess->user, name);if (rc) {ksmbd_put_durable_fd (dh_info.fp); goto err_out2;
fs/smb/server/vfs_cache.c +78 -9
161 lines not on this page · raw message
fs/smb/server/vfs_cache.h +11 -1
@@ -68,6 +68,13 @@ enum {FP_CLOSED }; +/* Owner information for durable handle reconnect */ +struct durable_owner { +unsigned int uid; +unsigned int gid; +char *name; +}; +struct ksmbd_file {struct file *filp;u64 persistent_id;
@@ -114,6 +121,7 @@ struct ksmbd_file {bool is_resilient;bool is_posix_ctxt; +struct durable_owner owner; }; static inline void set_ctx_actor(struct dir_context *ctx,
@@ -140,7 +148,7 @@ static inline bool ksmbd_stream_fd(struct ksmbd_file *fp) }int ksmbd_init_file_table (struct ksmbd_file_table *ft); -void ksmbd_destroy_file_table (struct ksmbd_file_table *ft); +void ksmbd_destroy_file_table (struct ksmbd_session *sess);int ksmbd_close_fd (struct ksmbd_work *work,u64 id);struct ksmbd_file *ksmbd_lookup_fd_fast (struct ksmbd_work *work,u64 id);struct ksmbd_file *ksmbd_lookup_foreign_fd (struct ksmbd_work *work,u64 id);
@@ -166,6 +174,8 @@ void ksmbd_free_global_file_table(void);void ksmbd_set_fd_limit (unsigned long limit);void ksmbd_update_fstate (struct ksmbd_file_table *ft,struct ksmbd_file *fp,unsigned int state); +bool ksmbd_vfs_compare_durable_owner (struct ksmbd_file *fp, +struct ksmbd_user *user);/* * INODE hash
Signature · 1 line
2.53.0