cifs: lock chan_lock outside match_session
authorShyam Prasad N <sprasad@microsoft.com>
Mon, 20 Feb 2023 13:02:11 +0000 (13:02 +0000)
committerSteve French <stfrench@microsoft.com>
Thu, 1 Jun 2023 04:22:32 +0000 (23:22 -0500)
Coverity had rightly indicated a possible deadlock
due to chan_lock being done inside match_session.
All callers of match_* functions should pick up the
necessary locks and call them.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Reviewed-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
Cc: stable@vger.kernel.org
Fixes: 724244cdb382 ("cifs: protect session channel fields with chan_lock")
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/connect.c

index 5a43ff9c44829a7648aeb59c10222496c771f993..1f56b60642577403a0aeb038d3872486c56d7c6f 100644 (file)
@@ -1732,7 +1732,7 @@ out_err:
        return ERR_PTR(rc);
 }
 
-/* this function must be called with ses_lock held */
+/* this function must be called with ses_lock and chan_lock held */
 static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx)
 {
        if (ctx->sectype != Unspecified &&
@@ -1743,12 +1743,8 @@ static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx)
         * If an existing session is limited to less channels than
         * requested, it should not be reused
         */
-       spin_lock(&ses->chan_lock);
-       if (ses->chan_max < ctx->max_channels) {
-               spin_unlock(&ses->chan_lock);
+       if (ses->chan_max < ctx->max_channels)
                return 0;
-       }
-       spin_unlock(&ses->chan_lock);
 
        switch (ses->sectype) {
        case Kerberos:
@@ -1876,10 +1872,13 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
                        spin_unlock(&ses->ses_lock);
                        continue;
                }
+               spin_lock(&ses->chan_lock);
                if (!match_session(ses, ctx)) {
+                       spin_unlock(&ses->chan_lock);
                        spin_unlock(&ses->ses_lock);
                        continue;
                }
+               spin_unlock(&ses->chan_lock);
                spin_unlock(&ses->ses_lock);
 
                ++ses->ses_count;
@@ -2704,6 +2703,7 @@ cifs_match_super(struct super_block *sb, void *data)
 
        spin_lock(&tcp_srv->srv_lock);
        spin_lock(&ses->ses_lock);
+       spin_lock(&ses->chan_lock);
        spin_lock(&tcon->tc_lock);
        if (!match_server(tcp_srv, ctx, dfs_super_cmp) ||
            !match_session(ses, ctx) ||
@@ -2716,6 +2716,7 @@ cifs_match_super(struct super_block *sb, void *data)
        rc = compare_mount_options(sb, mnt_data);
 out:
        spin_unlock(&tcon->tc_lock);
+       spin_unlock(&ses->chan_lock);
        spin_unlock(&ses->ses_lock);
        spin_unlock(&tcp_srv->srv_lock);