Commit 7e5a70ad authored by Aurelien Aptel's avatar Aurelien Aptel Committed by Steve French
Browse files

CIFS: fix deadlock in cached root handling

Prevent deadlock between open_shroot() and
cifs_mark_open_files_invalid() by releasing the lock before entering
SMB2_open, taking it again after and checking if we still need to use
the result.

Link: https://lore.kernel.org/linux-cifs/684ed01c-cbca-2716-bc28-b0a59a0f8521@prodrive-technologies.com/T/#u


Fixes: 3d4ef9a1 ("smb3: fix redundant opens on root")
Signed-off-by: default avatarAurelien Aptel <aaptel@suse.com>
Reviewed-by: default avatarPavel Shilovsky <pshilov@microsoft.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
CC: Stable <stable@vger.kernel.org>
parent ae9b728c
Loading
Loading
Loading
Loading
+45 −1
Original line number Diff line number Diff line
@@ -694,8 +694,51 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)

	smb2_set_related(&rqst[1]);

	/*
	 * We do not hold the lock for the open because in case
	 * SMB2_open needs to reconnect, it will end up calling
	 * cifs_mark_open_files_invalid() which takes the lock again
	 * thus causing a deadlock
	 */

	mutex_unlock(&tcon->crfid.fid_mutex);
	rc = compound_send_recv(xid, ses, flags, 2, rqst,
				resp_buftype, rsp_iov);
	mutex_lock(&tcon->crfid.fid_mutex);

	/*
	 * Now we need to check again as the cached root might have
	 * been successfully re-opened from a concurrent process
	 */

	if (tcon->crfid.is_valid) {
		/* work was already done */

		/* stash fids for close() later */
		struct cifs_fid fid = {
			.persistent_fid = pfid->persistent_fid,
			.volatile_fid = pfid->volatile_fid,
		};

		/*
		 * caller expects this func to set pfid to a valid
		 * cached root, so we copy the existing one and get a
		 * reference.
		 */
		memcpy(pfid, tcon->crfid.fid, sizeof(*pfid));
		kref_get(&tcon->crfid.refcount);

		mutex_unlock(&tcon->crfid.fid_mutex);

		if (rc == 0) {
			/* close extra handle outside of crit sec */
			SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
		}
		goto oshr_free;
	}

	/* Cached root is still invalid, continue normaly */

	if (rc)
		goto oshr_exit;

@@ -731,6 +774,7 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)

oshr_exit:
	mutex_unlock(&tcon->crfid.fid_mutex);
oshr_free:
	SMB2_open_free(&rqst[0]);
	SMB2_query_info_free(&rqst[1]);
	free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);