Skip to content

Commit db90c2c

Browse files
committed
smb: client: fix use-after-free in cifs_oplock_break
jira LE-4375 cve CVE-2025-38527 Rebuild_History Non-Buildable kernel-4.18.0-553.78.1.el8_10 commit-author Wang Zhaolong <wangzhaolong@huaweicloud.com> commit 705c791 Empty-Commit: Cherry-Pick Conflicts during history rebuild. Will be included in final tarball splat. Ref for failed cherry-pick at: ciq/ciq_backports/kernel-4.18.0-553.78.1.el8_10/705c7910.failed 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: Jonathan Maple <jmaple@ciq.com> # Conflicts: # fs/cifs/file.c
1 parent e8b85ba commit db90c2c

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
smb: client: fix use-after-free in cifs_oplock_break
2+
3+
jira LE-4375
4+
cve CVE-2025-38527
5+
Rebuild_History Non-Buildable kernel-4.18.0-553.78.1.el8_10
6+
commit-author Wang Zhaolong <wangzhaolong@huaweicloud.com>
7+
commit 705c79101ccf9edea5a00d761491a03ced314210
8+
Empty-Commit: Cherry-Pick Conflicts during history rebuild.
9+
Will be included in final tarball splat. Ref for failed cherry-pick at:
10+
ciq/ciq_backports/kernel-4.18.0-553.78.1.el8_10/705c7910.failed
11+
12+
A race condition can occur in cifs_oplock_break() leading to a
13+
use-after-free of the cinode structure when unmounting:
14+
15+
cifs_oplock_break()
16+
_cifsFileInfo_put(cfile)
17+
cifsFileInfo_put_final()
18+
cifs_sb_deactive()
19+
[last ref, start releasing sb]
20+
kill_sb()
21+
kill_anon_super()
22+
generic_shutdown_super()
23+
evict_inodes()
24+
dispose_list()
25+
evict()
26+
destroy_inode()
27+
call_rcu(&inode->i_rcu, i_callback)
28+
spin_lock(&cinode->open_file_lock) <- OK
29+
[later] i_callback()
30+
cifs_free_inode()
31+
kmem_cache_free(cinode)
32+
spin_unlock(&cinode->open_file_lock) <- UAF
33+
cifs_done_oplock_break(cinode) <- UAF
34+
35+
The issue occurs when umount has already released its reference to the
36+
superblock. When _cifsFileInfo_put() calls cifs_sb_deactive(), this
37+
releases the last reference, triggering the immediate cleanup of all
38+
inodes under RCU. However, cifs_oplock_break() continues to access the
39+
cinode after this point, resulting in use-after-free.
40+
41+
Fix this by holding an extra reference to the superblock during the
42+
entire oplock break operation. This ensures that the superblock and
43+
its inodes remain valid until the oplock break completes.
44+
45+
Link: https://bugzilla.kernel.org/show_bug.cgi?id=220309
46+
Fixes: b98749cac4a6 ("CIFS: keep FileInfo handle live during oplock break")
47+
Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
48+
Signed-off-by: Wang Zhaolong <wangzhaolong@huaweicloud.com>
49+
Signed-off-by: Steve French <stfrench@microsoft.com>
50+
(cherry picked from commit 705c79101ccf9edea5a00d761491a03ced314210)
51+
Signed-off-by: Jonathan Maple <jmaple@ciq.com>
52+
53+
# Conflicts:
54+
# fs/cifs/file.c
55+
diff --cc fs/cifs/file.c
56+
index 6aaac9bc59dc,1421bde045c2..000000000000
57+
--- a/fs/cifs/file.c
58+
+++ b/fs/cifs/file.c
59+
@@@ -4767,12 -3088,23 +4767,23 @@@ void cifs_oplock_break(struct work_stru
60+
struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
61+
oplock_break);
62+
struct inode *inode = d_inode(cfile->dentry);
63+
++<<<<<<< HEAD:fs/cifs/file.c
64+
++=======
65+
+ struct super_block *sb = inode->i_sb;
66+
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
67+
++>>>>>>> 705c79101ccf (smb: client: fix use-after-free in cifs_oplock_break):fs/smb/client/file.c
68+
struct cifsInodeInfo *cinode = CIFS_I(inode);
69+
- struct cifs_tcon *tcon;
70+
- struct TCP_Server_Info *server;
71+
- struct tcon_link *tlink;
72+
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
73+
+ struct TCP_Server_Info *server = tcon->ses->server;
74+
int rc = 0;
75+
- bool purge_cache = false, oplock_break_cancelled;
76+
- __u64 persistent_fid, volatile_fid;
77+
- __u16 net_fid;
78+
+ bool purge_cache = false;
79+
80+
+ /*
81+
+ * Hold a reference to the superblock to prevent it and its inodes from
82+
+ * being freed while we are accessing cinode. Otherwise, _cifsFileInfo_put()
83+
+ * may release the last reference to the sb and trigger inode eviction.
84+
+ */
85+
+ cifs_sb_active(sb);
86+
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
87+
TASK_UNINTERRUPTIBLE);
88+
89+
@@@ -4808,39 -3146,40 +4819,40 @@@
90+
91+
oplock_break_ack:
92+
/*
93+
- * When oplock break is received and there are no active
94+
- * file handles but cached, then schedule deferred close immediately.
95+
- * So, new open will not use cached handle.
96+
- */
97+
-
98+
- if (!CIFS_CACHE_HANDLE(cinode) && !list_empty(&cinode->deferred_closes))
99+
- cifs_close_deferred_file(cinode);
100+
-
101+
- persistent_fid = cfile->fid.persistent_fid;
102+
- volatile_fid = cfile->fid.volatile_fid;
103+
- net_fid = cfile->fid.netfid;
104+
- oplock_break_cancelled = cfile->oplock_break_cancelled;
105+
-
106+
- _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
107+
- /*
108+
- * MS-SMB2 3.2.5.19.1 and 3.2.5.19.2 (and MS-CIFS 3.2.5.42) do not require
109+
- * an acknowledgment to be sent when the file has already been closed.
110+
+ * releasing stale oplock after recent reconnect of smb session using
111+
+ * a now incorrect file handle is not a data integrity issue but do
112+
+ * not bother sending an oplock release if session to server still is
113+
+ * disconnected since oplock already released by the server
114+
*/
115+
- spin_lock(&cinode->open_file_lock);
116+
- /* check list empty since can race with kill_sb calling tree disconnect */
117+
- if (!oplock_break_cancelled && !list_empty(&cinode->openFileList)) {
118+
- spin_unlock(&cinode->open_file_lock);
119+
- rc = server->ops->oplock_response(tcon, persistent_fid,
120+
- volatile_fid, net_fid, cinode);
121+
+ if (!cfile->oplock_break_cancelled) {
122+
+ rc = tcon->ses->server->ops->oplock_response(tcon, &cfile->fid,
123+
+ cinode);
124+
cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
125+
- } else
126+
- spin_unlock(&cinode->open_file_lock);
127+
-
128+
- cifs_put_tlink(tlink);
129+
-out:
130+
+ }
131+
+ _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
132+
cifs_done_oplock_break(cinode);
133+
+ cifs_sb_deactive(sb);
134+
}
135+
136+
+/*
137+
+ * The presence of cifs_direct_io() in the address space ops vector
138+
+ * allowes open() O_DIRECT flags which would have failed otherwise.
139+
+ *
140+
+ * In the non-cached mode (mount with cache=none), we shunt off direct read and write requests
141+
+ * so this method should never be called.
142+
+ *
143+
+ * Direct IO is not yet supported in the cached mode.
144+
+ */
145+
+static ssize_t
146+
+cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter)
147+
+{
148+
+ /*
149+
+ * FIXME
150+
+ * Eventually need to support direct IO for non forcedirectio mounts
151+
+ */
152+
+ return -EINVAL;
153+
+}
154+
+
155+
static int cifs_swap_activate(struct swap_info_struct *sis,
156+
struct file *swap_file, sector_t *span)
157+
{
158+
* Unmerged path fs/cifs/file.c

0 commit comments

Comments
 (0)