mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-09 14:56:54 -04:00
s390/pci: Fix leak of struct zpci_dev when zpci_add_device() fails
Prior to commit0467cdde8c("s390/pci: Sort PCI functions prior to creating virtual busses") the IOMMU was initialized and the device was registered as part of zpci_create_device() with the struct zpci_dev freed if either resulted in an error. With that commit this was moved into a separate function called zpci_add_device(). While this new function logs when adding failed, it expects the caller not to use and to free the struct zpci_dev on error. This difference between it and zpci_create_device() was missed while changing the callers and the incompletely initialized struct zpci_dev may get used in zpci_scan_configured_device in the error path. This then leads to a crash due to the device not being registered with the zbus. It was also not freed in this case. Fix this by handling the error return of zpci_add_device(). Since in this case the zdev was not added to the zpci_list it can simply be discarded and freed. Also make this more explicit by moving the kref_init() into zpci_add_device() and document that zpci_zdev_get()/zpci_zdev_put() must be used after adding. Cc: stable@vger.kernel.org Fixes:0467cdde8c("s390/pci: Sort PCI functions prior to creating virtual busses") Reviewed-by: Gerd Bayer <gbayer@linux.ibm.com> Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com> Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com> Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
This commit is contained in:
committed by
Heiko Carstens
parent
adb44a4bfc
commit
48796104c8
@@ -776,8 +776,9 @@ int zpci_hot_reset_device(struct zpci_dev *zdev)
|
||||
* @fh: Current Function Handle of the device to be created
|
||||
* @state: Initial state after creation either Standby or Configured
|
||||
*
|
||||
* Creates a new zpci device and adds it to its, possibly newly created, zbus
|
||||
* as well as zpci_list.
|
||||
* Allocates a new struct zpci_dev and queries the platform for its details.
|
||||
* If successful the device can subsequently be added to the zPCI subsystem
|
||||
* using zpci_add_device().
|
||||
*
|
||||
* Returns: the zdev on success or an error pointer otherwise
|
||||
*/
|
||||
@@ -800,7 +801,6 @@ struct zpci_dev *zpci_create_device(u32 fid, u32 fh, enum zpci_state state)
|
||||
goto error;
|
||||
zdev->state = state;
|
||||
|
||||
kref_init(&zdev->kref);
|
||||
mutex_init(&zdev->state_lock);
|
||||
mutex_init(&zdev->fmb_lock);
|
||||
mutex_init(&zdev->kzdev_lock);
|
||||
@@ -813,6 +813,17 @@ struct zpci_dev *zpci_create_device(u32 fid, u32 fh, enum zpci_state state)
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* zpci_add_device() - Add a previously created zPCI device to the zPCI subsystem
|
||||
* @zdev: The zPCI device to be added
|
||||
*
|
||||
* A struct zpci_dev is added to the zPCI subsystem and to a virtual PCI bus creating
|
||||
* a new one as necessary. A hotplug slot is created and events start to be handled.
|
||||
* If successful from this point on zpci_zdev_get() and zpci_zdev_put() must be used.
|
||||
* If adding the struct zpci_dev fails the device was not added and should be freed.
|
||||
*
|
||||
* Return: 0 on success, or an error code otherwise
|
||||
*/
|
||||
int zpci_add_device(struct zpci_dev *zdev)
|
||||
{
|
||||
int rc;
|
||||
@@ -826,6 +837,7 @@ int zpci_add_device(struct zpci_dev *zdev)
|
||||
if (rc)
|
||||
goto error_destroy_iommu;
|
||||
|
||||
kref_init(&zdev->kref);
|
||||
spin_lock(&zpci_list_lock);
|
||||
list_add_tail(&zdev->entry, &zpci_list);
|
||||
spin_unlock(&zpci_list_lock);
|
||||
@@ -1118,7 +1130,8 @@ static void zpci_add_devices(struct list_head *scan_list)
|
||||
list_sort(NULL, scan_list, &zpci_cmp_rid);
|
||||
list_for_each_entry_safe(zdev, tmp, scan_list, entry) {
|
||||
list_del_init(&zdev->entry);
|
||||
zpci_add_device(zdev);
|
||||
if (zpci_add_device(zdev))
|
||||
kfree(zdev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -340,7 +340,10 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
|
||||
zdev = zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_CONFIGURED);
|
||||
if (IS_ERR(zdev))
|
||||
break;
|
||||
zpci_add_device(zdev);
|
||||
if (zpci_add_device(zdev)) {
|
||||
kfree(zdev);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* the configuration request may be stale */
|
||||
if (zdev->state != ZPCI_FN_STATE_STANDBY)
|
||||
@@ -354,7 +357,10 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
|
||||
zdev = zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_STANDBY);
|
||||
if (IS_ERR(zdev))
|
||||
break;
|
||||
zpci_add_device(zdev);
|
||||
if (zpci_add_device(zdev)) {
|
||||
kfree(zdev);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
zpci_update_fh(zdev, ccdf->fh);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user