cifs: fix potential use-after-free bugs
authorPaulo Alcantara <pc@cjr.nz>
Fri, 12 Nov 2021 18:16:08 +0000 (15:16 -0300)
committerSteve French <stfrench@microsoft.com>
Fri, 12 Nov 2021 18:59:59 +0000 (12:59 -0600)
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>
fs/cifs/connect.c

index ae21dff..5c506f6 100644 (file)
@@ -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 @@ out:
 }
 
 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);