| From 1204e35bedf4e5015cda559ed8c84789a6dae24e Mon Sep 17 00:00:00 2001 |
| From: Lukas Wunner <lukas@wunner.de> |
| Date: Thu, 19 Jul 2018 17:27:34 -0500 |
| Subject: PCI: pciehp: Fix unprotected list iteration in IRQ handler |
| |
| From: Lukas Wunner <lukas@wunner.de> |
| |
| commit 1204e35bedf4e5015cda559ed8c84789a6dae24e upstream. |
| |
| Commit b440bde74f04 ("PCI: Add pci_ignore_hotplug() to ignore hotplug |
| events for a device") iterates over the devices on a hotplug port's |
| subordinate bus in pciehp's IRQ handler without acquiring pci_bus_sem. |
| It is thus possible for a user to cause a crash by concurrently |
| manipulating the device list, e.g. by disabling slot power via sysfs |
| on a different CPU or by initiating a remove/rescan via sysfs. |
| |
| This can't be fixed by acquiring pci_bus_sem because it may sleep. |
| The simplest fix is to avoid the list iteration altogether and just |
| check the ignore_hotplug flag on the port itself. This works because |
| pci_ignore_hotplug() sets the flag both on the device as well as on its |
| parent bridge. |
| |
| We do lose the ability to print the name of the device blocking hotplug |
| in the debug message, but that's probably bearable. |
| |
| Fixes: b440bde74f04 ("PCI: Add pci_ignore_hotplug() to ignore hotplug events for a device") |
| Signed-off-by: Lukas Wunner <lukas@wunner.de> |
| Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/pci/hotplug/pciehp_hpc.c | 13 +++---------- |
| 1 file changed, 3 insertions(+), 10 deletions(-) |
| |
| --- a/drivers/pci/hotplug/pciehp_hpc.c |
| +++ b/drivers/pci/hotplug/pciehp_hpc.c |
| @@ -562,8 +562,6 @@ static irqreturn_t pciehp_isr(int irq, v |
| { |
| struct controller *ctrl = (struct controller *)dev_id; |
| struct pci_dev *pdev = ctrl_dev(ctrl); |
| - struct pci_bus *subordinate = pdev->subordinate; |
| - struct pci_dev *dev; |
| struct slot *slot = ctrl->slot; |
| u16 status, events; |
| u8 present; |
| @@ -611,14 +609,9 @@ static irqreturn_t pciehp_isr(int irq, v |
| wake_up(&ctrl->queue); |
| } |
| |
| - if (subordinate) { |
| - list_for_each_entry(dev, &subordinate->devices, bus_list) { |
| - if (dev->ignore_hotplug) { |
| - ctrl_dbg(ctrl, "ignoring hotplug event %#06x (%s requested no hotplug)\n", |
| - events, pci_name(dev)); |
| - return IRQ_HANDLED; |
| - } |
| - } |
| + if (pdev->ignore_hotplug) { |
| + ctrl_dbg(ctrl, "ignoring hotplug event %#06x\n", events); |
| + return IRQ_HANDLED; |
| } |
| |
| /* Check Attention Button Pressed */ |