Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cifs: Fix infinite loop when using hard mount option

For every request we send, whether it is SMB1 or SMB2+, we attempt to
reconnect tcon (cifs_reconnect_tcon or smb2_reconnect) before carrying
out the request.

So, while server->tcpStatus != CifsNeedReconnect, we wait for the
reconnection to succeed on wait_event_interruptible_timeout(). If it
returns, that means that either the condition was evaluated to true, or
timeout elapsed, or it was interrupted by a signal.

Since we're not handling the case where the process woke up due to a
received signal (-ERESTARTSYS), the next call to
wait_event_interruptible_timeout() will _always_ fail and we end up
looping forever inside either cifs_reconnect_tcon() or smb2_reconnect().

Here's an example of how to trigger that:

$ mount.cifs //foo/share /mnt/test -o
username=foo,password=foo,vers=1.0,hard

(break connection to server before executing bellow cmd)
$ stat -f /mnt/test & sleep 140
[1] 2511

$ ps -aux -q 2511
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2511 0.0 0.0 12892 1008 pts/0 S 12:24 0:00 stat -f
/mnt/test

$ kill -9 2511

(wait for a while; process is stuck in the kernel)
$ ps -aux -q 2511
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2511 83.2 0.0 12892 1008 pts/0 R 12:24 30:01 stat -f
/mnt/test

By using 'hard' mount point means that cifs.ko will keep retrying
indefinitely, however we must allow the process to be killed otherwise
it would hang the system.

Signed-off-by: Paulo Alcantara <palcantara@suse.de>
Cc: stable@vger.kernel.org
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Paulo Alcantara and committed by
Steve French
7ffbe655 f46ecbd9

+20 -8
+8 -2
fs/cifs/cifssmb.c
··· 157 157 * greater than cifs socket timeout which is 7 seconds 158 158 */ 159 159 while (server->tcpStatus == CifsNeedReconnect) { 160 - wait_event_interruptible_timeout(server->response_q, 161 - (server->tcpStatus != CifsNeedReconnect), 10 * HZ); 160 + rc = wait_event_interruptible_timeout(server->response_q, 161 + (server->tcpStatus != CifsNeedReconnect), 162 + 10 * HZ); 163 + if (rc < 0) { 164 + cifs_dbg(FYI, "%s: aborting reconnect due to a received" 165 + " signal by the process\n", __func__); 166 + return -ERESTARTSYS; 167 + } 162 168 163 169 /* are we still trying to reconnect? */ 164 170 if (server->tcpStatus != CifsNeedReconnect)
+12 -6
fs/cifs/smb2pdu.c
··· 155 155 static int 156 156 smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) 157 157 { 158 - int rc = 0; 158 + int rc; 159 159 struct nls_table *nls_codepage; 160 160 struct cifs_ses *ses; 161 161 struct TCP_Server_Info *server; ··· 166 166 * for those three - in the calling routine. 167 167 */ 168 168 if (tcon == NULL) 169 - return rc; 169 + return 0; 170 170 171 171 if (smb2_command == SMB2_TREE_CONNECT) 172 - return rc; 172 + return 0; 173 173 174 174 if (tcon->tidStatus == CifsExiting) { 175 175 /* ··· 212 212 return -EAGAIN; 213 213 } 214 214 215 - wait_event_interruptible_timeout(server->response_q, 216 - (server->tcpStatus != CifsNeedReconnect), 10 * HZ); 215 + rc = wait_event_interruptible_timeout(server->response_q, 216 + (server->tcpStatus != CifsNeedReconnect), 217 + 10 * HZ); 218 + if (rc < 0) { 219 + cifs_dbg(FYI, "%s: aborting reconnect due to a received" 220 + " signal by the process\n", __func__); 221 + return -ERESTARTSYS; 222 + } 217 223 218 224 /* are we still trying to reconnect? */ 219 225 if (server->tcpStatus != CifsNeedReconnect) ··· 237 231 } 238 232 239 233 if (!tcon->ses->need_reconnect && !tcon->need_reconnect) 240 - return rc; 234 + return 0; 241 235 242 236 nls_codepage = load_nls_default(); 243 237