Skip to content

Commit 991f8a7

Browse files
llllIIIllllsmfrench
authored andcommitted
ksmbd: vfs: fix race on m_flags in vfs_cache
ksmbd maintains delete-on-close and pending-delete state in ksmbd_inode->m_flags. In vfs_cache.c this field is accessed under inconsistent locking: some paths read and modify m_flags under ci->m_lock while others do so without taking the lock at all. Examples: - ksmbd_query_inode_status() and __ksmbd_inode_close() use ci->m_lock when checking or updating m_flags. - ksmbd_inode_pending_delete(), ksmbd_set_inode_pending_delete(), ksmbd_clear_inode_pending_delete() and ksmbd_fd_set_delete_on_close() used to read and modify m_flags without ci->m_lock. This creates a potential data race on m_flags when multiple threads open, close and delete the same file concurrently. In the worst case delete-on-close and pending-delete bits can be lost or observed in an inconsistent state, leading to confusing delete semantics (files that stay on disk after delete-on-close, or files that disappear while still in use). Fix it by: - Making ksmbd_query_inode_status() look at m_flags under ci->m_lock after dropping inode_hash_lock. - Adding ci->m_lock protection to all helpers that read or modify m_flags (ksmbd_inode_pending_delete(), ksmbd_set_inode_pending_delete(), ksmbd_clear_inode_pending_delete(), ksmbd_fd_set_delete_on_close()). - Keeping the existing ci->m_lock protection in __ksmbd_inode_close(), and moving the actual unlink/xattr removal outside the lock. This unifies the locking around m_flags and removes the data race while preserving the existing delete-on-close behaviour. Reported-by: Qianchang Zhao <pioooooooooip@gmail.com> Reported-by: Zhitong Liu <liuzhitong1993@gmail.com> Signed-off-by: Qianchang Zhao <pioooooooooip@gmail.com> Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent dc81b8f commit 991f8a7

1 file changed

Lines changed: 62 additions & 26 deletions

File tree

fs/smb/server/vfs_cache.c

Lines changed: 62 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -112,40 +112,62 @@ int ksmbd_query_inode_status(struct dentry *dentry)
112112

113113
read_lock(&inode_hash_lock);
114114
ci = __ksmbd_inode_lookup(dentry);
115-
if (ci) {
116-
ret = KSMBD_INODE_STATUS_OK;
117-
if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS))
118-
ret = KSMBD_INODE_STATUS_PENDING_DELETE;
119-
atomic_dec(&ci->m_count);
120-
}
121115
read_unlock(&inode_hash_lock);
116+
if (!ci)
117+
return ret;
118+
119+
down_read(&ci->m_lock);
120+
if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS))
121+
ret = KSMBD_INODE_STATUS_PENDING_DELETE;
122+
else
123+
ret = KSMBD_INODE_STATUS_OK;
124+
up_read(&ci->m_lock);
125+
126+
atomic_dec(&ci->m_count);
122127
return ret;
123128
}
124129

125130
bool ksmbd_inode_pending_delete(struct ksmbd_file *fp)
126131
{
127-
return (fp->f_ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS));
132+
struct ksmbd_inode *ci = fp->f_ci;
133+
int ret;
134+
135+
down_read(&ci->m_lock);
136+
ret = (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS));
137+
up_read(&ci->m_lock);
138+
139+
return ret;
128140
}
129141

130142
void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp)
131143
{
132-
fp->f_ci->m_flags |= S_DEL_PENDING;
144+
struct ksmbd_inode *ci = fp->f_ci;
145+
146+
down_write(&ci->m_lock);
147+
ci->m_flags |= S_DEL_PENDING;
148+
up_write(&ci->m_lock);
133149
}
134150

135151
void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp)
136152
{
137-
fp->f_ci->m_flags &= ~S_DEL_PENDING;
153+
struct ksmbd_inode *ci = fp->f_ci;
154+
155+
down_write(&ci->m_lock);
156+
ci->m_flags &= ~S_DEL_PENDING;
157+
up_write(&ci->m_lock);
138158
}
139159

140160
void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
141161
int file_info)
142162
{
143-
if (ksmbd_stream_fd(fp)) {
144-
fp->f_ci->m_flags |= S_DEL_ON_CLS_STREAM;
145-
return;
146-
}
163+
struct ksmbd_inode *ci = fp->f_ci;
147164

148-
fp->f_ci->m_flags |= S_DEL_ON_CLS;
165+
down_write(&ci->m_lock);
166+
if (ksmbd_stream_fd(fp))
167+
ci->m_flags |= S_DEL_ON_CLS_STREAM;
168+
else
169+
ci->m_flags |= S_DEL_ON_CLS;
170+
up_write(&ci->m_lock);
149171
}
150172

151173
static void ksmbd_inode_hash(struct ksmbd_inode *ci)
@@ -257,27 +279,41 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
257279
struct file *filp;
258280

259281
filp = fp->filp;
260-
if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) {
261-
ci->m_flags &= ~S_DEL_ON_CLS_STREAM;
262-
err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp),
263-
&filp->f_path,
264-
fp->stream.name,
265-
true);
266-
if (err)
267-
pr_err("remove xattr failed : %s\n",
268-
fp->stream.name);
282+
283+
if (ksmbd_stream_fd(fp)) {
284+
bool remove_stream_xattr = false;
285+
286+
down_write(&ci->m_lock);
287+
if (ci->m_flags & S_DEL_ON_CLS_STREAM) {
288+
ci->m_flags &= ~S_DEL_ON_CLS_STREAM;
289+
remove_stream_xattr = true;
290+
}
291+
up_write(&ci->m_lock);
292+
293+
if (remove_stream_xattr) {
294+
err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp),
295+
&filp->f_path,
296+
fp->stream.name,
297+
true);
298+
if (err)
299+
pr_err("remove xattr failed : %s\n",
300+
fp->stream.name);
301+
}
269302
}
270303

271304
if (atomic_dec_and_test(&ci->m_count)) {
305+
bool do_unlink = false;
306+
272307
down_write(&ci->m_lock);
273308
if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) {
274309
ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING);
275-
up_write(&ci->m_lock);
276-
ksmbd_vfs_unlink(filp);
277-
down_write(&ci->m_lock);
310+
do_unlink = true;
278311
}
279312
up_write(&ci->m_lock);
280313

314+
if (do_unlink)
315+
ksmbd_vfs_unlink(filp);
316+
281317
ksmbd_inode_free(ci);
282318
}
283319
}

0 commit comments

Comments
 (0)