Skip to content

Commit 96a988f

Browse files
committed
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>
1 parent 53e0e11 commit 96a988f

File tree

4 files changed

+19
-6
lines changed

4 files changed

+19
-6
lines changed

fs/cifs/cifsglob.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,7 @@ struct cifs_tcon {
926926
bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */
927927
bool broken_sparse_sup; /* if server or share does not support sparse */
928928
bool need_reconnect:1; /* connection reset, tid now invalid */
929+
bool need_reopen_files:1; /* need to reopen tcon file handles */
929930
bool use_resilient:1; /* use resilient instead of durable handles */
930931
bool use_persistent:1; /* use persistent instead of durable handles */
931932
#ifdef CONFIG_CIFS_SMB2

fs/cifs/file.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,11 @@ cifs_reopen_persistent_handles(struct cifs_tcon *tcon)
777777
struct list_head *tmp1;
778778
struct list_head tmp_list;
779779

780+
if (!tcon->use_persistent || !tcon->need_reopen_files)
781+
return;
782+
783+
tcon->need_reopen_files = false;
784+
780785
cifs_dbg(FYI, "Reopen persistent handles");
781786
INIT_LIST_HEAD(&tmp_list);
782787

@@ -793,7 +798,8 @@ cifs_reopen_persistent_handles(struct cifs_tcon *tcon)
793798

794799
list_for_each_safe(tmp, tmp1, &tmp_list) {
795800
open_file = list_entry(tmp, struct cifsFileInfo, rlist);
796-
cifs_reopen_file(open_file, false /* do not flush */);
801+
if (cifs_reopen_file(open_file, false /* do not flush */))
802+
tcon->need_reopen_files = true;
797803
list_del_init(&open_file->rlist);
798804
cifsFileInfo_put(open_file);
799805
}

fs/cifs/smb2pdu.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -250,16 +250,19 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
250250
}
251251

252252
cifs_mark_open_files_invalid(tcon);
253+
if (tcon->use_persistent)
254+
tcon->need_reopen_files = true;
253255

254256
rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage);
255257
mutex_unlock(&tcon->ses->session_mutex);
256258

257-
if (tcon->use_persistent)
258-
cifs_reopen_persistent_handles(tcon);
259-
260259
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
261260
if (rc)
262261
goto out;
262+
263+
if (smb2_command != SMB2_INTERNAL_CMD)
264+
queue_delayed_work(cifsiod_wq, &server->reconnect, 0);
265+
263266
atomic_inc(&tconInfoReconnectCount);
264267
out:
265268
/*
@@ -1990,7 +1993,7 @@ void smb2_reconnect_server(struct work_struct *work)
19901993
spin_lock(&cifs_tcp_ses_lock);
19911994
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
19921995
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
1993-
if (tcon->need_reconnect) {
1996+
if (tcon->need_reconnect || tcon->need_reopen_files) {
19941997
tcon->tc_count++;
19951998
list_add_tail(&tcon->rlist, &tmp_list);
19961999
tcon_exist = true;
@@ -2007,7 +2010,8 @@ void smb2_reconnect_server(struct work_struct *work)
20072010
spin_unlock(&cifs_tcp_ses_lock);
20082011

20092012
list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) {
2010-
smb2_reconnect(SMB2_ECHO, tcon);
2013+
if (!smb2_reconnect(SMB2_INTERNAL_CMD, tcon))
2014+
cifs_reopen_persistent_handles(tcon);
20112015
list_del_init(&tcon->rlist);
20122016
cifs_put_tcon(tcon);
20132017
}

fs/cifs/smb2pdu.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@
8080
#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE)
8181
#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE)
8282

83+
#define SMB2_INTERNAL_CMD cpu_to_le16(0xFFFF)
84+
8385
#define NUMBER_OF_SMB2_COMMANDS 0x0013
8486

8587
/* BB FIXME - analyze following length BB */

0 commit comments

Comments
 (0)