Skip to content

Commit a46044a

Browse files
niklas88Vasily Gorbik
authored andcommitted
s390/pci: fix zpci_zdev_put() on reserve
Since commit 2a671f7 ("s390/pci: fix use after free of zpci_dev") the reference count of a zpci_dev is incremented between pcibios_add_device() and pcibios_release_device() which was supposed to prevent the zpci_dev from being freed while the common PCI code has access to it. It was missed however that the handling of zPCI availability events assumed that once zpci_zdev_put() was called no later availability event would still see the device. With the previously mentioned commit however this assumption no longer holds and we must make sure that we only drop the initial long-lived reference the zPCI subsystem holds exactly once. Do so by introducing a zpci_device_reserved() function that handles when a device is reserved. Here we make sure the zpci_dev will not be considered for further events by removing it from the zpci_list. This also means that the device actually stays in the ZPCI_FN_STATE_RESERVED state between the time we know it has been reserved and the final reference going away. We thus need to consider it a real state instead of just a conceptual state after the removal. The final cleanup of PCI resources, removal from zbus, and destruction of the IOMMU stays in zpci_release_device() to make sure holders of the reference do see valid data until the release. Fixes: 2a671f7 ("s390/pci: fix use after free of zpci_dev") Cc: [email protected] Signed-off-by: Niklas Schnelle <[email protected]> Signed-off-by: Vasily Gorbik <[email protected]>
1 parent 686cb8b commit a46044a

File tree

4 files changed

+45
-15
lines changed

4 files changed

+45
-15
lines changed

arch/s390/include/asm/pci.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ int zpci_enable_device(struct zpci_dev *);
207207
int zpci_disable_device(struct zpci_dev *);
208208
int zpci_scan_configured_device(struct zpci_dev *zdev, u32 fh);
209209
int zpci_deconfigure_device(struct zpci_dev *zdev);
210+
void zpci_device_reserved(struct zpci_dev *zdev);
211+
bool zpci_is_device_configured(struct zpci_dev *zdev);
210212

211213
int zpci_register_ioat(struct zpci_dev *, u8, u64, u64, u64);
212214
int zpci_unregister_ioat(struct zpci_dev *, u8);

arch/s390/pci/pci.c

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ void zpci_remove_reserved_devices(void)
9292
spin_unlock(&zpci_list_lock);
9393

9494
list_for_each_entry_safe(zdev, tmp, &remove, entry)
95-
zpci_zdev_put(zdev);
95+
zpci_device_reserved(zdev);
9696
}
9797

9898
int pci_domain_nr(struct pci_bus *bus)
@@ -751,6 +751,14 @@ struct zpci_dev *zpci_create_device(u32 fid, u32 fh, enum zpci_state state)
751751
return ERR_PTR(rc);
752752
}
753753

754+
bool zpci_is_device_configured(struct zpci_dev *zdev)
755+
{
756+
enum zpci_state state = zdev->state;
757+
758+
return state != ZPCI_FN_STATE_RESERVED &&
759+
state != ZPCI_FN_STATE_STANDBY;
760+
}
761+
754762
/**
755763
* zpci_scan_configured_device() - Scan a freshly configured zpci_dev
756764
* @zdev: The zpci_dev to be configured
@@ -822,6 +830,31 @@ int zpci_deconfigure_device(struct zpci_dev *zdev)
822830
return 0;
823831
}
824832

833+
/**
834+
* zpci_device_reserved() - Mark device as resverved
835+
* @zdev: the zpci_dev that was reserved
836+
*
837+
* Handle the case that a given zPCI function was reserved by another system.
838+
* After a call to this function the zpci_dev can not be found via
839+
* get_zdev_by_fid() anymore but may still be accessible via existing
840+
* references though it will not be functional anymore.
841+
*/
842+
void zpci_device_reserved(struct zpci_dev *zdev)
843+
{
844+
if (zdev->has_hp_slot)
845+
zpci_exit_slot(zdev);
846+
/*
847+
* Remove device from zpci_list as it is going away. This also
848+
* makes sure we ignore subsequent zPCI events for this device.
849+
*/
850+
spin_lock(&zpci_list_lock);
851+
list_del(&zdev->entry);
852+
spin_unlock(&zpci_list_lock);
853+
zdev->state = ZPCI_FN_STATE_RESERVED;
854+
zpci_dbg(3, "rsv fid:%x\n", zdev->fid);
855+
zpci_zdev_put(zdev);
856+
}
857+
825858
void zpci_release_device(struct kref *kref)
826859
{
827860
struct zpci_dev *zdev = container_of(kref, struct zpci_dev, kref);
@@ -843,6 +876,12 @@ void zpci_release_device(struct kref *kref)
843876
case ZPCI_FN_STATE_STANDBY:
844877
if (zdev->has_hp_slot)
845878
zpci_exit_slot(zdev);
879+
spin_lock(&zpci_list_lock);
880+
list_del(&zdev->entry);
881+
spin_unlock(&zpci_list_lock);
882+
zpci_dbg(3, "rsv fid:%x\n", zdev->fid);
883+
fallthrough;
884+
case ZPCI_FN_STATE_RESERVED:
846885
if (zdev->has_resources)
847886
zpci_cleanup_bus_resources(zdev);
848887
zpci_bus_device_unregister(zdev);
@@ -851,10 +890,6 @@ void zpci_release_device(struct kref *kref)
851890
default:
852891
break;
853892
}
854-
855-
spin_lock(&zpci_list_lock);
856-
list_del(&zdev->entry);
857-
spin_unlock(&zpci_list_lock);
858893
zpci_dbg(3, "rem fid:%x\n", zdev->fid);
859894
kfree(zdev);
860895
}

arch/s390/pci/pci_event.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
140140
/* The 0x0304 event may immediately reserve the device */
141141
if (!clp_get_state(zdev->fid, &state) &&
142142
state == ZPCI_FN_STATE_RESERVED) {
143-
zpci_zdev_put(zdev);
143+
zpci_device_reserved(zdev);
144144
}
145145
}
146146
break;
@@ -151,7 +151,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
151151
case 0x0308: /* Standby -> Reserved */
152152
if (!zdev)
153153
break;
154-
zpci_zdev_put(zdev);
154+
zpci_device_reserved(zdev);
155155
break;
156156
default:
157157
break;

drivers/pci/hotplug/s390_pci_hpc.c

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
6262
struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev,
6363
hotplug_slot);
6464

65-
switch (zdev->state) {
66-
case ZPCI_FN_STATE_STANDBY:
67-
*value = 0;
68-
break;
69-
default:
70-
*value = 1;
71-
break;
72-
}
65+
*value = zpci_is_device_configured(zdev) ? 1 : 0;
7366
return 0;
7467
}
7568

0 commit comments

Comments
 (0)