[PATCH OLK-5.10] cifs: fix mount deadlock by avoiding super block iteration in DFS reconnect

hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ICE4PV CVE: NA -------------------------------- A deadlock issue occurs when network interruption during mount triggers DFS reconnection logic that calls iterate_supers_type(). The detailed call process is as follows: mount.cifs cifsd path_mount do_new_mount vfs_get_tree legacy_get_tree cifs_smb3_do_mount sget alloc_super down_write_nested(&s->s_umount, ..); // hold lock cifs_read_super cifs_root_iget cifs_get_inode_info smb2_query_path_info smb2_compound_op compound_send_recv wait_for_response // wait for cifsd to wake it up cifs_demultiplex_thread cifs_read_from_socket cifs_readv_from_socket server_unresponsive cifs_reconnect cifs_get_tcp_super __cifs_get_super iterate_supers_type down_read(&sb->s_umount); // block mid->callback() cifs_wake_up_task wake_up_process // won't be executed do_new_mount_fc up_write(&sb->s_umount); // release lock here This patch fixes the problem by doing the following: - Add vfs_sb back-pointer to cifs_sb_info for direct access - Protect list traversal with existing tcon->sb_list_lock - Use atomic operations to safely manage super block references - Remove complex callback-based iteration in favor of simple loop Fixes: 93d5cb517db3 ("cifs: Add support for failover in cifs_reconnect()") Signed-off-by: Wang Zhaolong <wangzhaolong1@huawei.com> --- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/cifsfs.c | 1 + fs/cifs/connect.c | 10 ++-- fs/cifs/misc.c | 121 +++++++++++++++++++++---------------------- 4 files changed, 66 insertions(+), 67 deletions(-) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index d14002d67b6a..d180ef3f9d89 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -58,10 +58,11 @@ struct cifs_sb_info { struct rb_root tlink_tree; struct list_head tcon_sb_link; spinlock_t tlink_tree_lock; + struct super_block *vfs_sb; struct tcon_link *master_tlink; struct nls_table *local_nls; unsigned int bsize; unsigned int rsize; unsigned int wsize; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index dac20bbc2786..a86a1fb34e59 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -778,10 +778,11 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb) static int cifs_set_super(struct super_block *sb, void *data) { struct cifs_mnt_data *mnt_data = data; sb->s_fs_info = mnt_data->cifs_sb; + mnt_data->cifs_sb->vfs_sb = sb; return set_anon_super(sb, NULL); } static struct dentry * cifs_smb3_do_mount(struct file_system_type *fs_type, diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 56afed6d9ef8..23798ab5d5f1 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -405,16 +405,11 @@ cifs_reconnect(struct TCP_Server_Info *server) spin_lock(&GlobalMid_Lock); server->nr_targets = 1; #ifdef CONFIG_CIFS_DFS_UPCALL spin_unlock(&GlobalMid_Lock); sb = cifs_get_tcp_super(server); - if (IS_ERR(sb)) { - rc = PTR_ERR(sb); - cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n", - __func__, rc); - sb = NULL; - } else { + if (sb) { cifs_sb = CIFS_SB(sb); rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list); if (rc) { cifs_sb = NULL; if (rc != -EOPNOTSUPP) { @@ -422,10 +417,13 @@ cifs_reconnect(struct TCP_Server_Info *server) __func__); } } else { server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list); } + } else { + cifs_dbg(FYI, "%s: will not do DFS failover\n", __func__); + rc = -EINVAL; } cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__, server->nr_targets); spin_lock(&GlobalMid_Lock); #endif diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 12131a5d5073..2b52750acc07 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -1034,66 +1034,52 @@ int copy_path_name(char *dst, const char *src) struct super_cb_data { void *data; struct super_block *sb; }; -static void tcp_super_cb(struct super_block *sb, void *arg) +struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server) { - struct super_cb_data *sd = arg; - struct TCP_Server_Info *server = sd->data; - struct cifs_sb_info *cifs_sb; + struct super_block *sb; + struct cifs_ses *ses; struct cifs_tcon *tcon; + struct cifs_sb_info *cifs_sb; - if (sd->sb) - return; - - cifs_sb = CIFS_SB(sb); - tcon = cifs_sb_master_tcon(cifs_sb); - if (tcon->ses->server == server) - sd->sb = sb; -} + if (!server) + return NULL; -static struct super_block *__cifs_get_super(void (*f)(struct super_block *, void *), - void *data) -{ - struct super_cb_data sd = { - .data = data, - .sb = NULL, - }; - struct file_system_type **fs_type = (struct file_system_type *[]) { - &cifs_fs_type, &smb3_fs_type, NULL, - }; - - for (; *fs_type; fs_type++) { - iterate_supers_type(*fs_type, f, &sd); - if (sd.sb) { - /* - * Grab an active reference in order to prevent automounts (DFS links) - * of expiring and then freeing up our cifs superblock pointer while - * we're doing failover. - */ - cifs_sb_active(sd.sb); - return sd.sb; + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + spin_lock(&tcon->sb_list_lock); + list_for_each_entry(cifs_sb, &tcon->cifs_sb_list, tcon_sb_link) { + sb = cifs_sb->vfs_sb; + + /* Safely increment s_active only if it's not zero. + * + * When s_active == 0, the super block is being deactivated + * and should not be used. This prevents UAF scenarios + * where we might grab a reference to a super block that's + * in the middle of destruction. + */ + if (!atomic_add_unless(&sb->s_active, 1, 0)) + continue; + spin_unlock(&tcon->sb_list_lock); + spin_unlock(&cifs_tcp_ses_lock); + return sb; + } + spin_unlock(&tcon->sb_list_lock); } } - return ERR_PTR(-EINVAL); -} - -static void __cifs_put_super(struct super_block *sb) -{ - if (!IS_ERR_OR_NULL(sb)) - cifs_sb_deactive(sb); -} + spin_unlock(&cifs_tcp_ses_lock); -struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server) -{ - return __cifs_get_super(tcp_super_cb, server); + return NULL; } void cifs_put_tcp_super(struct super_block *sb) { - __cifs_put_super(sb); + if (!IS_ERR_OR_NULL(sb)) + cifs_sb_deactive(sb); } #ifdef CONFIG_CIFS_DFS_UPCALL int match_target_ip(struct TCP_Server_Info *server, const char *share, size_t share_len, @@ -1138,33 +1124,46 @@ int match_target_ip(struct TCP_Server_Info *server, kfree(tip); return rc; } -static void tcon_super_cb(struct super_block *sb, void *arg) +static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon) { - struct super_cb_data *sd = arg; - struct cifs_tcon *tcon = sd->data; struct cifs_sb_info *cifs_sb; + struct super_block *sb = ERR_PTR(-EINVAL); + + if (!tcon && list_empty(&tcon->cifs_sb_list)) + return sb; + + spin_lock(&tcon->sb_list_lock); + list_for_each_entry(cifs_sb, &tcon->cifs_sb_list, tcon_sb_link) { + sb = cifs_sb->vfs_sb; + + if (!tcon->dfs_path) + continue; + if (!cifs_sb->origin_fullpath) + continue; + if (strcasecmp(tcon->dfs_path, cifs_sb->origin_fullpath)) + continue; + /* + * Use atomic_add_unless to safely increment s_active. + * This ensures we don't add a reference to a super block + * that has s_active == 0 (being destroyed). + */ + if (!atomic_add_unless(&sb->s_active, 1, 0)) + continue; + break; + } + spin_unlock(&tcon->sb_list_lock); - if (sd->sb) - return; - - cifs_sb = CIFS_SB(sb); - if (tcon->dfs_path && cifs_sb->origin_fullpath && - !strcasecmp(tcon->dfs_path, cifs_sb->origin_fullpath)) - sd->sb = sb; -} - -static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon) -{ - return __cifs_get_super(tcon_super_cb, tcon); + return sb; } static inline void cifs_put_tcon_super(struct super_block *sb) { - __cifs_put_super(sb); + if (!IS_ERR_OR_NULL(sb)) + cifs_sb_deactive(sb); } #else static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon) { return ERR_PTR(-EOPNOTSUPP); -- 2.34.3

反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/16674 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/6OV... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://gitee.com/openeuler/kernel/pulls/16674 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/6OV...
participants (2)
-
patchwork bot
-
Wang Zhaolong