diff options
author | Aurelien Aptel <aaptel@suse.com> | 2019-07-17 06:46:28 -0400 |
---|---|---|
committer | Steve French <stfrench@microsoft.com> | 2019-07-18 14:51:35 -0400 |
commit | 7e5a70ad88b1e6f6d9b934b2efb41afff496820f (patch) | |
tree | 9c13366cebf1ad4eccc41e40ca9005e55305d2f5 /fs/cifs/smb2ops.c | |
parent | ae9b728c8dc0a9939d89f84e8603258ca2a0df22 (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.c | 46 |
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: | 775 | oshr_exit: |
733 | mutex_unlock(&tcon->crfid.fid_mutex); | 776 | mutex_unlock(&tcon->crfid.fid_mutex); |
777 | oshr_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); |