diff options
| author | Wentao Liang <vulab@iscas.ac.cn> | 2026-09-17 16:34:39 +0000 |
|---|---|---|
| committer | Paulo Alcantara <pc@manguebit.org> | 2026-09-17 19:29:25 -0300 |
| commit | 717e0a25036b6c92cecace30913b2d874a4c22b8 (patch) | |
| tree | 32363e9fdd9a75ee9eef2c6a9ca9205406d3aab2 /fs | |
| parent | 5f0306e731e2f46e91419eae57eee3a241c055e0 (diff) | |
| download | lwn-717e0a25036b6c92cecace30913b2d874a4c22b8.tar.gz lwn-717e0a25036b6c92cecace30913b2d874a4c22b8.zip | |
cifs: Fix server use-after-free in cifs_chan_skip_or_disable()
When a secondary channel is no longer supported by the server,
cifs_chan_skip_or_disable() drops the channel reference with
cifs_put_tcp_session() and then continues to use the server pointer by
calling cifs_signal_cifsd_for_reconnect() on it and reading its
primary_server pointer. cifs_put_tcp_session() can drop the last
reference of the channel and tear it down, so both the channel and the
primary server (whose reference is also dropped by
cifs_put_tcp_session()) can be freed before they are signaled for
reconnect.
Signal the channel and the primary server and capture the primary
server pointer before dropping the channel reference with
cifs_put_tcp_session().
Fixes: f591062bdbf4 ("cifs: handle servers that still advertise multichannel after disabling")
Cc: stable@vger.kernel.org
Signed-off-by: Wentao Liang <vulab@iscas.ac.cn>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/smb/client/smb2pdu.c | 13 |
1 files changed, 7 insertions, 6 deletions
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index dea05aeb53a1..880ce12f50c4 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -189,18 +189,19 @@ cifs_chan_skip_or_disable(struct cifs_ses *ses, spin_unlock(&ses->chan_lock); /* - * the above reference of server by channel - * needs to be dropped without holding chan_lock - * as cifs_put_tcp_session takes a higher lock - * i.e. cifs_tcp_ses_lock + * signal the channel and its primary server to + * reconnect before dropping the above reference of + * server by channel, which is done without holding + * chan_lock as cifs_put_tcp_session takes a higher + * lock i.e. cifs_tcp_ses_lock */ - cifs_put_tcp_session(server, from_reconnect); - cifs_signal_cifsd_for_reconnect(server, false); /* mark primary server as needing reconnect */ pserver = server->primary_server; cifs_signal_cifsd_for_reconnect(pserver, false); + + cifs_put_tcp_session(server, from_reconnect); skip_terminate: return -EHOSTDOWN; } |
