Skip to content

Commit 63dc0ae

Browse files
committed
smb: client: fix use-after-free in cifs_oplock_break
jira VULN-131073 cve CVE-2025-38527 commit-author Wang Zhaolong <[email protected]> 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) <[email protected]> Signed-off-by: Wang Zhaolong <[email protected]> Signed-off-by: Steve French <[email protected]> (cherry picked from commit 705c791) Signed-off-by: Brett Mastbergen <[email protected]>
1 parent c09abe8 commit 63dc0ae

File tree

1 file changed

+9
-1
lines changed

1 file changed

+9
-1
lines changed

fs/cifs/file.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5048,7 +5048,8 @@ void cifs_oplock_break(struct work_struct *work)
50485048
struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
50495049
oplock_break);
50505050
struct inode *inode = d_inode(cfile->dentry);
5051-
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
5051+
struct super_block *sb = inode->i_sb;
5052+
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
50525053
struct cifsInodeInfo *cinode = CIFS_I(inode);
50535054
struct cifs_tcon *tcon;
50545055
struct TCP_Server_Info *server;
@@ -5058,6 +5059,12 @@ void cifs_oplock_break(struct work_struct *work)
50585059
__u64 persistent_fid, volatile_fid;
50595060
__u16 net_fid;
50605061

5062+
/*
5063+
* Hold a reference to the superblock to prevent it and its inodes from
5064+
* being freed while we are accessing cinode. Otherwise, _cifsFileInfo_put()
5065+
* may release the last reference to the sb and trigger inode eviction.
5066+
*/
5067+
cifs_sb_active(sb);
50615068
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
50625069
TASK_UNINTERRUPTIBLE);
50635070

@@ -5130,6 +5137,7 @@ void cifs_oplock_break(struct work_struct *work)
51305137
cifs_put_tlink(tlink);
51315138
out:
51325139
cifs_done_oplock_break(cinode);
5140+
cifs_sb_deactive(sb);
51335141
}
51345142

51355143
/*

0 commit comments

Comments
 (0)