[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.