Skip to content

Commit 2a671f7

Browse files
niklas88Vasily Gorbik
authored andcommitted
s390/pci: fix use after free of zpci_dev
The struct pci_dev uses reference counting but zPCI assumed erroneously that the last reference would always be the local reference after calling pci_stop_and_remove_bus_device(). This is usually the case but not how reference counting works and thus inherently fragile. In fact one case where this causes a NULL pointer dereference when on an SRIOV device the function 0 was hot unplugged before another function of the same multi-function device. In this case the second function's pdev->sriov->dev reference keeps the struct pci_dev of function 0 alive even after the unplug. This bug was previously hidden by the fact that we were leaking the struct pci_dev which in turn means that it always outlived the struct zpci_dev. This was fixed in commit 0b13525 ("s390/pci: fix leak of PCI device structure") exposing the broken behavior. Fix this by accounting for the long living reference a struct pci_dev has to its underlying struct zpci_dev via the zbus->function[] array and only release that in pcibios_release_device() ensuring that the struct pci_dev is not left with a dangling reference. This is a minimal fix in the future it would probably better to use fine grained reference counting for struct zpci_dev. Fixes: 05bc1be ("s390/pci: create zPCI bus") Cc: [email protected] Reviewed-by: Matthew Rosato <[email protected]> Signed-off-by: Niklas Schnelle <[email protected]> Signed-off-by: Vasily Gorbik <[email protected]>
1 parent 7c60610 commit 2a671f7

File tree

2 files changed

+11
-0
lines changed

2 files changed

+11
-0
lines changed

arch/s390/pci/pci.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,9 +560,12 @@ static void zpci_cleanup_bus_resources(struct zpci_dev *zdev)
560560

561561
int pcibios_add_device(struct pci_dev *pdev)
562562
{
563+
struct zpci_dev *zdev = to_zpci(pdev);
563564
struct resource *res;
564565
int i;
565566

567+
/* The pdev has a reference to the zdev via its bus */
568+
zpci_zdev_get(zdev);
566569
if (pdev->is_physfn)
567570
pdev->no_vf_scan = 1;
568571

@@ -582,7 +585,10 @@ int pcibios_add_device(struct pci_dev *pdev)
582585

583586
void pcibios_release_device(struct pci_dev *pdev)
584587
{
588+
struct zpci_dev *zdev = to_zpci(pdev);
589+
585590
zpci_unmap_resources(pdev);
591+
zpci_zdev_put(zdev);
586592
}
587593

588594
int pcibios_enable_device(struct pci_dev *pdev, int mask)

arch/s390/pci/pci_bus.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ static inline void zpci_zdev_put(struct zpci_dev *zdev)
2222
kref_put(&zdev->kref, zpci_release_device);
2323
}
2424

25+
static inline void zpci_zdev_get(struct zpci_dev *zdev)
26+
{
27+
kref_get(&zdev->kref);
28+
}
29+
2530
int zpci_alloc_domain(int domain);
2631
void zpci_free_domain(int domain);
2732
int zpci_setup_bus_resources(struct zpci_dev *zdev,

0 commit comments

Comments
 (0)