From: Zihan Xi <zihanx@nebusec.ai> maillist inclusion category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/20389 Reference: https://lore.kernel.org/all/20260930160851.2985631-2-sashal@kernel.org/ -------------------------------- [ Upstream commit d2ff5fb93ea83034025850266b5eed391f96b825 ] open_cached_dir() sends CREATE and QUERY_INFO as a compound request. If the CREATE succeeds but a later command returns an error, the function must retain the CREATE FID so common cleanup can issue SMB2_close(). It also must not treat a response error as a valid CREATE. Validate the CREATE response before using its fields, record the FIDs, and mark the handle open before handling errors from later compound commands. Move the -EREMCHG reconnect handling before response validation so a missing response does not hide the reconnect request. Count the handle when it is marked open; confirmed close responses decrement the counter, while existing close retry behavior remains best effort on transport failures. Fixes: b0f6df737a1c ("cifs: cache FILE_ALL_INFO for the shared root handle") Cc: stable@vger.kernel.org Reported-by: Vega <vega@nebusec.ai> Assisted-by: LLM Co-developed-by: Luxing Yin <root@tr0jan.top> Signed-off-by: Luxing Yin <root@tr0jan.top> Signed-off-by: Zihan Xi <zihanx@nebusec.ai> Tested-by: Frank Sorenson <sorenson@redhat.com> Signed-off-by: Paulo Alcantara <pc@manguebit.org> Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Zizhi Wo <wozizhi@huawei.com> --- fs/smb/client/cached_dir.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c index fbc746813a6f..bb1358d64949 100644 --- a/fs/smb/client/cached_dir.c +++ b/fs/smb/client/cached_dir.c @@ -6,10 +6,11 @@ */ #include <linux/namei.h> #include "cifsglob.h" #include "cifsproto.h" +#include "../common/smb2status.h" #include "cifs_debug.h" #include "smb2proto.h" #include "cached_dir.h" static struct cached_fid *init_cached_dir(const char *path); @@ -292,29 +293,41 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, } rc = compound_send_recv(xid, ses, server, flags, 2, rqst, resp_buftype, rsp_iov); - if (rc) { - if (rc == -EREMCHG) { - tcon->need_reconnect = true; - pr_warn_once("server share %s deleted\n", - tcon->tree_name); - } - goto oshr_free; + if (rc == -EREMCHG) { + tcon->need_reconnect = true; + pr_warn_once("server share %s deleted\n", + tcon->tree_name); } - cfid->is_open = true; - spin_lock(&cfids->cfid_list_lock); + if (!rsp_iov[0].iov_base || rsp_iov[0].iov_len < sizeof(*o_rsp)) { + if (!rc) + rc = -EIO; + goto oshr_free; + } o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; + if (o_rsp->hdr.Status != STATUS_SUCCESS) { + if (!rc) + rc = -EIO; + goto oshr_free; + } + oparms.fid->persistent_fid = o_rsp->PersistentFileId; oparms.fid->volatile_fid = o_rsp->VolatileFileId; #ifdef CONFIG_CIFS_DEBUG2 oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId); #endif /* CIFS_DEBUG2 */ + cfid->is_open = true; + atomic_inc(&tcon->num_remote_opens); + if (rc) + goto oshr_free; + + spin_lock(&cfids->cfid_list_lock); if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) { spin_unlock(&cfids->cfid_list_lock); rc = -EINVAL; goto oshr_free; @@ -376,11 +389,10 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, spin_unlock(&cfids->cfid_list_lock); close_cached_dir(cfid); } else { *ret_cfid = cfid; - atomic_inc(&tcon->num_remote_opens); } kfree(utf16_path); if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) -- 2.52.0