Skip to content

Commit 069d68b

Browse files
smb: client: fix use-after-free in cifs_oplock_break
jira VULN-131074 cve CVE-2025-38527 commit-author Wang Zhaolong <wangzhaolong@huaweicloud.com> commit 705c791 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> (cherry picked from commit 705c791) Signed-off-by: Shreeya Patel <spatel@ciq.com>
1 parent 4c2efe4 commit 069d68b

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
@@ -5090,7 +5090,8 @@ void cifs_oplock_break(struct work_struct *work)
50905090
struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
50915091
oplock_break);
50925092
struct inode *inode = d_inode(cfile->dentry);
5093-
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
5093+
struct super_block *sb = inode->i_sb;
5094+
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
50945095
struct cifsInodeInfo *cinode = CIFS_I(inode);
50955096
struct cifs_tcon *tcon;
50965097
struct TCP_Server_Info *server;
@@ -5100,6 +5101,12 @@ void cifs_oplock_break(struct work_struct *work)
51005101
__u64 persistent_fid, volatile_fid;
51015102
__u16 net_fid;
51025103

5104+
/*
5105+
* Hold a reference to the superblock to prevent it and its inodes from
5106+
* being freed while we are accessing cinode. Otherwise, _cifsFileInfo_put()
5107+
* may release the last reference to the sb and trigger inode eviction.
5108+
*/
5109+
cifs_sb_active(sb);
51035110
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
51045111
TASK_UNINTERRUPTIBLE);
51055112

@@ -5172,6 +5179,7 @@ void cifs_oplock_break(struct work_struct *work)
51725179
cifs_put_tlink(tlink);
51735180
out:
51745181
cifs_done_oplock_break(cinode);
5182+
cifs_sb_deactive(sb);
51755183
}
51765184

51775185
/*

0 commit comments

Comments
 (0)