[PATCH 2/2] scsi: lpfc: add KUnit coverage for EDC descriptor bounds
From: Michael Bommarito
Date: Thu Jul 09 2026 - 22:31:35 EST
Add KUnit coverage for lpfc_els_rcv_edc() descriptor-list bounds. The
tests live in lpfc_els.c so they can drive the real static EDC parser
without exporting test-only symbols.
The suite covers a valid congestion-signaling EDC descriptor and a
malformed EDC payload whose top-level descriptor-list length exceeds the
received payload. That malformed case documents the frame rejected by
the previous patch.
Assisted-by: Codex:gpt-5-5-xhigh
Signed-off-by: Michael Bommarito <michael.bommarito@xxxxxxxxx>
---
drivers/scsi/Kconfig | 7 ++
drivers/scsi/lpfc/lpfc_els.c | 154 +++++++++++++++++++++++++++++++++++
2 files changed, 161 insertions(+)
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 19d0884479a24..e0f01ddd22707 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1163,6 +1163,13 @@ config SCSI_LPFC_DEBUG_FS
This makes debugging information from the lpfc driver
available via the debugfs filesystem.
+config LPFC_EDC_KUNIT_TEST
+ bool "KUnit tests for lpfc EDC descriptor bounds" if !KUNIT_ALL_TESTS
+ depends on KUNIT && SCSI_LPFC
+ default KUNIT_ALL_TESTS
+ help
+ Internal validation coverage for lpfc EDC descriptor list bounds.
+
source "drivers/scsi/elx/Kconfig"
config SCSI_SIM710
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 5408b116f2d5a..0711f88da3f1a 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -12574,3 +12574,157 @@ lpfc_cmpl_els_uvem(struct lpfc_hba *phba, struct lpfc_iocbq *icmdiocb,
lpfc_els_free_iocb(phba, icmdiocb);
lpfc_nlp_put(ndlp);
}
+
+#if IS_ENABLED(CONFIG_LPFC_EDC_KUNIT_TEST)
+#include <kunit/device.h>
+#include <kunit/test.h>
+
+static void lpfc_edc_kunit_prep_stub(struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_vport *vport,
+ struct lpfc_dmabuf *bmp, u16 cmd_size,
+ u32 did, u32 elscmd, u8 tmo,
+ u8 expect_rsp)
+{
+}
+
+static void lpfc_edc_kunit_release_stub(struct lpfc_hba *phba,
+ struct lpfc_iocbq *iocbq)
+{
+}
+
+static int lpfc_edc_kunit_issue_mbox_stub(struct lpfc_hba *phba,
+ LPFC_MBOXQ_t *mboxq,
+ uint32_t flag)
+{
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return 0;
+}
+
+static void lpfc_edc_kunit_setup_hba(struct kunit *test,
+ struct lpfc_hba *phba,
+ struct lpfc_vport *vport)
+{
+ struct lpfc_iocbq *free_iocbs;
+ struct device *kdev;
+ int i;
+
+ spin_lock_init(&phba->hbalock);
+ INIT_LIST_HEAD(&phba->lpfc_iocb_list);
+ INIT_LIST_HEAD(&phba->elsbuf);
+ phba->link_state = LPFC_LINK_DOWN;
+ phba->sli_rev = LPFC_SLI_REV3;
+ phba->__lpfc_sli_prep_els_req_rsp = lpfc_edc_kunit_prep_stub;
+ phba->__lpfc_sli_release_iocbq = lpfc_edc_kunit_release_stub;
+ phba->lpfc_sli_issue_mbox = lpfc_edc_kunit_issue_mbox_stub;
+ phba->pport = vport;
+
+ free_iocbs = kunit_kzalloc(test, 4 * sizeof(*free_iocbs), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, free_iocbs);
+ for (i = 0; i < 4; i++)
+ list_add_tail(&free_iocbs[i].list, &phba->lpfc_iocb_list);
+
+ kdev = kunit_device_register(test, "lpfc_edc_test");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, kdev);
+ phba->lpfc_mbuf_pool = dma_pool_create("lpfc_edc_mbuf", kdev,
+ LPFC_BPL_SIZE, 8, 0);
+ KUNIT_ASSERT_NOT_NULL(test, phba->lpfc_mbuf_pool);
+ phba->mbox_mem_pool = mempool_create_kmalloc_pool(1,
+ sizeof(LPFC_MBOXQ_t));
+ KUNIT_ASSERT_NOT_NULL(test, phba->mbox_mem_pool);
+}
+
+static void lpfc_edc_kunit_teardown_hba(struct lpfc_hba *phba)
+{
+ if (phba->mbox_mem_pool)
+ mempool_destroy(phba->mbox_mem_pool);
+ if (phba->lpfc_mbuf_pool)
+ dma_pool_destroy(phba->lpfc_mbuf_pool);
+}
+
+static void lpfc_edc_kunit_run(struct kunit *test, bool malformed)
+{
+ struct lpfc_hba *phba;
+ struct lpfc_vport *vport;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_iocbq *cmdiocb;
+ struct lpfc_dmabuf *cmd_dmabuf;
+ struct fc_els_edc *edc;
+ struct fc_tlv_desc *tlv;
+ size_t payload_len;
+
+ phba = kunit_kzalloc(test, sizeof(*phba), GFP_KERNEL);
+ vport = kunit_kzalloc(test, sizeof(*vport), GFP_KERNEL);
+ ndlp = kunit_kzalloc(test, sizeof(*ndlp), GFP_KERNEL);
+ cmdiocb = kunit_kzalloc(test, sizeof(*cmdiocb), GFP_KERNEL);
+ cmd_dmabuf = kunit_kzalloc(test, sizeof(*cmd_dmabuf), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, phba);
+ KUNIT_ASSERT_NOT_NULL(test, vport);
+ KUNIT_ASSERT_NOT_NULL(test, ndlp);
+ KUNIT_ASSERT_NOT_NULL(test, cmdiocb);
+ KUNIT_ASSERT_NOT_NULL(test, cmd_dmabuf);
+
+ vport->phba = phba;
+ ndlp->nlp_DID = Fabric_Cntl_DID;
+ lpfc_edc_kunit_setup_hba(test, phba, vport);
+
+ if (malformed)
+ payload_len = sizeof(*edc) + FC_TLV_DESC_HDR_SZ;
+ else
+ payload_len = sizeof(*edc) +
+ sizeof(struct fc_diag_cg_sig_desc);
+
+ cmd_dmabuf->virt = kunit_kzalloc(test, payload_len, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, cmd_dmabuf->virt);
+ INIT_LIST_HEAD(&cmd_dmabuf->list);
+
+ edc = cmd_dmabuf->virt;
+ edc->edc_cmd = ELS_EDC;
+ tlv = edc->desc;
+ if (malformed) {
+ edc->desc_len = cpu_to_be32(0x100);
+ tlv->desc_tag = cpu_to_be32(0xdeadbeef);
+ tlv->desc_len = cpu_to_be32(0);
+ } else {
+ struct fc_diag_cg_sig_desc *cgn = (void *)tlv;
+
+ edc->desc_len = cpu_to_be32(sizeof(*cgn));
+ cgn->desc_tag = cpu_to_be32(ELS_DTAG_CG_SIGNAL_CAP);
+ cgn->desc_len =
+ cpu_to_be32(FC_TLV_DESC_LENGTH_FROM_SZ(*cgn));
+ cgn->xmt_signal_capability =
+ cpu_to_be32(EDC_CG_SIG_NOTSUPPORTED);
+ cgn->xmt_signal_frequency.count =
+ cpu_to_be16(EDC_CG_SIGFREQ_CNT_MIN);
+ cgn->xmt_signal_frequency.units =
+ cpu_to_be16(EDC_CG_SIGFREQ_MSEC);
+ }
+
+ cmdiocb->cmd_dmabuf = cmd_dmabuf;
+ lpfc_els_rcv_edc(vport, cmdiocb, ndlp, payload_len);
+
+ KUNIT_EXPECT_TRUE(test, true);
+ lpfc_edc_kunit_teardown_hba(phba);
+}
+
+static void lpfc_edc_control_test(struct kunit *test)
+{
+ lpfc_edc_kunit_run(test, false);
+}
+
+static void lpfc_edc_oob_test(struct kunit *test)
+{
+ lpfc_edc_kunit_run(test, true);
+}
+
+static struct kunit_case lpfc_edc_test_cases[] = {
+ KUNIT_CASE(lpfc_edc_control_test),
+ KUNIT_CASE(lpfc_edc_oob_test),
+ {}
+};
+
+static struct kunit_suite lpfc_edc_test_suite = {
+ .name = "lpfc_edc",
+ .test_cases = lpfc_edc_test_cases,
+};
+kunit_test_suite(lpfc_edc_test_suite);
+#endif /* CONFIG_LPFC_EDC_KUNIT_TEST */