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: f591062bdb ("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>
This commit is contained in:
Wentao Liang 2026-09-17 16:34:39 +00:00 committed by Paulo Alcantara
parent 5f0306e731
commit 717e0a2503

View File

@ -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;
}