Commit bf99f6be authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French
Browse files

smb: client: fix missed ses refcounting



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: 8e355415 ("cifs: fix sharing of DFS connections")
Signed-off-by: default avatarPaulo Alcantara (SUSE) <pc@manguebit.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent f1f047bd
Loading
Loading
Loading
Loading
+10 −16
Original line number Original line Diff line number Diff line
@@ -66,6 +66,12 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
	return rc;
	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)
static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
{
{
	struct smb3_fs_context *ctx = mnt_ctx->fs_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);
		INIT_LIST_HEAD(&root_ses->list);


		spin_lock(&cifs_tcp_ses_lock);
		spin_lock(&cifs_tcp_ses_lock);
		ses->ses_count++;
		cifs_smb_ses_inc_refcount(ses);
		spin_unlock(&cifs_tcp_ses_lock);
		spin_unlock(&cifs_tcp_ses_lock);
		root_ses->ses = ses;
		root_ses->ses = ses;
		list_add_tail(&root_ses->list, &mnt_ctx->dfs_ses_list);
		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;
	ctx->dfs_root_ses = ses;
	return 0;
	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)
int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
{
{
	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
	struct cifs_ses *ses;
	bool nodfs = ctx->nodfs;
	bool nodfs = ctx->nodfs;
	int rc;
	int rc;


@@ -276,20 +282,8 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
	}
	}


	*isdfs = true;
	*isdfs = true;
	/*
	add_root_smb_session(mnt_ctx);
	 * Prevent DFS root session of being put in the first call to
	return __dfs_mount_share(mnt_ctx);
	 * 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;
}
}


/* Update dfs referral path of superblock */
/* Update dfs referral path of superblock */
+1 −1
Original line number Original line Diff line number Diff line
@@ -160,7 +160,7 @@ smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
			spin_unlock(&ses->ses_lock);
			spin_unlock(&ses->ses_lock);
			continue;
			continue;
		}
		}
		++ses->ses_count;
		cifs_smb_ses_inc_refcount(ses);
		spin_unlock(&ses->ses_lock);
		spin_unlock(&ses->ses_lock);
		return ses;
		return ses;
	}
	}