From f1f2d31c5575097e3ce6ccdbd1e92eeaf3f4a865 Mon Sep 17 00:00:00 2001 From: Wang Zhaolong Date: Tue, 22 Apr 2025 15:44:12 +0800 Subject: [PATCH 1/4] Revert "cifs: Prevent NULL pointer dereference caused by cifs_sb->rsize is 0" hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IC1OPD -------------------------------- This reverts commit 3319eda60a52c7975f57bc080d7e75e71e8f2f0b. There is already a community solution, so choose to revert this temporary solution and adopt the community solution. Fixes: 3319eda60a52 ("cifs: Prevent NULL pointer dereference caused by cifs_sb->rsize is 0") Signed-off-by: Wang Zhaolong --- fs/smb/client/connect.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 8f381bd97142a..910ff0ab979f1 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -3551,11 +3551,6 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx) if ((cifs_sb->ctx->rsize == 0) || (cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx))) cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx); - if (cifs_sb->ctx->rsize == 0) { - cifs_dbg(VFS, "Negotiated rsize is 0, get connect failed\n"); - rc = -EINVAL; - goto out; - } /* * The cookie is initialized from volume info returned above. -- Gitee From 097880fb906d62837e5ff99b25ff5a7c319b50d8 Mon Sep 17 00:00:00 2001 From: Wang Zhaolong Date: Tue, 22 Apr 2025 15:44:13 +0800 Subject: [PATCH 2/4] smb:client: smb: client: Add reverse mapping from tcon to superblocks mainline inclusion from mainline-v6.15-rc1 commit a091d9711bdee46a76fa14fad31cb261a6dad74a category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IC1OPD CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a091d9711bdee46a76fa14fad31cb261a6dad74a -------------------------------- Currently, when a SMB connection is reset and renegotiated with the server, there's no way to update all related mount points with new negotiated sizes. This is because while superblocks (cifs_sb_info) maintain references to tree connections (tcon) through tcon_link structures, there is no reverse mapping from a tcon back to all the superblocks using it. This patch adds a bidirectional relationship between tcon and cifs_sb_info structures by: 1. Adding a cifs_sb_list to tcon structure with appropriate locking 2. Adding tcon_sb_link to cifs_sb_info to join the list 3. Managing the list entries during mount and umount operations The bidirectional relationship enables future functionality to locate and update all superblocks connected to a specific tree connection, such as: - Updating negotiated parameters after reconnection - Efficiently notifying all affected mounts of capability changes This is the first part of a series to improve connection resilience by keeping all mount parameters in sync with server capabilities after reconnection. Signed-off-by: Wang Zhaolong Signed-off-by: Steve French Conflicts: fs/smb/client/cifsglob.h [Mainline patch 4f42a8b54b5c ("smb: client: fix DFS interlink failover" is not imported to this branch.] Signed-off-by: Wang Zhaolong --- fs/smb/client/cifs_fs_sb.h | 1 + fs/smb/client/cifsglob.h | 3 ++- fs/smb/client/connect.c | 15 +++++++++++++++ fs/smb/client/misc.c | 2 ++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/fs/smb/client/cifs_fs_sb.h b/fs/smb/client/cifs_fs_sb.h index 6517591922801..5e8d163cb5f80 100644 --- a/fs/smb/client/cifs_fs_sb.h +++ b/fs/smb/client/cifs_fs_sb.h @@ -49,6 +49,7 @@ struct cifs_sb_info { struct rb_root tlink_tree; + struct list_head tcon_sb_link; spinlock_t tlink_tree_lock; struct tcon_link *master_tlink; struct nls_table *local_nls; diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 6992e1ec02e41..62c37cf303266 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -1284,7 +1284,8 @@ struct cifs_tcon { #endif struct list_head pending_opens; /* list of incomplete opens */ struct cached_fids *cfids; - /* BB add field for back pointer to sb struct(s)? */ + struct list_head cifs_sb_list; + spinlock_t sb_list_lock; #ifdef CONFIG_CIFS_DFS_UPCALL struct delayed_work dfs_cache_work; #endif diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 910ff0ab979f1..18ff31e472a1b 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -3354,6 +3354,7 @@ int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb) struct smb3_fs_context *ctx = cifs_sb->ctx; INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks); + INIT_LIST_HEAD(&cifs_sb->tcon_sb_link); spin_lock_init(&cifs_sb->tlink_tree_lock); cifs_sb->tlink_tree = RB_ROOT; @@ -3586,6 +3587,10 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, tlink_rb_insert(&cifs_sb->tlink_tree, tlink); spin_unlock(&cifs_sb->tlink_tree_lock); + spin_lock(&tcon->sb_list_lock); + list_add(&cifs_sb->tcon_sb_link, &tcon->cifs_sb_list); + spin_unlock(&tcon->sb_list_lock); + queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, TLINK_IDLE_EXPIRE); return 0; @@ -3928,9 +3933,19 @@ cifs_umount(struct cifs_sb_info *cifs_sb) struct rb_root *root = &cifs_sb->tlink_tree; struct rb_node *node; struct tcon_link *tlink; + struct cifs_tcon *tcon = NULL; cancel_delayed_work_sync(&cifs_sb->prune_tlinks); + if (cifs_sb->master_tlink) { + tcon = cifs_sb->master_tlink->tl_tcon; + if (tcon) { + spin_lock(&tcon->sb_list_lock); + list_del_init(&cifs_sb->tcon_sb_link); + spin_unlock(&tcon->sb_list_lock); + } + } + spin_lock(&cifs_sb->tlink_tree_lock); while ((node = rb_first(root))) { tlink = rb_entry(node, struct tcon_link, tl_rbnode); diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index 65d4b72b4d51a..b0381c7dd15b2 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -136,8 +136,10 @@ tcon_info_alloc(bool dir_leases_enabled, enum smb3_tcon_ref_trace trace) spin_lock_init(&ret_buf->tc_lock); INIT_LIST_HEAD(&ret_buf->openFileList); INIT_LIST_HEAD(&ret_buf->tcon_list); + INIT_LIST_HEAD(&ret_buf->cifs_sb_list); spin_lock_init(&ret_buf->open_file_lock); spin_lock_init(&ret_buf->stat_lock); + spin_lock_init(&ret_buf->sb_list_lock); atomic_set(&ret_buf->num_local_opens, 0); atomic_set(&ret_buf->num_remote_opens, 0); ret_buf->stats_from_time = ktime_get_real_seconds(); -- Gitee From 1bb73c674fedca4121541493d9198049539d344b Mon Sep 17 00:00:00 2001 From: Wang Zhaolong Date: Tue, 22 Apr 2025 15:44:14 +0800 Subject: [PATCH 3/4] smb: client: Store original IO parameters and prevent zero IO sizes mainline inclusion from mainline-v6.15-rc1 commit 287906b20035a04a234d1a3c64f760a5678387be category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IC1OPD CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=287906b20035a04a234d1a3c64f760a5678387be -------------------------------- During mount option processing and negotiation with the server, the original user-specified rsize/wsize values were being modified directly. This makes it impossible to recover these values after a connection reset, leading to potential degraded performance after reconnection. The other problem is that When negotiating read and write sizes, there are cases where the negotiated values might calculate to zero, especially during reconnection when server->max_read or server->max_write might be reset. In general, these values come from the negotiation response. According to MS-SMB2 specification, these values should be at least 65536 bytes. This patch improves IO parameter handling: 1. Adds vol_rsize and vol_wsize fields to store the original user-specified values separately from the negotiated values 2. Uses got_rsize/got_wsize flags to determine if values were user-specified rather than checking for non-zero values, which is more reliable 3. Adds a prevent_zero_iosize() helper function to ensure IO sizes are never negotiated down to zero, which could happen in edge cases like when server->max_read/write is zero The changes make the CIFS client more resilient to unusual server responses and reconnection scenarios, preventing potential failures when IO sizes are calculated to be zero. Signed-off-by: Wang Zhaolong Signed-off-by: Steve French Signed-off-by: Wang Zhaolong --- fs/smb/client/fs_context.c | 2 ++ fs/smb/client/fs_context.h | 3 +++ fs/smb/client/smb1ops.c | 6 +++--- fs/smb/client/smb2ops.c | 27 +++++++++++++++++++-------- fs/smb/common/smb2pdu.h | 3 +++ 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c index b3fb1db26eb34..f5750fc6d7218 100644 --- a/fs/smb/client/fs_context.c +++ b/fs/smb/client/fs_context.c @@ -1245,6 +1245,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, case Opt_rsize: ctx->rsize = result.uint_32; ctx->got_rsize = true; + ctx->vol_rsize = ctx->rsize; break; case Opt_wsize: ctx->wsize = result.uint_32; @@ -1260,6 +1261,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, ctx->wsize, PAGE_SIZE); } } + ctx->vol_wsize = ctx->wsize; break; case Opt_acregmax: if (result.uint_32 > CIFS_MAX_ACTIMEO / HZ) { diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h index bbd2063ab838d..d0a2043ea4468 100644 --- a/fs/smb/client/fs_context.h +++ b/fs/smb/client/fs_context.h @@ -253,6 +253,9 @@ struct smb3_fs_context { bool use_client_guid:1; /* reuse existing guid for multichannel */ u8 client_guid[SMB2_CLIENT_GUID_SIZE]; + /* User-specified original r/wsize value */ + unsigned int vol_rsize; + unsigned int vol_wsize; unsigned int bsize; unsigned int rasize; unsigned int rsize; diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c index bc1bac36c1b29..740f9e4b43743 100644 --- a/fs/smb/client/smb1ops.c +++ b/fs/smb/client/smb1ops.c @@ -444,8 +444,8 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) unsigned int wsize; /* start with specified wsize, or default */ - if (ctx->wsize) - wsize = ctx->wsize; + if (ctx->got_wsize) + wsize = ctx->vol_wsize; else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) wsize = CIFS_DEFAULT_IOSIZE; else @@ -497,7 +497,7 @@ cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) else defsize = server->maxBuf - sizeof(READ_RSP); - rsize = ctx->rsize ? ctx->rsize : defsize; + rsize = ctx->got_rsize ? ctx->vol_rsize : defsize; /* * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index b809a616728f2..91dbf48de9d03 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -434,6 +434,17 @@ smb2_negotiate(const unsigned int xid, return rc; } +static inline unsigned int +prevent_zero_iosize(unsigned int size, const char *type) +{ + if (size == 0) { + cifs_dbg(VFS, "SMB: Zero %ssize calculated, using minimum value %u\n", + type, CIFS_MIN_DEFAULT_IOSIZE); + return CIFS_MIN_DEFAULT_IOSIZE; + } + return size; +} + static unsigned int smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) { @@ -441,12 +452,12 @@ smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) unsigned int wsize; /* start with specified wsize, or default */ - wsize = ctx->wsize ? ctx->wsize : CIFS_DEFAULT_IOSIZE; + wsize = ctx->got_wsize ? ctx->vol_wsize : CIFS_DEFAULT_IOSIZE; wsize = min_t(unsigned int, wsize, server->max_write); if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); - return wsize; + return prevent_zero_iosize(wsize, "w"); } static unsigned int @@ -456,7 +467,7 @@ smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) unsigned int wsize; /* start with specified wsize, or default */ - wsize = ctx->wsize ? ctx->wsize : SMB3_DEFAULT_IOSIZE; + wsize = ctx->got_wsize ? ctx->vol_wsize : SMB3_DEFAULT_IOSIZE; wsize = min_t(unsigned int, wsize, server->max_write); #ifdef CONFIG_CIFS_SMB_DIRECT if (server->rdma) { @@ -478,7 +489,7 @@ smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); - return wsize; + return prevent_zero_iosize(wsize, "w"); } static unsigned int @@ -488,13 +499,13 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) unsigned int rsize; /* start with specified rsize, or default */ - rsize = ctx->rsize ? ctx->rsize : CIFS_DEFAULT_IOSIZE; + rsize = ctx->got_rsize ? ctx->vol_rsize : CIFS_DEFAULT_IOSIZE; rsize = min_t(unsigned int, rsize, server->max_read); if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); - return rsize; + return prevent_zero_iosize(rsize, "r"); } static unsigned int @@ -504,7 +515,7 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) unsigned int rsize; /* start with specified rsize, or default */ - rsize = ctx->rsize ? ctx->rsize : SMB3_DEFAULT_IOSIZE; + rsize = ctx->got_rsize ? ctx->vol_rsize : SMB3_DEFAULT_IOSIZE; rsize = min_t(unsigned int, rsize, server->max_read); #ifdef CONFIG_CIFS_SMB_DIRECT if (server->rdma) { @@ -527,7 +538,7 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); - return rsize; + return prevent_zero_iosize(rsize, "r"); } /* diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h index c3ee42188d252..1af827ae757e0 100644 --- a/fs/smb/common/smb2pdu.h +++ b/fs/smb/common/smb2pdu.h @@ -95,6 +95,9 @@ */ #define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) +/* According to MS-SMB2 specification The minimum recommended value is 65536.*/ +#define CIFS_MIN_DEFAULT_IOSIZE (65536) + /* * SMB2 Header Definition * -- Gitee From 9491e9fa78936b50c0e97fb08c4d44bea5a36bf1 Mon Sep 17 00:00:00 2001 From: Wang Zhaolong Date: Tue, 22 Apr 2025 15:44:15 +0800 Subject: [PATCH 4/4] smb: client: Update IO sizes after reconnection mainline inclusion from mainline-v6.15-rc1 commit 764da2fff399756d09b02db7fa7bd05e57928cc0 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IC1OPD CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=764da2fff399756d09b02db7fa7bd05e57928cc0 -------------------------------- When a SMB connection is reset and reconnected, the negotiated IO parameters (rsize/wsize) can become out of sync with the server's current capabilities. This can lead to suboptimal performance or even IO failures if the server's limits have changed. This patch implements automatic IO size renegotiation: 1. Adds cifs_renegotiate_iosize() function to update all superblocks associated with a tree connection 2. Updates each mount's rsize/wsize based on current server capabilities 3. Calls this function after successful tree connection reconnection With this change, all mount points will automatically maintain optimal and reliable IO parameters after network disruptions, using the bidirectional mapping added in previous patches. This completes the series improving connection resilience by keeping mount parameters synchronized with server capabilities. Signed-off-by: Wang Zhaolong Signed-off-by: Steve French Conflicts: fs/smb/client/smb2pdu.c [Mainline patch f046d71e84e1 ("smb: client: insert compression check/call on write requests") is not imported to this branch.] Signed-off-by: Wang Zhaolong --- fs/smb/client/smb2pdu.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 24b1738a35a15..0e84197916ac9 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -40,6 +40,7 @@ #include "dfs_cache.h" #endif #include "cached_dir.h" +#include "fs_context.h" /* * The following table defines the expected "StructureSize" of SMB2 requests @@ -4105,6 +4106,24 @@ smb2_echo_callback(struct mid_q_entry *mid) add_credits(server, &credits, CIFS_ECHO_OP); } +static void cifs_renegotiate_iosize(struct TCP_Server_Info *server, + struct cifs_tcon *tcon) +{ + struct cifs_sb_info *cifs_sb; + + if (server == NULL || tcon == NULL) + return; + + spin_lock(&tcon->sb_list_lock); + list_for_each_entry(cifs_sb, &tcon->cifs_sb_list, tcon_sb_link) { + cifs_sb->ctx->rsize = + server->ops->negotiate_rsize(tcon, cifs_sb->ctx); + cifs_sb->ctx->wsize = + server->ops->negotiate_wsize(tcon, cifs_sb->ctx); + } + spin_unlock(&tcon->sb_list_lock); +} + void smb2_reconnect_server(struct work_struct *work) { struct TCP_Server_Info *server = container_of(work, @@ -4190,9 +4209,10 @@ void smb2_reconnect_server(struct work_struct *work) list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) { rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server, true); - if (!rc) + if (!rc) { + cifs_renegotiate_iosize(server, tcon); cifs_reopen_persistent_handles(tcon); - else + } else resched = true; list_del_init(&tcon->rlist); if (tcon->ipc) -- Gitee