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

cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname



TCP_Server_Info::hostname may be updated once or many times during
reconnect, so protect its access outside reconnect path as well and
then prevent any potential use-after-free bugs.

Cc: stable@vger.kernel.org
Signed-off-by: default avatarPaulo Alcantara (SUSE) <pc@manguebit.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 1810769e
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -280,8 +280,10 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
		seq_printf(m, "\n%d) ConnectionId: 0x%llx ",
			c, server->conn_id);

		spin_lock(&server->srv_lock);
		if (server->hostname)
			seq_printf(m, "Hostname: %s ", server->hostname);
		spin_unlock(&server->srv_lock);
#ifdef CONFIG_CIFS_SMB_DIRECT
		if (!server->rdma)
			goto skip_rdma;
@@ -623,10 +625,13 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
				server->fastest_cmd[j],
				server->slowest_cmd[j]);
		for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++)
			if (atomic_read(&server->smb2slowcmd[j]))
			if (atomic_read(&server->smb2slowcmd[j])) {
				spin_lock(&server->srv_lock);
				seq_printf(m, "  %d slow responses from %s for command %d\n",
					atomic_read(&server->smb2slowcmd[j]),
					server->hostname, j);
				spin_unlock(&server->srv_lock);
			}
#endif /* STATS2 */
		list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
			list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+6 −6
Original line number Diff line number Diff line
@@ -81,19 +81,19 @@ do { \

#define cifs_server_dbg_func(ratefunc, type, fmt, ...)			\
do {									\
	const char *sn = "";						\
	if (server && server->hostname)					\
		sn = server->hostname;					\
	spin_lock(&server->srv_lock);					\
	if ((type) & FYI && cifsFYI & CIFS_INFO) {			\
		pr_debug_ ## ratefunc("%s: \\\\%s " fmt,		\
				      __FILE__, sn, ##__VA_ARGS__);	\
				      __FILE__, server->hostname,	\
				      ##__VA_ARGS__);			\
	} else if ((type) & VFS) {					\
		pr_err_ ## ratefunc("VFS: \\\\%s " fmt,			\
				    sn, ##__VA_ARGS__);			\
				    server->hostname, ##__VA_ARGS__);	\
	} else if ((type) & NOISY && (NOISY != 0)) {			\
		pr_debug_ ## ratefunc("\\\\%s " fmt,			\
				      sn, ##__VA_ARGS__);		\
				      server->hostname, ##__VA_ARGS__);	\
	}								\
	spin_unlock(&server->srv_lock);					\
} while (0)

#define cifs_server_dbg(type, fmt, ...)					\
+7 −3
Original line number Diff line number Diff line
@@ -403,8 +403,10 @@ static int __reconnect_target_unlocked(struct TCP_Server_Info *server, const cha
		if (server->hostname != target) {
			hostname = extract_hostname(target);
			if (!IS_ERR(hostname)) {
				spin_lock(&server->srv_lock);
				kfree(server->hostname);
				server->hostname = hostname;
				spin_unlock(&server->srv_lock);
			} else {
				cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n",
					 __func__, PTR_ERR(hostname));
@@ -561,9 +563,7 @@ cifs_echo_request(struct work_struct *work)
		goto requeue_echo;

	rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS;
	if (rc)
		cifs_dbg(FYI, "Unable to send echo request to server: %s\n",
			 server->hostname);
	cifs_server_dbg(FYI, "send echo request: rc = %d\n", rc);

	/* Check witness registrations */
	cifs_swn_check();
@@ -1404,6 +1404,8 @@ static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *
{
	struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;

	lockdep_assert_held(&server->srv_lock);

	if (ctx->nosharesock)
		return 0;

@@ -1810,7 +1812,9 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
	if (tcon == NULL)
		return -ENOMEM;

	spin_lock(&server->srv_lock);
	scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname);
	spin_unlock(&server->srv_lock);

	xid = get_xid();
	tcon->ses = ses;
+4 −3
Original line number Diff line number Diff line
@@ -159,6 +159,7 @@ cifs_chan_is_iface_active(struct cifs_ses *ses,
/* returns number of channels added */
int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
{
	struct TCP_Server_Info *server = ses->server;
	int old_chan_count, new_chan_count;
	int left;
	int rc = 0;
@@ -178,16 +179,16 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
		return 0;
	}

	if (ses->server->dialect < SMB30_PROT_ID) {
	if (server->dialect < SMB30_PROT_ID) {
		spin_unlock(&ses->chan_lock);
		cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
		return 0;
	}

	if (!(ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
	if (!(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
		ses->chan_max = 1;
		spin_unlock(&ses->chan_lock);
		cifs_dbg(VFS, "server %s does not support multichannel\n", ses->server->hostname);
		cifs_server_dbg(VFS, "no multichannel support\n");
		return 0;
	}
	spin_unlock(&ses->chan_lock);