Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ksmbd: fix use-after-free in __ksmbd_close_fd() via durable scavenger

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().

Fixes: c8efcc786146 ("ksmbd: add support for durable handles v1/v2")
Co-developed-by: munan Huang <munanevil@gmail.com>
Signed-off-by: munan Huang <munanevil@gmail.com>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Namjae Jeon and committed by
Steve French
235e3232 3df614eb

+30 -11
+30 -11
fs/smb/server/vfs_cache.c
··· 463 463 * there are not accesses to fp->lock_list. 464 464 */ 465 465 list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { 466 - spin_lock(&fp->conn->llist_lock); 467 - list_del(&smb_lock->clist); 468 - spin_unlock(&fp->conn->llist_lock); 466 + if (!list_empty(&smb_lock->clist) && fp->conn) { 467 + spin_lock(&fp->conn->llist_lock); 468 + list_del(&smb_lock->clist); 469 + spin_unlock(&fp->conn->llist_lock); 470 + } 469 471 470 472 list_del(&smb_lock->flist); 471 473 locks_free_lock(smb_lock->fl); ··· 997 995 struct ksmbd_inode *ci; 998 996 struct oplock_info *op; 999 997 struct ksmbd_conn *conn; 998 + struct ksmbd_lock *smb_lock, *tmp_lock; 1000 999 1001 1000 if (!is_reconnectable(fp)) 1002 1001 return false; ··· 1013 1010 op->conn = NULL; 1014 1011 } 1015 1012 up_write(&ci->m_lock); 1013 + 1014 + list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { 1015 + spin_lock(&fp->conn->llist_lock); 1016 + list_del_init(&smb_lock->clist); 1017 + spin_unlock(&fp->conn->llist_lock); 1018 + } 1016 1019 1017 1020 fp->conn = NULL; 1018 1021 fp->tcon = NULL; ··· 1099 1090 { 1100 1091 struct ksmbd_inode *ci; 1101 1092 struct oplock_info *op; 1093 + struct ksmbd_conn *conn = work->conn; 1094 + struct ksmbd_lock *smb_lock; 1095 + unsigned int old_f_state; 1102 1096 1103 1097 if (!fp->is_durable || fp->conn || fp->tcon) { 1104 1098 pr_err("Invalid durable fd [%p:%p]\n", fp->conn, fp->tcon); ··· 1113 1101 return -EBADF; 1114 1102 } 1115 1103 1116 - fp->conn = work->conn; 1104 + old_f_state = fp->f_state; 1105 + fp->f_state = FP_NEW; 1106 + __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); 1107 + if (!has_file_id(fp->volatile_id)) { 1108 + fp->f_state = old_f_state; 1109 + return -EBADF; 1110 + } 1111 + 1112 + fp->conn = conn; 1117 1113 fp->tcon = work->tcon; 1114 + 1115 + list_for_each_entry(smb_lock, &fp->lock_list, flist) { 1116 + spin_lock(&conn->llist_lock); 1117 + list_add_tail(&smb_lock->clist, &conn->lock_list); 1118 + spin_unlock(&conn->llist_lock); 1119 + } 1118 1120 1119 1121 ci = fp->f_ci; 1120 1122 down_write(&ci->m_lock); ··· 1140 1114 } 1141 1115 up_write(&ci->m_lock); 1142 1116 1143 - fp->f_state = FP_NEW; 1144 - __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); 1145 - if (!has_file_id(fp->volatile_id)) { 1146 - fp->conn = NULL; 1147 - fp->tcon = NULL; 1148 - return -EBADF; 1149 - } 1150 1117 return 0; 1151 1118 } 1152 1119