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

cifs: fix potential use-after-free bugs



Ensure that share and prefix variables are set to NULL after kfree()
when looping through DFS targets in __tree_connect_dfs_target().

Also, get rid of @ref in __tree_connect_dfs_target() and just pass a
boolean to indicate whether we're handling link targets or not.

Fixes: c88f7dcd ("cifs: support nested dfs links over reconnect")
Signed-off-by: default avatarPaulo Alcantara (SUSE) <pc@cjr.nz>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 869da64d
Loading
Loading
Loading
Loading
+19 −22
Original line number Diff line number Diff line
@@ -4141,14 +4141,13 @@ static int target_share_matches_server(struct TCP_Server_Info *server, const cha
}

static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
			      struct cifs_sb_info *cifs_sb, char *tree,
			      struct dfs_cache_tgt_list *tl, struct dfs_info3_param *ref)
				     struct cifs_sb_info *cifs_sb, char *tree, bool islink,
				     struct dfs_cache_tgt_list *tl)
{
	int rc;
	struct TCP_Server_Info *server = tcon->ses->server;
	const struct smb_version_operations *ops = server->ops;
	struct cifs_tcon *ipc = tcon->ses->tcon_ipc;
	bool islink;
	char *share = NULL, *prefix = NULL;
	const char *tcp_host;
	size_t tcp_host_len;
@@ -4157,9 +4156,6 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t

	extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);

	islink = ref->server_type == DFS_TYPE_LINK;
	free_dfs_info_param(ref);

	tit = dfs_cache_get_tgt_iterator(tl);
	if (!tit) {
		rc = -ENOENT;
@@ -4173,6 +4169,7 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t

		kfree(share);
		kfree(prefix);
		share = prefix = NULL;

		/* Check if share matches with tcp ses */
		rc = dfs_cache_get_tgt_share(server->current_fullpath + 1, tit, &share, &prefix);
@@ -4209,15 +4206,14 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
		 * newly resolved target.
		 */
		if (dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target,
				   ref, &ntl)) {
				   NULL, &ntl)) {
			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
			if (rc)
				continue;
			rc = dfs_cache_noreq_update_tgthint(server->current_fullpath + 1, tit);
			if (!rc)
				rc = cifs_update_super_prepath(cifs_sb, prefix);
			break;
		}
		} else {
			/* Target is another dfs share */
			rc = update_server_fullpath(server, cifs_sb, target);
			dfs_cache_free_tgts(tl);
@@ -4225,9 +4221,8 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
			if (!rc) {
				rc = -EREMOTE;
				list_replace_init(&ntl.tl_list, &tl->tl_list);
		} else {
			} else
				dfs_cache_free_tgts(&ntl);
			free_dfs_info_param(ref);
		}
		break;
	}
@@ -4240,15 +4235,15 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
}

static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
			    struct cifs_sb_info *cifs_sb, char *tree,
			    struct dfs_cache_tgt_list *tl, struct dfs_info3_param *ref)
				   struct cifs_sb_info *cifs_sb, char *tree, bool islink,
				   struct dfs_cache_tgt_list *tl)
{
	int rc;
	int num_links = 0;
	struct TCP_Server_Info *server = tcon->ses->server;

	do {
		rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, tl, ref);
		rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl);
		if (!rc || rc != -EREMOTE)
			break;
	} while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS);
@@ -4302,7 +4297,9 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
		goto out;
	}

	rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, &tl, &ref);
	rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, ref.server_type == DFS_TYPE_LINK,
				     &tl);
	free_dfs_info_param(&ref);

out:
	kfree(tree);