[PATCH 1/2] smb: client: hold server ref while queueing reconnect work
From: Joseph Qi
Date: Tue Nov 03 2026 - 04:24:44 EST
When query_server_interfaces() returns -EOPNOTSUPP on a secondary
channel, smb2_reconnect() calls smb3_update_ses_channels() which for a
channel runs cifs_chan_skip_or_disable(). That clears the channel's
slot in the session and calls cifs_put_tcp_session(), which can drop
the last reference of the channel and tear it down.
smb2_reconnect() then falls through to skip_add_channels and queues
reconnect work on that server, using a server that may already be
freed. The send path obtained the channel from cifs_pick_channel(),
which does not take a reference, so the session's channel reference is
the only one.
Take our own reference on the channel before calling
smb3_update_ses_channels() and drop it after the reconnect work has
been queued, so the channel cannot be freed while it is still used.
Fixes: f591062bdbf4 ("cifs: handle servers that still advertise multichannel after disabling")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Joseph Qi <joseph.qi@xxxxxxxxxxxxxxxxx>
---
fs/smb/client/smb2pdu.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index f85595136ef8a..8e53e9e02df6c 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -253,6 +253,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
struct cifs_ses *ses;
int xid;
int rc = 0;
+ bool hold_server_ref = false;
/*
* SMB2s NegProt, SessSetup, Logoff do not have tcon yet so
@@ -470,6 +471,20 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
* treat this as server not supporting multichannel
*/
+ /*
+ * If server is a secondary channel,
+ * cifs_chan_skip_or_disable() called below may
+ * drop its last reference, so hold our own
+ * reference until the reconnect work below has
+ * been queued.
+ */
+ spin_lock(&cifs_tcp_ses_lock);
+ if (server->srv_count) {
+ server->srv_count++;
+ hold_server_ref = true;
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+
rc = smb3_update_ses_channels(ses, server,
from_reconnect,
true /* disable_mchan */);
@@ -498,6 +513,8 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
if (smb2_command != SMB2_INTERNAL_CMD)
cifs_queue_server_reconn(server);
+ if (hold_server_ref)
+ cifs_put_tcp_session(server, from_reconnect);
atomic_inc(&tconInfoReconnectCount);
out:
--
2.39.3