[PATCH] ksmbd: protect stream_del_pending with fp->f_lock
"Gaël Blivet-Bailly" <[email protected]>
| Newsgroups | org.kernel.vger.linux-cifs |
|---|---|
| Message-ID | <[email protected]> |
From: Gael Blivet <[email protected]> Follow-up to commit 36442851a4d1 ("ksmbd: route stream FileDispositionInformation through stream delete flag"): fp's stream_del_pending is read and written from three call sites (ksmbd_inode_pending_delete(), ksmbd_fd_set_delete_pending()/ ksmbd_fd_clear_delete_pending(), and __ksmbd_inode_close()) with no locking at all, unlike every other piece of shared per-handle mutable state in this file, which uses fp->f_lock. A FileDispositionInformation SET_INFO racing a concurrent close on the same handle can see a stale value and skip removing the stream's xattr even though the client asked for deletion. Protect all three touch points with fp->f_lock, matching the existing convention (e.g. notify_pendings). Signed-off-by: Gael Blivet <[email protected]> --- fs/smb/server/vfs_cache.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index b393290b8..42b03e449 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -236,8 +236,14 @@ bool ksmbd_inode_pending_delete(struct ksmbd_file *fp) * also incorrectly report a whole-file pending-delete as applying * to an unrelated stream handle on the same inode. */ - if (ksmbd_stream_fd(fp)) - return fp->stream_del_pending; + if (ksmbd_stream_fd(fp)) { + bool pending; + + spin_lock(&fp->f_lock); + pending = fp->stream_del_pending; + spin_unlock(&fp->f_lock); + return pending; + } down_read(&ci->m_lock); ret = (ci->m_flags & S_DEL_PENDING); @@ -318,18 +324,24 @@ void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, */ void ksmbd_fd_set_delete_pending(struct ksmbd_file *fp) { - if (ksmbd_stream_fd(fp)) + if (ksmbd_stream_fd(fp)) { + spin_lock(&fp->f_lock); fp->stream_del_pending = true; - else + spin_unlock(&fp->f_lock); + } else { ksmbd_set_inode_pending_delete(fp); + } } void ksmbd_fd_clear_delete_pending(struct ksmbd_file *fp) { - if (ksmbd_stream_fd(fp)) + if (ksmbd_stream_fd(fp)) { + spin_lock(&fp->f_lock); fp->stream_del_pending = false; - else + spin_unlock(&fp->f_lock); + } else { ksmbd_clear_inode_pending_delete(fp); + } } static void ksmbd_inode_hash(struct ksmbd_inode *ci) @@ -458,10 +470,12 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) * the inode-wide flag above, which only ever meant "some * stream on this file" with no way to say which one. */ + spin_lock(&fp->f_lock); if (fp->stream_del_pending) { fp->stream_del_pending = false; remove_stream_xattr = true; } + spin_unlock(&fp->f_lock); if (remove_stream_xattr) { const struct cred *saved_cred; base-commit: 5e6eeafe46a4a19559808c5ef36ea36ea4f2a204 -- 2.43.0