[PATCH OLK-6.6] smb: client: close completed creates on compound wait errors
From: Zihan Xi <zihanx@nebusec.ai> stable inclusion from stable-v6.6.160 commit 8afc80156b1d9e2cfd02d42c255305fad95b59fc category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/20391 Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/... -------------------------------- [ Upstream commit 6c5c547f037bc18f0b8d0b5db5a648f8f630ce85 ] compound_send_recv() waits for responses in order. If a later wait is interrupted, or if a later MID fails during response synchronization, an earlier CREATE may already have opened a remote handle. The earlier mid is then released without invoking handle_cancelled_mid(), leaving the remote handle open because no FID was copied to the caller. Mark completed earlier mids as cancelled when a compound wait or MID synchronization aborts. Keep their response buffers attached while the MIDs are synchronized, and transfer them only after synchronization of the processed responses, so the release path can inspect successful CREATE responses and queue SMB2_close() after a later failure. Account for a remote open only after the close work is allocated and before it is queued, since the caller has not yet updated num_remote_opens. Mark the create+close compound used by smb2_unlink() so it is not closed again. Non-CREATE responses and compounds that already include a close keep their existing behavior. Fixes: e0bba0b85481 ("cifs: add compound_send_recv()") 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> [ replaced the upstream cancellation helper with inline loops using midQ and MID_WAIT_CANCELLED under server->mid_lock. ] Signed-off-by: Sasha Levin <sashal@kernel.org> Conflicts: fs/smb/client/transport.c [Simple context conflicts.] Signed-off-by: Zizhi Wo <wozizhi@huawei.com> --- fs/smb/client/smb2misc.c | 9 +++-- fs/smb/client/transport.c | 69 ++++++++++++++++++++++++++++++--------- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c index 6fa23cf7a529..e37fe2d3b409 100644 --- a/fs/smb/client/smb2misc.c +++ b/fs/smb/client/smb2misc.c @@ -816,11 +816,12 @@ smb2_cancelled_close_fid(struct work_struct *work) * SMB2_CREATE to the server but before we processed the reply, and second * if a close was interrupted before we sent the SMB2_CLOSE to the server. */ static int __smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid, - __u64 persistent_fid, __u64 volatile_fid) + __u64 persistent_fid, __u64 volatile_fid, + bool account_remote_open) { struct close_cancelled_open *cancelled; cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL); if (!cancelled) @@ -830,10 +831,12 @@ __smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid, cancelled->fid.volatile_fid = volatile_fid; cancelled->tcon = tcon; cancelled->cmd = cmd; cancelled->mid = mid; INIT_WORK(&cancelled->work, smb2_cancelled_close_fid); + if (account_remote_open) + atomic_inc(&tcon->num_remote_opens); WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false); return 0; } @@ -866,11 +869,11 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, netfs_trace_tcon_ref_get_cancelled_close); spin_unlock(&tcon->tc_lock); rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0, - persistent_fid, volatile_fid); + persistent_fid, volatile_fid, false); if (rc) cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_close); return rc; } @@ -894,11 +897,11 @@ smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *serve rc = __smb2_handle_cancelled_cmd(tcon, le16_to_cpu(hdr->Command), le64_to_cpu(hdr->MessageId), rsp->PersistentFileId, - rsp->VolatileFileId); + rsp->VolatileFileId, true); if (rc) cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_mid); return rc; } diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c index 9d1f2af27de5..4d7322e895b4 100644 --- a/fs/smb/client/transport.c +++ b/fs/smb/client/transport.c @@ -1079,10 +1079,11 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, struct TCP_Server_Info *server, const int flags, const int num_rqst, struct smb_rqst *rqst, int *resp_buf_type, struct kvec *resp_iov) { int i, j, optype, rc = 0; + int num_processed = 0; struct mid_q_entry *midQ[MAX_COMPOUND]; bool cancelled_mid[MAX_COMPOUND] = {false}; struct cifs_credits credits[MAX_COMPOUND] = { { .value = 0, .instance = 0 } }; @@ -1221,10 +1222,21 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, rc = wait_for_response(server, midQ[i]); if (rc != 0) break; } if (rc != 0) { + /* + * A completed CREATE earlier in the compound chain may have + * opened a remote handle even though a later wait was + * interrupted. Mark it cancelled so __release_mid() invokes + * the existing unmatched-open cleanup. + */ + spin_lock(&server->mid_lock); + for (j = 0; j < i; j++) + midQ[j]->mid_flags |= MID_WAIT_CANCELLED; + spin_unlock(&server->mid_lock); + for (; i < num_rqst; i++) { cifs_server_dbg(FYI, "Cancelling wait for mid %llu cmd: %d\n", midQ[i]->mid, le16_to_cpu(midQ[i]->command)); send_cancel(server, &rqst[i], midQ[i]); spin_lock(&midQ[i]->mid_lock); @@ -1242,59 +1254,84 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, if (rc < 0) goto out; rc = cifs_sync_mid_result(midQ[i], server); if (rc != 0) { + /* + * A previous CREATE may have completed before this + * response failed. Mark it cancelled so its remote + * handle is closed when the mid is released. + */ + spin_lock(&server->mid_lock); + for (j = 0; j < i; j++) + midQ[j]->mid_flags |= MID_WAIT_CANCELLED; + spin_unlock(&server->mid_lock); + /* Keep their response buffers for cancelled-mid cleanup. */ + num_processed = 0; /* mark this mid as cancelled to not free it below */ cancelled_mid[i] = true; goto out; } if (!midQ[i]->resp_buf || midQ[i]->mid_state != MID_RESPONSE_READY) { rc = -EIO; cifs_dbg(FYI, "Bad MID state?\n"); + spin_lock(&server->mid_lock); + for (j = 0; j < i; j++) + midQ[j]->mid_flags |= MID_WAIT_CANCELLED; + spin_unlock(&server->mid_lock); + num_processed = 0; goto out; } + rc = server->ops->check_receive(midQ[i], server, + flags & CIFS_LOG_ERROR); + num_processed = i + 1; + } + +out: + /* + * Delay moving response buffers out of their mids until response + * synchronization completes. This lets cancelled-mid cleanup inspect + * an earlier CREATE response if a later MID fails. + */ + for (i = 0; i < num_processed; i++) { buf = (char *)midQ[i]->resp_buf; resp_iov[i].iov_base = buf; resp_iov[i].iov_len = midQ[i]->resp_buf_size + HEADER_PREAMBLE_SIZE(server); if (midQ[i]->large_buf) resp_buf_type[i] = CIFS_LARGE_BUFFER; else resp_buf_type[i] = CIFS_SMALL_BUFFER; - rc = server->ops->check_receive(midQ[i], server, - flags & CIFS_LOG_ERROR); - /* mark it so buf will not be freed by delete_mid */ if ((flags & CIFS_NO_RSP_BUF) == 0) midQ[i]->resp_buf = NULL; - } /* * Compounding is never used during session establish. */ - spin_lock(&ses->ses_lock); - if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { - struct kvec iov = { - .iov_base = resp_iov[0].iov_base, - .iov_len = resp_iov[0].iov_len - }; - spin_unlock(&ses->ses_lock); - cifs_server_lock(server); - smb311_update_preauth_hash(ses, server, &iov, 1); - cifs_server_unlock(server); + if (num_processed == num_rqst) { spin_lock(&ses->ses_lock); + if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { + struct kvec iov = { + .iov_base = resp_iov[0].iov_base, + .iov_len = resp_iov[0].iov_len + }; + spin_unlock(&ses->ses_lock); + cifs_server_lock(server); + smb311_update_preauth_hash(ses, server, &iov, 1); + cifs_server_unlock(server); + spin_lock(&ses->ses_lock); + } + spin_unlock(&ses->ses_lock); } - spin_unlock(&ses->ses_lock); -out: /* * This will dequeue all mids. After this it is important that the * demultiplex_thread will not process any of these mids any futher. * This is prevented above by using a noop callback that will not * wake this thread except for the very last PDU. -- 2.52.0
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/30138 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/PFL... 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://atomgit.com/openeuler/kernel/merge_requests/30138 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/PFL...
participants (2)
-
patchwork bot -
Zizhi Wo