cifs: Fix infinite loop when using hard mount option
authorPaulo Alcantara <paulo@paulo.ac>
Thu, 5 Jul 2018 16:46:34 +0000 (13:46 -0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 11 Jul 2018 14:29:15 +0000 (16:29 +0200)
commit 7ffbe65578b44fafdef577a360eb0583929f7c6e upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/cifs/cifssmb.c
fs/cifs/smb2pdu.c

index 7fd39ea..b5a4365 100644 (file)
@@ -150,8 +150,14 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
         * greater than cifs socket timeout which is 7 seconds
         */
        while (server->tcpStatus == CifsNeedReconnect) {
-               wait_event_interruptible_timeout(server->response_q,
-                       (server->tcpStatus != CifsNeedReconnect), 10 * HZ);
+               rc = wait_event_interruptible_timeout(server->response_q,
+                                                     (server->tcpStatus != CifsNeedReconnect),
+                                                     10 * HZ);
+               if (rc < 0) {
+                       cifs_dbg(FYI, "%s: aborting reconnect due to a received"
+                                " signal by the process\n", __func__);
+                       return -ERESTARTSYS;
+               }
 
                /* are we still trying to reconnect? */
                if (server->tcpStatus != CifsNeedReconnect)
index 5247b40..71b8198 100644 (file)
@@ -153,7 +153,7 @@ out:
 static int
 smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
 {
-       int rc = 0;
+       int rc;
        struct nls_table *nls_codepage;
        struct cifs_ses *ses;
        struct TCP_Server_Info *server;
@@ -164,10 +164,10 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
         * for those three - in the calling routine.
         */
        if (tcon == NULL)
-               return rc;
+               return 0;
 
        if (smb2_command == SMB2_TREE_CONNECT)
-               return rc;
+               return 0;
 
        if (tcon->tidStatus == CifsExiting) {
                /*
@@ -210,8 +210,14 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
                        return -EAGAIN;
                }
 
-               wait_event_interruptible_timeout(server->response_q,
-                       (server->tcpStatus != CifsNeedReconnect), 10 * HZ);
+               rc = wait_event_interruptible_timeout(server->response_q,
+                                                     (server->tcpStatus != CifsNeedReconnect),
+                                                     10 * HZ);
+               if (rc < 0) {
+                       cifs_dbg(FYI, "%s: aborting reconnect due to a received"
+                                " signal by the process\n", __func__);
+                       return -ERESTARTSYS;
+               }
 
                /* are we still trying to reconnect? */
                if (server->tcpStatus != CifsNeedReconnect)
@@ -229,7 +235,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
        }
 
        if (!tcon->ses->need_reconnect && !tcon->need_reconnect)
-               return rc;
+               return 0;
 
        nls_codepage = load_nls_default();