summaryrefslogtreecommitdiffstats
path: root/fs/cifs/smb2ops.c
diff options
context:
space:
mode:
authorAurelien Aptel <aaptel@suse.com>2019-07-17 06:46:28 -0400
committerSteve French <stfrench@microsoft.com>2019-07-18 14:51:35 -0400
commit7e5a70ad88b1e6f6d9b934b2efb41afff496820f (patch)
tree9c13366cebf1ad4eccc41e40ca9005e55305d2f5 /fs/cifs/smb2ops.c
parentae9b728c8dc0a9939d89f84e8603258ca2a0df22 (diff)
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: 3d4ef9a15343 ("smb3: fix redundant opens on root") Signed-off-by: Aurelien Aptel <aaptel@suse.com> Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com> Signed-off-by: Steve French <stfrench@microsoft.com> CC: Stable <stable@vger.kernel.org>
Diffstat (limited to 'fs/cifs/smb2ops.c')
-rw-r--r--fs/cifs/smb2ops.c46
1 files changed, 45 insertions, 1 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 0cdc4e47ca87..fed75e1646c1 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -694,8 +694,51 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
694 694
695 smb2_set_related(&rqst[1]); 695 smb2_set_related(&rqst[1]);
696 696
697 /*
698 * We do not hold the lock for the open because in case
699 * SMB2_open needs to reconnect, it will end up calling
700 * cifs_mark_open_files_invalid() which takes the lock again
701 * thus causing a deadlock
702 */
703
704 mutex_unlock(&tcon->crfid.fid_mutex);
697 rc = compound_send_recv(xid, ses, flags, 2, rqst, 705 rc = compound_send_recv(xid, ses, flags, 2, rqst,
698 resp_buftype, rsp_iov); 706 resp_buftype, rsp_iov);
707 mutex_lock(&tcon->crfid.fid_mutex);
708
709 /*
710 * Now we need to check again as the cached root might have
711 * been successfully re-opened from a concurrent process
712 */
713
714 if (tcon->crfid.is_valid) {
715 /* work was already done */
716
717 /* stash fids for close() later */
718 struct cifs_fid fid = {
719 .persistent_fid = pfid->persistent_fid,
720 .volatile_fid = pfid->volatile_fid,
721 };
722
723 /*
724 * caller expects this func to set pfid to a valid
725 * cached root, so we copy the existing one and get a
726 * reference.
727 */
728 memcpy(pfid, tcon->crfid.fid, sizeof(*pfid));
729 kref_get(&tcon->crfid.refcount);
730
731 mutex_unlock(&tcon->crfid.fid_mutex);
732
733 if (rc == 0) {
734 /* close extra handle outside of crit sec */
735 SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
736 }
737 goto oshr_free;
738 }
739
740 /* Cached root is still invalid, continue normaly */
741
699 if (rc) 742 if (rc)
700 goto oshr_exit; 743 goto oshr_exit;
701 744
@@ -729,8 +772,9 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
729 (char *)&tcon->crfid.file_all_info)) 772 (char *)&tcon->crfid.file_all_info))
730 tcon->crfid.file_all_info_is_valid = 1; 773 tcon->crfid.file_all_info_is_valid = 1;
731 774
732 oshr_exit: 775oshr_exit:
733 mutex_unlock(&tcon->crfid.fid_mutex); 776 mutex_unlock(&tcon->crfid.fid_mutex);
777oshr_free:
734 SMB2_open_free(&rqst[0]); 778 SMB2_open_free(&rqst[0]);
735 SMB2_query_info_free(&rqst[1]); 779 SMB2_query_info_free(&rqst[1]);
736 free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 780 free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);