From ebfd35c64433821bd5619a6d07ccc2df8b5b1de3 Mon Sep 17 00:00:00 2001 From: Nilesh Javali Date: Thu, 30 Jul 2026 21:28:06 +0530 Subject: [PATCH] scsi: qla2xxx: Clamp MSI-X derived queue counts to avoid truncation ha->msix_count is u16, but ha->max_req_queues, ha->max_rsp_queues and ha->max_qpairs are u8. Deriving the queue count as "ha->max_req_queues = ha->msix_count - 1" therefore truncates: a board (or a misconfigured/malicious hot-plugged device) advertising 257 MSI-X vectors yields msix_count - 1 == 256, which truncates to 0. An MSI-X count of 1 zeroes it as well, and in target mode the subsequent "ha->max_req_queues--" then underflows 0 to 255. When the count is 0, qla2x00_alloc_queues() calls kzalloc_objs(struct req_que *, 0), which returns ZERO_SIZE_PTR. That is not NULL, so the allocation check passes and the following "ha->req_q_map[0] = req" dereferences ZERO_SIZE_PTR, corrupting memory or crashing the kernel. Add qla_calc_queue_count() to clamp the derived value into [1, QLA_MAX_QUEUES - 1] so it always fits in u8 and is never zero, and use it at all three derivation sites (qla25xx_iospace_config(), qla83xx_iospace_config() and qla24xx_enable_msix()). Also guard the target-mode decrement so it cannot reintroduce a zero (which would in turn underflow max_qpairs). Fixes: d74595278f4a ("scsi: qla2xxx: Add multiple queue pair functionality.") Cc: stable@vger.kernel.org Reported-by: Sashiko Signed-off-by: Nilesh Javali Link: https://patch.msgid.link/20260730155838.2119230-2-njavali@marvell.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/qla2xxx/qla_inline.h | 13 +++++++++++++ drivers/scsi/qla2xxx/qla_isr.c | 4 ++-- drivers/scsi/qla2xxx/qla_os.c | 6 +++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 9e33bcc87b39..90da4bad8e6e 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -138,6 +138,19 @@ qla_logio_set_vp_index(struct qla_hw_data *ha, void *pkt, u16 vp_idx) ((struct logio_entry_24xx *)pkt)->vp_index = vp_idx; } +static inline u8 +qla_calc_queue_count(u16 msix_count) +{ + /* + * Request/response queues are bounded by the MSI-X vector count less + * the mailbox vector. These counters are u8, so a board advertising + * e.g. 257 vectors would truncate msix_count - 1 (256) to 0 and hand + * kzalloc_objs() a zero count (ZERO_SIZE_PTR), faulting on the first + * ha->req_q_map[0] store. Clamp into [1, QLA_MAX_QUEUES - 1]. + */ + return clamp_t(u16, msix_count - 1, 1, QLA_MAX_QUEUES - 1); +} + static inline void qla2x00_poll(struct rsp_que *rsp) { diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 9d2d11f10a76..86e1e8428f2a 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -4705,10 +4705,10 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) ha->msix_count = ret; /* Recalculate queue values */ if (ha->mqiobase && (ql2xmqsupport || ql2xnvmeenable)) { - ha->max_req_queues = ha->msix_count - 1; + ha->max_req_queues = qla_calc_queue_count(ha->msix_count); /* ATIOQ needs 1 vector. That's 1 less QPair */ - if (QLA_TGT_MODE_ENABLED()) + if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1) ha->max_req_queues--; ha->max_rsp_queues = ha->max_req_queues; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 186c6c7a3944..5209dda45459 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -2132,7 +2132,7 @@ qla2x00_iospace_config(struct qla_hw_data *ha) ha->msix_count = msix + 1; /* Max queues are bounded by available msix vectors */ /* MB interrupt uses 1 vector */ - ha->max_req_queues = ha->msix_count - 1; + ha->max_req_queues = qla_calc_queue_count(ha->msix_count); ha->max_rsp_queues = ha->max_req_queues; /* Queue pairs is the max value minus the base queue pair */ ha->max_qpairs = ha->max_rsp_queues - 1; @@ -2224,10 +2224,10 @@ qla83xx_iospace_config(struct qla_hw_data *ha) */ if (ql2xmqsupport || ql2xnvmeenable) { /* MB interrupt uses 1 vector */ - ha->max_req_queues = ha->msix_count - 1; + ha->max_req_queues = qla_calc_queue_count(ha->msix_count); /* ATIOQ needs 1 vector. That's 1 less QPair */ - if (QLA_TGT_MODE_ENABLED()) + if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1) ha->max_req_queues--; ha->max_rsp_queues = ha->max_req_queues;