diff mbox series

smb: client: fix missed ses refcounting

Message ID 20230711171510.5295-1-pc@manguebit.com (mailing list archive)
State New, archived
Headers show
Series smb: client: fix missed ses refcounting | expand

Commit Message

Paulo Alcantara July 11, 2023, 5:15 p.m. UTC
Use new cifs_smb_ses_inc_refcount() helper to get an active reference
of @ses and @ses->dfs_root_ses (if set).  This will prevent
@ses->dfs_root_ses of being put in the next call to cifs_put_smb_ses()
and thus potentially causing an use-after-free bug.

Fixes: 8e3554150d6c ("cifs: fix sharing of DFS connections")
Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
---
 fs/smb/client/dfs.c           | 26 ++++++++++----------------
 fs/smb/client/smb2transport.c |  2 +-
 2 files changed, 11 insertions(+), 17 deletions(-)

Comments

Steve French July 12, 2023, 4 p.m. UTC | #1
Tentatively merged into cifs-2.6.git for-next pending testing

On Tue, Jul 11, 2023 at 12:15 PM Paulo Alcantara <pc@manguebit.com> wrote:
>
> Use new cifs_smb_ses_inc_refcount() helper to get an active reference
> of @ses and @ses->dfs_root_ses (if set).  This will prevent
> @ses->dfs_root_ses of being put in the next call to cifs_put_smb_ses()
> and thus potentially causing an use-after-free bug.
>
> Fixes: 8e3554150d6c ("cifs: fix sharing of DFS connections")
> Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
> ---
>  fs/smb/client/dfs.c           | 26 ++++++++++----------------
>  fs/smb/client/smb2transport.c |  2 +-
>  2 files changed, 11 insertions(+), 17 deletions(-)
>
> diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c
> index 1403a2d1ab17..df3fd3b720da 100644
> --- a/fs/smb/client/dfs.c
> +++ b/fs/smb/client/dfs.c
> @@ -66,6 +66,12 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
>         return rc;
>  }
>
> +/*
> + * Track individual DFS referral servers used by new DFS mount.
> + *
> + * On success, their lifetime will be shared by final tcon (dfs_ses_list).
> + * Otherwise, they will be put by dfs_put_root_smb_sessions() in cifs_mount().
> + */
>  static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
>  {
>         struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
> @@ -80,11 +86,12 @@ static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
>                 INIT_LIST_HEAD(&root_ses->list);
>
>                 spin_lock(&cifs_tcp_ses_lock);
> -               ses->ses_count++;
> +               cifs_smb_ses_inc_refcount(ses);
>                 spin_unlock(&cifs_tcp_ses_lock);
>                 root_ses->ses = ses;
>                 list_add_tail(&root_ses->list, &mnt_ctx->dfs_ses_list);
>         }
> +       /* Select new DFS referral server so that new referrals go through it */
>         ctx->dfs_root_ses = ses;
>         return 0;
>  }
> @@ -242,7 +249,6 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
>  int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
>  {
>         struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
> -       struct cifs_ses *ses;
>         bool nodfs = ctx->nodfs;
>         int rc;
>
> @@ -276,20 +282,8 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
>         }
>
>         *isdfs = true;
> -       /*
> -        * Prevent DFS root session of being put in the first call to
> -        * cifs_mount_put_conns().  If another DFS root server was not found
> -        * while chasing the referrals (@ctx->dfs_root_ses == @ses), then we
> -        * can safely put extra refcount of @ses.
> -        */
> -       ses = mnt_ctx->ses;
> -       mnt_ctx->ses = NULL;
> -       mnt_ctx->server = NULL;
> -       rc = __dfs_mount_share(mnt_ctx);
> -       if (ses == ctx->dfs_root_ses)
> -               cifs_put_smb_ses(ses);
> -
> -       return rc;
> +       add_root_smb_session(mnt_ctx);
> +       return __dfs_mount_share(mnt_ctx);
>  }
>
>  /* Update dfs referral path of superblock */
> diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c
> index c6db898dab7c..7676091b3e77 100644
> --- a/fs/smb/client/smb2transport.c
> +++ b/fs/smb/client/smb2transport.c
> @@ -160,7 +160,7 @@ smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
>                         spin_unlock(&ses->ses_lock);
>                         continue;
>                 }
> -               ++ses->ses_count;
> +               cifs_smb_ses_inc_refcount(ses);
>                 spin_unlock(&ses->ses_lock);
>                 return ses;
>         }
> --
> 2.41.0
>
diff mbox series

Patch

diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c
index 1403a2d1ab17..df3fd3b720da 100644
--- a/fs/smb/client/dfs.c
+++ b/fs/smb/client/dfs.c
@@ -66,6 +66,12 @@  static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
 	return rc;
 }
 
+/*
+ * Track individual DFS referral servers used by new DFS mount.
+ *
+ * On success, their lifetime will be shared by final tcon (dfs_ses_list).
+ * Otherwise, they will be put by dfs_put_root_smb_sessions() in cifs_mount().
+ */
 static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
 {
 	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
@@ -80,11 +86,12 @@  static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
 		INIT_LIST_HEAD(&root_ses->list);
 
 		spin_lock(&cifs_tcp_ses_lock);
-		ses->ses_count++;
+		cifs_smb_ses_inc_refcount(ses);
 		spin_unlock(&cifs_tcp_ses_lock);
 		root_ses->ses = ses;
 		list_add_tail(&root_ses->list, &mnt_ctx->dfs_ses_list);
 	}
+	/* Select new DFS referral server so that new referrals go through it */
 	ctx->dfs_root_ses = ses;
 	return 0;
 }
@@ -242,7 +249,6 @@  static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
 int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
 {
 	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
-	struct cifs_ses *ses;
 	bool nodfs = ctx->nodfs;
 	int rc;
 
@@ -276,20 +282,8 @@  int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
 	}
 
 	*isdfs = true;
-	/*
-	 * Prevent DFS root session of being put in the first call to
-	 * cifs_mount_put_conns().  If another DFS root server was not found
-	 * while chasing the referrals (@ctx->dfs_root_ses == @ses), then we
-	 * can safely put extra refcount of @ses.
-	 */
-	ses = mnt_ctx->ses;
-	mnt_ctx->ses = NULL;
-	mnt_ctx->server = NULL;
-	rc = __dfs_mount_share(mnt_ctx);
-	if (ses == ctx->dfs_root_ses)
-		cifs_put_smb_ses(ses);
-
-	return rc;
+	add_root_smb_session(mnt_ctx);
+	return __dfs_mount_share(mnt_ctx);
 }
 
 /* Update dfs referral path of superblock */
diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c
index c6db898dab7c..7676091b3e77 100644
--- a/fs/smb/client/smb2transport.c
+++ b/fs/smb/client/smb2transport.c
@@ -160,7 +160,7 @@  smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
 			spin_unlock(&ses->ses_lock);
 			continue;
 		}
-		++ses->ses_count;
+		cifs_smb_ses_inc_refcount(ses);
 		spin_unlock(&ses->ses_lock);
 		return ses;
 	}