aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/smb2pdu.c
diff options
context:
space:
mode:
authorPavel Shilovsky <pshilov@microsoft.com>2016-11-29 11:31:23 -0800
committerPavel Shilovsky <pshilov@microsoft.com>2016-12-05 12:52:01 -0800
commit96a988ffeb90dba33a71c3826086fe67c897a183 (patch)
treeaeb1443b368e35a1af1bf5a51a55997eb5124371 /fs/cifs/smb2pdu.c
parentCIFS: Fix a possible memory corruption during reconnect (diff)
downloadlinux-dev-96a988ffeb90dba33a71c3826086fe67c897a183.tar.xz
linux-dev-96a988ffeb90dba33a71c3826086fe67c897a183.zip
CIFS: Fix a possible double locking of mutex during reconnect
With the current code it is possible to lock a mutex twice when a subsequent reconnects are triggered. On the 1st reconnect we reconnect sessions and tcons and then persistent file handles. If the 2nd reconnect happens during the reconnecting of persistent file handles then the following sequence of calls is observed: cifs_reopen_file -> SMB2_open -> small_smb2_init -> smb2_reconnect -> cifs_reopen_persistent_file_handles -> cifs_reopen_file (again!). So, we are trying to acquire the same cfile->fh_mutex twice which is wrong. Fix this by moving reconnecting of persistent handles to the delayed work (smb2_reconnect_server) and submitting this work every time we reconnect tcon in SMB2 commands handling codepath. This can also lead to corruption of a temporary file list in cifs_reopen_persistent_file_handles() because we can recursively call this function twice. Cc: Stable <stable@vger.kernel.org> # v4.9+ Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
Diffstat (limited to 'fs/cifs/smb2pdu.c')
-rw-r--r--fs/cifs/smb2pdu.c14
1 files changed, 9 insertions, 5 deletions
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 4ba3f68a1766..87457227812c 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -250,16 +250,19 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
}
cifs_mark_open_files_invalid(tcon);
+ if (tcon->use_persistent)
+ tcon->need_reopen_files = true;
rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage);
mutex_unlock(&tcon->ses->session_mutex);
- if (tcon->use_persistent)
- cifs_reopen_persistent_handles(tcon);
-
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
if (rc)
goto out;
+
+ if (smb2_command != SMB2_INTERNAL_CMD)
+ queue_delayed_work(cifsiod_wq, &server->reconnect, 0);
+
atomic_inc(&tconInfoReconnectCount);
out:
/*
@@ -1990,7 +1993,7 @@ void smb2_reconnect_server(struct work_struct *work)
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
- if (tcon->need_reconnect) {
+ if (tcon->need_reconnect || tcon->need_reopen_files) {
tcon->tc_count++;
list_add_tail(&tcon->rlist, &tmp_list);
tcon_exist = true;
@@ -2007,7 +2010,8 @@ void smb2_reconnect_server(struct work_struct *work)
spin_unlock(&cifs_tcp_ses_lock);
list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) {
- smb2_reconnect(SMB2_ECHO, tcon);
+ if (!smb2_reconnect(SMB2_INTERNAL_CMD, tcon))
+ cifs_reopen_persistent_handles(tcon);
list_del_init(&tcon->rlist);
cifs_put_tcon(tcon);
}