The patch below does not apply to the 5.15-stable tree. If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to stable@vger.kernel.org.
Possible dependencies:
7f28af9cf542 ("cifs: fix potential use-after-free bugs") 0e62904836ec ("smb3: remove trivial dfs compile warning") c88f7dcd6d64 ("cifs: support nested dfs links over reconnect") bbcce3680445 ("cifs: split out dfs code from cifs_reconnect()") 43b459aa5e22 ("cifs: introduce new helper for cifs_reconnect()") 7be3248f3139 ("cifs: To match file servers, make sure the server hostname matches")
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 7f28af9cf542f61758b982f8304f951ca99c8f58 Mon Sep 17 00:00:00 2001 From: Paulo Alcantara pc@cjr.nz Date: Fri, 12 Nov 2021 15:16:08 -0300 Subject: [PATCH] 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: c88f7dcd6d64 ("cifs: support nested dfs links over reconnect") Signed-off-by: Paulo Alcantara (SUSE) pc@cjr.nz Signed-off-by: Steve French stfrench@microsoft.com
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index ae21dff02f30..5c506f6ecd65 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -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,25 +4206,23 @@ 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; - } - /* Target is another dfs share */ - rc = update_server_fullpath(server, cifs_sb, target); - dfs_cache_free_tgts(tl); - - if (!rc) { - rc = -EREMOTE; - list_replace_init(&ntl.tl_list, &tl->tl_list); } else { - dfs_cache_free_tgts(&ntl); - free_dfs_info_param(ref); + /* Target is another dfs share */ + rc = update_server_fullpath(server, cifs_sb, target); + dfs_cache_free_tgts(tl); + + if (!rc) { + rc = -EREMOTE; + list_replace_init(&ntl.tl_list, &tl->tl_list); + } else + dfs_cache_free_tgts(&ntl); } 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);