Skip to content

Commit ab90950

Browse files
niklas88Vasily Gorbik
authored andcommitted
PCI: s390: Fix use-after-free of PCI resources with per-function hotplug
On s390 PCI functions may be hotplugged individually even when they belong to a multi-function device. In particular on an SR-IOV device VFs may be removed and later re-added. In commit a50297c ("s390/pci: separate zbus creation from scanning") it was missed however that struct pci_bus and struct zpci_bus's resource list retained a reference to the PCI functions MMIO resources even though those resources are released and freed on hot-unplug. These stale resources may subsequently be claimed when the PCI function re-appears resulting in use-after-free. One idea of fixing this use-after-free in s390 specific code that was investigated was to simply keep resources around from the moment a PCI function first appeared until the whole virtual PCI bus created for a multi-function device disappears. The problem with this however is that due to the requirement of artificial MMIO addreesses (address cookies) extra logic is then needed to keep the address cookies compatible on re-plug. At the same time the MMIO resources semantically belong to the PCI function so tying their lifecycle to the function seems more logical. Instead a simpler approach is to remove the resources of an individually hot-unplugged PCI function from the PCI bus's resource list while keeping the resources of other PCI functions on the PCI bus untouched. This is done by introducing pci_bus_remove_resource() to remove an individual resource. Similarly the resource also needs to be removed from the struct zpci_bus's resource list. It turns out however, that there is really no need to add the MMIO resources to the struct zpci_bus's resource list at all and instead we can simply use the zpci_bar_struct's resource pointer directly. Fixes: a50297c ("s390/pci: separate zbus creation from scanning") Signed-off-by: Niklas Schnelle <[email protected]> Reviewed-by: Matthew Rosato <[email protected]> Acked-by: Bjorn Helgaas <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Vasily Gorbik <[email protected]>
1 parent a52e5cd commit ab90950

File tree

5 files changed

+38
-15
lines changed

5 files changed

+38
-15
lines changed

arch/s390/pci/pci.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -544,8 +544,7 @@ static struct resource *__alloc_res(struct zpci_dev *zdev, unsigned long start,
544544
return r;
545545
}
546546

547-
int zpci_setup_bus_resources(struct zpci_dev *zdev,
548-
struct list_head *resources)
547+
int zpci_setup_bus_resources(struct zpci_dev *zdev)
549548
{
550549
unsigned long addr, size, flags;
551550
struct resource *res;
@@ -581,7 +580,6 @@ int zpci_setup_bus_resources(struct zpci_dev *zdev,
581580
return -ENOMEM;
582581
}
583582
zdev->bars[i].res = res;
584-
pci_add_resource(resources, res);
585583
}
586584
zdev->has_resources = 1;
587585

@@ -590,17 +588,23 @@ int zpci_setup_bus_resources(struct zpci_dev *zdev,
590588

591589
static void zpci_cleanup_bus_resources(struct zpci_dev *zdev)
592590
{
591+
struct resource *res;
593592
int i;
594593

594+
pci_lock_rescan_remove();
595595
for (i = 0; i < PCI_STD_NUM_BARS; i++) {
596-
if (!zdev->bars[i].size || !zdev->bars[i].res)
596+
res = zdev->bars[i].res;
597+
if (!res)
597598
continue;
598599

600+
release_resource(res);
601+
pci_bus_remove_resource(zdev->zbus->bus, res);
599602
zpci_free_iomap(zdev, zdev->bars[i].map_idx);
600-
release_resource(zdev->bars[i].res);
601-
kfree(zdev->bars[i].res);
603+
zdev->bars[i].res = NULL;
604+
kfree(res);
602605
}
603606
zdev->has_resources = 0;
607+
pci_unlock_rescan_remove();
604608
}
605609

606610
int pcibios_device_add(struct pci_dev *pdev)

arch/s390/pci/pci_bus.c

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,7 @@ static int zpci_nb_devices;
4141
*/
4242
static int zpci_bus_prepare_device(struct zpci_dev *zdev)
4343
{
44-
struct resource_entry *window, *n;
45-
struct resource *res;
46-
int rc;
44+
int rc, i;
4745

4846
if (!zdev_enabled(zdev)) {
4947
rc = zpci_enable_device(zdev);
@@ -57,10 +55,10 @@ static int zpci_bus_prepare_device(struct zpci_dev *zdev)
5755
}
5856

5957
if (!zdev->has_resources) {
60-
zpci_setup_bus_resources(zdev, &zdev->zbus->resources);
61-
resource_list_for_each_entry_safe(window, n, &zdev->zbus->resources) {
62-
res = window->res;
63-
pci_bus_add_resource(zdev->zbus->bus, res, 0);
58+
zpci_setup_bus_resources(zdev);
59+
for (i = 0; i < PCI_STD_NUM_BARS; i++) {
60+
if (zdev->bars[i].res)
61+
pci_bus_add_resource(zdev->zbus->bus, zdev->bars[i].res, 0);
6462
}
6563
}
6664

arch/s390/pci/pci_bus.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ static inline void zpci_zdev_get(struct zpci_dev *zdev)
3030

3131
int zpci_alloc_domain(int domain);
3232
void zpci_free_domain(int domain);
33-
int zpci_setup_bus_resources(struct zpci_dev *zdev,
34-
struct list_head *resources);
33+
int zpci_setup_bus_resources(struct zpci_dev *zdev);
3534

3635
static inline struct zpci_dev *zdev_from_bus(struct pci_bus *bus,
3736
unsigned int devfn)

drivers/pci/bus.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,27 @@ struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n)
7676
}
7777
EXPORT_SYMBOL_GPL(pci_bus_resource_n);
7878

79+
void pci_bus_remove_resource(struct pci_bus *bus, struct resource *res)
80+
{
81+
struct pci_bus_resource *bus_res, *tmp;
82+
int i;
83+
84+
for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) {
85+
if (bus->resource[i] == res) {
86+
bus->resource[i] = NULL;
87+
return;
88+
}
89+
}
90+
91+
list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) {
92+
if (bus_res->res == res) {
93+
list_del(&bus_res->list);
94+
kfree(bus_res);
95+
return;
96+
}
97+
}
98+
}
99+
79100
void pci_bus_remove_resources(struct pci_bus *bus)
80101
{
81102
int i;

include/linux/pci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,6 +1438,7 @@ void pci_bus_add_resource(struct pci_bus *bus, struct resource *res,
14381438
unsigned int flags);
14391439
struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n);
14401440
void pci_bus_remove_resources(struct pci_bus *bus);
1441+
void pci_bus_remove_resource(struct pci_bus *bus, struct resource *res);
14411442
int devm_request_pci_bus_resources(struct device *dev,
14421443
struct list_head *resources);
14431444

0 commit comments

Comments
 (0)