summaryrefslogtreecommitdiff
path: root/fs/cifs
diff options
context:
space:
mode:
authorAurelien Aptel <aaptel@suse.com>2019-07-17 12:46:28 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-10-01 08:26:03 +0200
commite867ef11300420fc1244258c150d3fa50ff8cbfa (patch)
treeeeebc7f36e444df9cedd461a367db1a6c8fe38cc /fs/cifs
parentf3160a1d71f7eeb1b177f1bf48b87d7b97df1d48 (diff)
CIFS: fix deadlock in cached root handling
commit 7e5a70ad88b1e6f6d9b934b2efb41afff496820f upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/smb2ops.c44
1 files changed, 44 insertions, 0 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index cc9e846a3865..094be406cde4 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -553,7 +553,50 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
oparams.fid = pfid;
oparams.reconnect = false;
+ /*
+ * 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 = SMB2_open(xid, &oparams, &srch_path, &oplock, NULL, NULL, NULL);
+ 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 critical section */
+ SMB2_close(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid);
+ }
+ return 0;
+ }
+
+ /* Cached root is still invalid, continue normaly */
+
if (rc == 0) {
memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid));
tcon->crfid.tcon = tcon;
@@ -561,6 +604,7 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
kref_init(&tcon->crfid.refcount);
kref_get(&tcon->crfid.refcount);
}
+
mutex_unlock(&tcon->crfid.fid_mutex);
return rc;
}