Skip to content

Commit 705c791

Browse files
Wang Zhaolongsmfrench
authored andcommitted
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) <[email protected]> Signed-off-by: Wang Zhaolong <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 347e9f5 commit 705c791

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
@@ -3088,7 +3088,8 @@ void cifs_oplock_break(struct work_struct *work)
30883088
struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
30893089
oplock_break);
30903090
struct inode *inode = d_inode(cfile->dentry);
3091-
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
3091+
struct super_block *sb = inode->i_sb;
3092+
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
30923093
struct cifsInodeInfo *cinode = CIFS_I(inode);
30933094
struct cifs_tcon *tcon;
30943095
struct TCP_Server_Info *server;
@@ -3098,6 +3099,12 @@ void cifs_oplock_break(struct work_struct *work)
30983099
__u64 persistent_fid, volatile_fid;
30993100
__u16 net_fid;
31003101

3102+
/*
3103+
* Hold a reference to the superblock to prevent it and its inodes from
3104+
* being freed while we are accessing cinode. Otherwise, _cifsFileInfo_put()
3105+
* may release the last reference to the sb and trigger inode eviction.
3106+
*/
3107+
cifs_sb_active(sb);
31013108
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
31023109
TASK_UNINTERRUPTIBLE);
31033110

@@ -3170,6 +3177,7 @@ void cifs_oplock_break(struct work_struct *work)
31703177
cifs_put_tlink(tlink);
31713178
out:
31723179
cifs_done_oplock_break(cinode);
3180+
cifs_sb_deactive(sb);
31733181
}
31743182

31753183
static int cifs_swap_activate(struct swap_info_struct *sis,

0 commit comments

Comments
 (0)