[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);