Skip to content

Commit 55ecb94

Browse files
committed
smb: client: fix use-after-free in cifs_oplock_break
JIRA: https://issues.redhat.com/browse/RHEL-104401 Conflicts: - Context difference due to missing upstream commit 742b344 ("cifs: Remove some code that's no longer used, part 1") in RHEL 9.7. commit 705c791 Author: Wang Zhaolong <wangzhaolong@huaweicloud.com> Date: Mon Jul 7 09:09:26 2025 +0800 smb: client: fix use-after-free in cifs_oplock_break A race condition can occur in cifs_oplock_break() leading to a use-after-free of the cinode structure when unmounting: cifs_oplock_break() _cifsFileInfo_put(cfile) cifsFileInfo_put_final() cifs_sb_deactive() [last ref, start releasing sb] kill_sb() kill_anon_super() generic_shutdown_super() evict_inodes() dispose_list() evict() destroy_inode() call_rcu(&inode->i_rcu, i_callback) spin_lock(&cinode->open_file_lock) <- OK [later] i_callback() cifs_free_inode() kmem_cache_free(cinode) spin_unlock(&cinode->open_file_lock) <- UAF cifs_done_oplock_break(cinode) <- UAF The issue occurs when umount has already released its reference to the superblock. When _cifsFileInfo_put() calls cifs_sb_deactive(), this releases the last reference, triggering the immediate cleanup of all inodes under RCU. However, cifs_oplock_break() continues to access the cinode after this point, resulting in use-after-free. Fix this by holding an extra reference to the superblock during the entire oplock break operation. This ensures that the superblock and its inodes remain valid until the oplock break completes. Link: https://bugzilla.kernel.org/show_bug.cgi?id=220309 Fixes: b98749c ("CIFS: keep FileInfo handle live during oplock break") Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org> Signed-off-by: Wang Zhaolong <wangzhaolong@huaweicloud.com> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Paulo Alcantara <paalcant@redhat.com>
1 parent b3bf1ba commit 55ecb94

File tree

1 file changed

+9
-1
lines changed

1 file changed

+9
-1
lines changed

fs/smb/client/file.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5263,7 +5263,8 @@ void cifs_oplock_break(struct work_struct *work)
52635263
struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
52645264
oplock_break);
52655265
struct inode *inode = d_inode(cfile->dentry);
5266-
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
5266+
struct super_block *sb = inode->i_sb;
5267+
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
52675268
struct cifsInodeInfo *cinode = CIFS_I(inode);
52685269
struct cifs_tcon *tcon;
52695270
struct TCP_Server_Info *server;
@@ -5273,6 +5274,12 @@ void cifs_oplock_break(struct work_struct *work)
52735274
__u64 persistent_fid, volatile_fid;
52745275
__u16 net_fid;
52755276

5277+
/*
5278+
* Hold a reference to the superblock to prevent it and its inodes from
5279+
* being freed while we are accessing cinode. Otherwise, _cifsFileInfo_put()
5280+
* may release the last reference to the sb and trigger inode eviction.
5281+
*/
5282+
cifs_sb_active(sb);
52765283
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
52775284
TASK_UNINTERRUPTIBLE);
52785285

@@ -5345,6 +5352,7 @@ void cifs_oplock_break(struct work_struct *work)
53455352
cifs_put_tlink(tlink);
53465353
out:
53475354
cifs_done_oplock_break(cinode);
5355+
cifs_sb_deactive(sb);
53485356
}
53495357

53505358
/*

0 commit comments

Comments
 (0)