[PATCH] scsi: stex: Fix NULL pointer dereference in stex_hard_reset()

Yang Zi <[email protected]>
Newsgroups org.kernel.vger.linux-scsi,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
stex_hard_reset() reads the PCI bridge device via hba->pdev->bus->self
and passes it to pci_read_config_byte()/pci_write_config_byte() without
checking whether it is NULL.  When the controller is attached to a root
bus (or otherwise has no bridge device), bus->self is NULL, and
pci_read_config_byte(NULL, ...) dereferences it in
pci_dev_is_disconnected(), triggering a NULL pointer dereference.

KASAN report:

    BUG: KASAN: null-ptr-deref in pci_dev_is_disconnected include/linux/pci.h:2676 [inline]
    BUG: KASAN: null-ptr-deref in pci_read_config_byte+0x23/0x90 drivers/pci/access.c:562
    Read of size 4 at addr 00000000000000c4 by task scsi_eh_6/2005

    RIP: 0010:pci_dev_is_disconnected include/linux/pci.h:2676 [inline]
    RIP: 0010:pci_read_config_byte+0x23/0x90 drivers/pci/access.c:562
    ...
    stex_hard_reset  drivers/scsi/stex.c:1322 [inline] [stex]
    stex_do_reset+0x3da6/0x6490  drivers/scsi/stex.c:1424 [stex]

Skip the secondary bus reset when there is no bridge device; the rest of
the reset sequence (config space save/restore and the PCI_COMMAND poll)
does not depend on bus->self and still runs.

Signed-off-by: Yang Zi <[email protected]>
---
 drivers/scsi/stex.c | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c
index 6aeeb338633d..f1dff2cc6086 100644
--- a/drivers/scsi/stex.c
+++ b/drivers/scsi/stex.c
@@ -1319,17 +1319,20 @@ static void stex_hard_reset(struct st_hba *hba)
     /* Reset secondary bus. Our controller(MU/ATU) is the only device on
        secondary bus. Consult Intel 80331/3 developer's manual for detail */
     bus = hba->pdev->bus;
-    pci_read_config_byte(bus->self, PCI_BRIDGE_CONTROL, &pci_bctl);
-    pci_bctl |= PCI_BRIDGE_CTL_BUS_RESET;
-    pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl);
+    if (bus->self) {
+        pci_read_config_byte(bus->self, PCI_BRIDGE_CONTROL, &pci_bctl);
+        pci_bctl |= PCI_BRIDGE_CTL_BUS_RESET;
+        pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl);
 
-    /*
-     * 1 ms may be enough for 8-port controllers. But 16-port controllers
-     * require more time to finish bus reset. Use 100 ms here for safety
-     */
-    msleep(100);
-    pci_bctl &= ~PCI_BRIDGE_CTL_BUS_RESET;
-    pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl);
+        /*
+         * 1 ms may be enough for 8-port controllers. But 16-port
+         * controllers require more time to finish bus reset. Use 100 ms
+         * here for safety
+         */
+        msleep(100);
+        pci_bctl &= ~PCI_BRIDGE_CTL_BUS_RESET;
+        pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl);
+    }
 
     for (i = 0; i < MU_HARD_RESET_WAIT; i++) {
         pci_read_config_word(hba->pdev, PCI_COMMAND, &pci_cmd);
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.