From: Tyrel Datwyler <tyreld@linux.ibm.com>
To: james.bottomley@hansenpartnership.com, martin.petersen@oracle.com
Cc: linux-scsi@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
linux-kernel@vger.kernel.org, brking@linux.ibm.com,
davemarq@linux.ibm.com, Tyrel Datwyler <tyreld@linux.ibm.com>
Subject: [PATCH 14/20] scsi: ibmvfc: fix UAF and stall in NVMe LS abort callback
Date: Wed, 16 Sep 2026 16:09:15 -0700 [thread overview]
Message-ID: <20260916230921.2204729-15-tyreld@linux.ibm.com> (raw)
In-Reply-To: <20260916230921.2204729-1-tyreld@linux.ibm.com>
Two problems in ibmvfc_nvme_ls_abort():
1. Use-after-free / stale pointer dereference.
ibmvfc_init_ls_abort() reads abt_evt = ls_abort->private and
immediately dereferences abt_evt->tgt. ibmvfc_ls_req_done() calls
ibmvfc_free_event() under host_lock, which returns the event slot to
the pool. If the LS completes naturally just before ls_abort is
called, abt_evt points to a freed (and potentially reused) event,
making the dereference a UAF.
Fix by taking host_lock before reading ls_abort->private and checking
evt->free (set to 1 by ibmvfc_free_event() under host_lock) to detect
whether the original LS has already completed. If so, there is
nothing to cancel and we return early. ibmvfc_get_event() is also
moved inside the lock so the validity check and event allocation are
atomic with respect to the completion path.
2. Blocking wait on timeout workqueue (same class as the FCP abort fix).
The original code called wait_for_completion() from ls_abort, which
is invoked by the NVMe-FC transport from a context that must not
block.
Fix by replacing ibmvfc_sync_nvme_completion with a dedicated async
callback ibmvfc_nvme_ls_abort_done() that logs any non-zero MAD
status, drops the target kref, and frees the event.
ibmvfc_send_event() guarantees the callback fires on both success and
failure paths, so ibmvfc_nvme_ls_abort() returns immediately after
ibmvfc_send_event().
ibmvfc_sync_nvme_completion is now unused and is removed.
ibmvfc_init_ls_abort() is updated to take the validated abt_evt pointer
directly instead of deriving it from ls_abort->private.
Fixes: 20bec08f0208 ("scsi: ibmvfc: implement nvme-fc LS abort handling callback")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 68 +++++++++++++++--------------
1 file changed, 35 insertions(+), 33 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index 52e2621a4342..7e18b79ae4cc 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -164,21 +164,12 @@ static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port *lport,
return 0;
}
-static void ibmvfc_sync_nvme_completion(struct ibmvfc_event *evt)
+static void ibmvfc_init_ls_abort(struct ibmvfc_event *evt,
+ struct ibmvfc_event *abt_evt)
{
- /* copy the response back */
- if (evt->sync_iu)
- *evt->sync_iu = *evt->xfer_iu;
-
- complete(&evt->comp);
-}
-
-static void ibmvfc_init_ls_abort(struct ibmvfc_event *evt, struct nvmefc_ls_req *ls_abort)
-{
- struct ibmvfc_tmf *tmf;
- struct ibmvfc_event *abt_evt = ls_abort->private;
struct ibmvfc_target *tgt = abt_evt->tgt;
struct ibmvfc_host *vhost = evt->vhost;
+ struct ibmvfc_tmf *tmf;
tmf = &evt->iu.tmf;
memset(tmf, 0, sizeof(*tmf));
@@ -192,8 +183,18 @@ static void ibmvfc_init_ls_abort(struct ibmvfc_event *evt, struct nvmefc_ls_req
tmf->cancel_key = cpu_to_be32((u64)abt_evt);
tmf->my_cancel_key = cpu_to_be32((u64)evt);
tmf->assoc_id = cpu_to_be64(tgt->assoc_id);
+}
+
+static void ibmvfc_nvme_ls_abort_done(struct ibmvfc_event *evt)
+{
+ u16 status = be16_to_cpu(evt->xfer_iu->mad_common.status);
+
+ if (status)
+ ibmvfc_dbg(evt->vhost, "ls_abort: cancel MAD failed with rc=%x\n",
+ status);
- init_completion(&evt->comp);
+ kref_put(&evt->tgt->kref, ibmvfc_release_tgt);
+ ibmvfc_free_event(evt);
}
static void ibmvfc_nvme_ls_abort(struct nvme_fc_local_port *lport,
@@ -202,34 +203,35 @@ static void ibmvfc_nvme_ls_abort(struct nvme_fc_local_port *lport,
{
struct ibmvfc_host *vhost = lport->private;
struct ibmvfc_target *tgt = rport->private;
- struct ibmvfc_event *evt;
- union ibmvfc_iu rsp;
+ struct ibmvfc_event *evt, *abt_evt;
unsigned long flags;
- u16 status = IBMVFC_MAD_CRQ_ERROR;
+
+ spin_lock_irqsave(&vhost->host->host_lock, flags);
+
+ /*
+ * If the original LS has already completed naturally, abt_evt will
+ * have been freed back to the pool (evt->free set to 1 under
+ * host_lock by ibmvfc_free_event()). Nothing left to cancel.
+ */
+ abt_evt = ls_abort->private;
+ if (!abt_evt || atomic_read(&abt_evt->free)) {
+ spin_unlock_irqrestore(&vhost->host->host_lock, flags);
+ return;
+ }
evt = ibmvfc_get_event(&vhost->crq);
- if (!vhost->logged_in || !evt)
+ if (!vhost->logged_in || !evt) {
+ spin_unlock_irqrestore(&vhost->host->host_lock, flags);
return;
+ }
- spin_lock_irqsave(&vhost->host->host_lock, flags);
kref_get(&tgt->kref);
- ibmvfc_init_event(evt, ibmvfc_sync_nvme_completion, IBMVFC_MAD_FORMAT);
- ibmvfc_init_ls_abort(evt, ls_abort);
- evt->sync_iu = &rsp;
-
- if (ibmvfc_send_event(evt, vhost, default_timeout))
- goto out;
-
- spin_unlock_irqrestore(&vhost->host->host_lock, flags);
+ ibmvfc_init_event(evt, ibmvfc_nvme_ls_abort_done, IBMVFC_MAD_FORMAT);
+ ibmvfc_init_ls_abort(evt, abt_evt);
+ evt->tgt = tgt;
- wait_for_completion(&evt->comp);
- status = be16_to_cpu(rsp.mad_common.status);
- spin_lock_irqsave(&vhost->host->host_lock, flags);
- ibmvfc_free_event(evt);
-out:
+ ibmvfc_send_event(evt, vhost, default_timeout);
spin_unlock_irqrestore(&vhost->host->host_lock, flags);
- ibmvfc_dbg(vhost, "ls_abort: cancel failed with rc=%x\n", status);
- kref_put(&tgt->kref, ibmvfc_release_tgt);
}
static void ibmvfc_nvme_done(struct ibmvfc_event *evt)
--
2.55.0
next prev parent reply other threads:[~2026-09-16 23:09 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 01/20] scsi: ibmvfc: initialize evt->tgt for NVMe FCP commands Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 02/20] scsi: ibmvfc: fix trace logging " Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 03/20] scsi: ibmvfc: complete NVMe FCP requests on H_CLOSED send failure Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 04/20] scsi: ibmvfc: defer NVMe local port registration out of atomic context Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 05/20] scsi: ibmvfc: fix uninitialized _done dereference for TMF events on send failure Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 06/20] scsi: ibmvfc: fix uninitialized shwqs in ibmvfc_purge_requests() Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 07/20] scsi: ibmvfc: fix uninitialized status logged on LS abort send failure Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 08/20] scsi: ibmvfc: fix inverted suppress-ABTS capability check in NVMe TMF path Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 09/20] scsi: ibmvfc: fix infinite reset loop on NULL evt in implicit logout path Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 10/20] scsi: ibmvfc: fix u16 overflow of max_cmds in ibmvfc_set_login_info() Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 11/20] scsi: ibmvfc: fix UAF and hang in ibmvfc_cancel_all_mq() on send failure Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 12/20] scsi: ibmvfc: fix data race on tgt->nvme_remote_port Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 13/20] scsi: ibmvfc: make NVMe FCP abort callback asynchronous Tyrel Datwyler
2026-09-16 23:09 ` Tyrel Datwyler [this message]
2026-09-16 23:09 ` [PATCH 15/20] scsi: ibmvfc: unregister NVMe local port on adapter removal Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 16/20] scsi: ibmvfc: fix NVMe local port leak on fabric link bounce Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 17/20] scsi: ibmvfc: fix TOCTOU race in ibmvfc_nvme_create_queue() on adapter removal Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 18/20] scsi: ibmvfc: fix NVMe sub-queue registration failure disabling SCSI multiqueue Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 19/20] scsi: ibmvfc: fix concurrent SCSI and NVMe discover-targets race dropping targets Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 20/20] scsi: ibmvfc: fix nr_nvme_hw_queues module parameter ignored for NVMe queue sizing Tyrel Datwyler
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260916230921.2204729-15-tyreld@linux.ibm.com \
--to=tyreld@linux.ibm.com \
--cc=brking@linux.ibm.com \
--cc=davemarq@linux.ibm.com \
--cc=james.bottomley@hansenpartnership.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-scsi@vger.kernel.org \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=martin.petersen@oracle.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®