[PATCH] scsi: qla4xxx: Fix NULL pointer dereference in qla4xxx_abort_active_cmds()

Yang Zi <[email protected]>
Newsgroups org.kernel.vger.linux-scsi,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
qla4xxx_probe_adapter()'s failure path first calls scsi_remove_host(),
which drops the SCSI host's tag set reference and frees tag_set.tags via
scsi_mq_free_tags() -> blk_mq_free_tag_set().  It then falls through to
qla4xxx_free_adapter(), which calls qla4xxx_abort_active_cmds().  That
function walks ha->host->can_queue entries and, for each one, calls
qla4xxx_del_from_active_array() -> scsi_host_find_tag(), which
dereferences shost->tag_set.tags -- now NULL -- resulting in a NULL
pointer dereference.

KASAN report:

    BUG: KASAN: null-ptr-deref in scsi_host_find_tag include/scsi/scsi_tcq.h:33 [inline] [qla4xxx]
    BUG: KASAN: null-ptr-deref in qla4xxx_del_from_active_array drivers/scsi/qla4xxx/ql4_os.c:9095 [inline] [qla4xxx]
    BUG: KASAN: null-ptr-deref in qla4xxx_abort_active_cmds+0x10b/0x610 drivers/scsi/qla4xxx/ql4_os.c:4816 [qla4xxx]

Guard qla4xxx_abort_active_cmds() so that it returns early when the SCSI
host has not been set up yet or its tag set has already been released:
in both cases there are no outstanding commands left to abort, and
scsi_host_find_tag() must not dereference a NULL tag_set.tags pointer.
This covers both the failed-probe cleanup path and the normal
qla4xxx_remove_adapter() path.

This patch addresses two reports with the same root cause (tracking IDs
115 and 139).

Signed-off-by: Yang Zi <[email protected]>
---
 drivers/scsi/qla4xxx/ql4_os.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index d598ab4126f8..2277f48fede8 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -4811,6 +4811,15 @@ static void qla4xxx_abort_active_cmds(struct scsi_qla_host *ha, int res)
     int i;
     unsigned long flags;
 
+    /*
+     * The SCSI host may not be fully set up yet, or its tag set may
+     * already have been released by scsi_remove_host().  In either case
+     * there are no outstanding commands to abort, and scsi_host_find_tag()
+     * would dereference a NULL tag_set.tags pointer.
+     */
+    if (!ha->host || !ha->host->tag_set.tags)
+        return;
+
     spin_lock_irqsave(&ha->hardware_lock, flags);
     for (i = 0; i < ha->host->can_queue; i++) {
         srb = qla4xxx_del_from_active_array(ha, i);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.