All of lore.kernel.org
 help / color / mirror / Atom feed
* FAILED: patch "[PATCH] s390/pci: fix zpci_zdev_put() on reserve" failed to apply to 5.10-stable tree
@ 2021-10-10 10:22 gregkh
  2021-10-12  9:34 ` [PATCH 5.10 STABLE] s390/pci: fix zpci_zdev_put() on reserve Niklas Schnelle
  0 siblings, 1 reply; 4+ messages in thread
From: gregkh @ 2021-10-10 10:22 UTC (permalink / raw)
  To: schnelle, gor; +Cc: stable


The patch below does not apply to the 5.10-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.

thanks,

greg k-h

------------------ original commit in Linus's tree ------------------

From a46044a92add6a400f4dada7b943b30221f7cc80 Mon Sep 17 00:00:00 2001
From: Niklas Schnelle <schnelle@linux.ibm.com>
Date: Wed, 22 Sep 2021 15:55:12 +0200
Subject: [PATCH] s390/pci: fix zpci_zdev_put() on reserve

Since commit 2a671f77ee49 ("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: 2a671f77ee49 ("s390/pci: fix use after free of zpci_dev")
Cc: stable@vger.kernel.org
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>

diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h
index e4803ec51110..6b3c366af78e 100644
--- a/arch/s390/include/asm/pci.h
+++ b/arch/s390/include/asm/pci.h
@@ -207,6 +207,8 @@ int zpci_enable_device(struct zpci_dev *);
 int zpci_disable_device(struct zpci_dev *);
 int zpci_scan_configured_device(struct zpci_dev *zdev, u32 fh);
 int zpci_deconfigure_device(struct zpci_dev *zdev);
+void zpci_device_reserved(struct zpci_dev *zdev);
+bool zpci_is_device_configured(struct zpci_dev *zdev);
 
 int zpci_register_ioat(struct zpci_dev *, u8, u64, u64, u64);
 int zpci_unregister_ioat(struct zpci_dev *, u8);
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index e7e6788d75a8..b833155ce838 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -92,7 +92,7 @@ void zpci_remove_reserved_devices(void)
 	spin_unlock(&zpci_list_lock);
 
 	list_for_each_entry_safe(zdev, tmp, &remove, entry)
-		zpci_zdev_put(zdev);
+		zpci_device_reserved(zdev);
 }
 
 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)
 	return ERR_PTR(rc);
 }
 
+bool zpci_is_device_configured(struct zpci_dev *zdev)
+{
+	enum zpci_state state = zdev->state;
+
+	return state != ZPCI_FN_STATE_RESERVED &&
+		state != ZPCI_FN_STATE_STANDBY;
+}
+
 /**
  * zpci_scan_configured_device() - Scan a freshly configured zpci_dev
  * @zdev: The zpci_dev to be configured
@@ -822,6 +830,31 @@ int zpci_deconfigure_device(struct zpci_dev *zdev)
 	return 0;
 }
 
+/**
+ * zpci_device_reserved() - Mark device as resverved
+ * @zdev: the zpci_dev that was reserved
+ *
+ * Handle the case that a given zPCI function was reserved by another system.
+ * After a call to this function the zpci_dev can not be found via
+ * get_zdev_by_fid() anymore but may still be accessible via existing
+ * references though it will not be functional anymore.
+ */
+void zpci_device_reserved(struct zpci_dev *zdev)
+{
+	if (zdev->has_hp_slot)
+		zpci_exit_slot(zdev);
+	/*
+	 * Remove device from zpci_list as it is going away. This also
+	 * makes sure we ignore subsequent zPCI events for this device.
+	 */
+	spin_lock(&zpci_list_lock);
+	list_del(&zdev->entry);
+	spin_unlock(&zpci_list_lock);
+	zdev->state = ZPCI_FN_STATE_RESERVED;
+	zpci_dbg(3, "rsv fid:%x\n", zdev->fid);
+	zpci_zdev_put(zdev);
+}
+
 void zpci_release_device(struct kref *kref)
 {
 	struct zpci_dev *zdev = container_of(kref, struct zpci_dev, kref);
@@ -843,6 +876,12 @@ void zpci_release_device(struct kref *kref)
 	case ZPCI_FN_STATE_STANDBY:
 		if (zdev->has_hp_slot)
 			zpci_exit_slot(zdev);
+		spin_lock(&zpci_list_lock);
+		list_del(&zdev->entry);
+		spin_unlock(&zpci_list_lock);
+		zpci_dbg(3, "rsv fid:%x\n", zdev->fid);
+		fallthrough;
+	case ZPCI_FN_STATE_RESERVED:
 		if (zdev->has_resources)
 			zpci_cleanup_bus_resources(zdev);
 		zpci_bus_device_unregister(zdev);
@@ -851,10 +890,6 @@ void zpci_release_device(struct kref *kref)
 	default:
 		break;
 	}
-
-	spin_lock(&zpci_list_lock);
-	list_del(&zdev->entry);
-	spin_unlock(&zpci_list_lock);
 	zpci_dbg(3, "rem fid:%x\n", zdev->fid);
 	kfree(zdev);
 }
diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c
index c856f80cb21b..5b8d647523f9 100644
--- a/arch/s390/pci/pci_event.c
+++ b/arch/s390/pci/pci_event.c
@@ -140,7 +140,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
 			/* The 0x0304 event may immediately reserve the device */
 			if (!clp_get_state(zdev->fid, &state) &&
 			    state == ZPCI_FN_STATE_RESERVED) {
-				zpci_zdev_put(zdev);
+				zpci_device_reserved(zdev);
 			}
 		}
 		break;
@@ -151,7 +151,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
 	case 0x0308: /* Standby -> Reserved */
 		if (!zdev)
 			break;
-		zpci_zdev_put(zdev);
+		zpci_device_reserved(zdev);
 		break;
 	default:
 		break;
diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c
index 014868752cd4..dcefdb42ac46 100644
--- a/drivers/pci/hotplug/s390_pci_hpc.c
+++ b/drivers/pci/hotplug/s390_pci_hpc.c
@@ -62,14 +62,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 	struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev,
 					     hotplug_slot);
 
-	switch (zdev->state) {
-	case ZPCI_FN_STATE_STANDBY:
-		*value = 0;
-		break;
-	default:
-		*value = 1;
-		break;
-	}
+	*value = zpci_is_device_configured(zdev) ? 1 : 0;
 	return 0;
 }
 


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 5.10 STABLE] s390/pci: fix zpci_zdev_put() on reserve
  2021-10-10 10:22 FAILED: patch "[PATCH] s390/pci: fix zpci_zdev_put() on reserve" failed to apply to 5.10-stable tree gregkh
@ 2021-10-12  9:34 ` Niklas Schnelle
  2021-10-13  9:09   ` Greg KH
  0 siblings, 1 reply; 4+ messages in thread
From: Niklas Schnelle @ 2021-10-12  9:34 UTC (permalink / raw)
  To: gregkh, stable; +Cc: gor, schnelle

[ Upstream commit a46044a92add6a400f4dada7b943b30221f7cc80 ]

Since commit 2a671f77ee49 ("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: 2a671f77ee49 ("s390/pci: fix use after free of zpci_dev")
Cc: stable@vger.kernel.org
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
---
 arch/s390/include/asm/pci.h        |  3 ++
 arch/s390/pci/pci.c                | 45 ++++++++++++++++++++++++++----
 arch/s390/pci/pci_event.c          |  4 +--
 drivers/pci/hotplug/s390_pci_hpc.c |  9 +-----
 4 files changed, 46 insertions(+), 15 deletions(-)

diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h
index a75d94a9bcb2..1226971533fe 100644
--- a/arch/s390/include/asm/pci.h
+++ b/arch/s390/include/asm/pci.h
@@ -205,6 +205,9 @@ int zpci_create_device(u32 fid, u32 fh, enum zpci_state state);
 void zpci_remove_device(struct zpci_dev *zdev, bool set_error);
 int zpci_enable_device(struct zpci_dev *);
 int zpci_disable_device(struct zpci_dev *);
+void zpci_device_reserved(struct zpci_dev *zdev);
+bool zpci_is_device_configured(struct zpci_dev *zdev);
+
 int zpci_register_ioat(struct zpci_dev *, u8, u64, u64, u64);
 int zpci_unregister_ioat(struct zpci_dev *, u8);
 void zpci_remove_reserved_devices(void);
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index f5ddbc625c1a..e14e4a3a647a 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -92,7 +92,7 @@ void zpci_remove_reserved_devices(void)
 	spin_unlock(&zpci_list_lock);
 
 	list_for_each_entry_safe(zdev, tmp, &remove, entry)
-		zpci_zdev_put(zdev);
+		zpci_device_reserved(zdev);
 }
 
 int pci_domain_nr(struct pci_bus *bus)
@@ -787,6 +787,39 @@ int zpci_create_device(u32 fid, u32 fh, enum zpci_state state)
 	return rc;
 }
 
+bool zpci_is_device_configured(struct zpci_dev *zdev)
+{
+	enum zpci_state state = zdev->state;
+
+	return state != ZPCI_FN_STATE_RESERVED &&
+		state != ZPCI_FN_STATE_STANDBY;
+}
+
+/**
+ * zpci_device_reserved() - Mark device as resverved
+ * @zdev: the zpci_dev that was reserved
+ *
+ * Handle the case that a given zPCI function was reserved by another system.
+ * After a call to this function the zpci_dev can not be found via
+ * get_zdev_by_fid() anymore but may still be accessible via existing
+ * references though it will not be functional anymore.
+ */
+void zpci_device_reserved(struct zpci_dev *zdev)
+{
+	if (zdev->has_hp_slot)
+		zpci_exit_slot(zdev);
+	/*
+	 * Remove device from zpci_list as it is going away. This also
+	 * makes sure we ignore subsequent zPCI events for this device.
+	 */
+	spin_lock(&zpci_list_lock);
+	list_del(&zdev->entry);
+	spin_unlock(&zpci_list_lock);
+	zdev->state = ZPCI_FN_STATE_RESERVED;
+	zpci_dbg(3, "rsv fid:%x\n", zdev->fid);
+	zpci_zdev_put(zdev);
+}
+
 void zpci_release_device(struct kref *kref)
 {
 	struct zpci_dev *zdev = container_of(kref, struct zpci_dev, kref);
@@ -802,6 +835,12 @@ void zpci_release_device(struct kref *kref)
 	case ZPCI_FN_STATE_STANDBY:
 		if (zdev->has_hp_slot)
 			zpci_exit_slot(zdev);
+		spin_lock(&zpci_list_lock);
+		list_del(&zdev->entry);
+		spin_unlock(&zpci_list_lock);
+		zpci_dbg(3, "rsv fid:%x\n", zdev->fid);
+		fallthrough;
+	case ZPCI_FN_STATE_RESERVED:
 		zpci_cleanup_bus_resources(zdev);
 		zpci_bus_device_unregister(zdev);
 		zpci_destroy_iommu(zdev);
@@ -809,10 +848,6 @@ void zpci_release_device(struct kref *kref)
 	default:
 		break;
 	}
-
-	spin_lock(&zpci_list_lock);
-	list_del(&zdev->entry);
-	spin_unlock(&zpci_list_lock);
 	zpci_dbg(3, "rem fid:%x\n", zdev->fid);
 	kfree(zdev);
 }
diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c
index ac0c65cdd69d..b7cfde7e80a8 100644
--- a/arch/s390/pci/pci_event.c
+++ b/arch/s390/pci/pci_event.c
@@ -146,7 +146,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
 		zdev->state = ZPCI_FN_STATE_STANDBY;
 		if (!clp_get_state(ccdf->fid, &state) &&
 		    state == ZPCI_FN_STATE_RESERVED) {
-			zpci_zdev_put(zdev);
+			zpci_device_reserved(zdev);
 		}
 		break;
 	case 0x0306: /* 0x308 or 0x302 for multiple devices */
@@ -156,7 +156,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
 	case 0x0308: /* Standby -> Reserved */
 		if (!zdev)
 			break;
-		zpci_zdev_put(zdev);
+		zpci_device_reserved(zdev);
 		break;
 	default:
 		break;
diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c
index a047c421debe..93174f503464 100644
--- a/drivers/pci/hotplug/s390_pci_hpc.c
+++ b/drivers/pci/hotplug/s390_pci_hpc.c
@@ -109,14 +109,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 	struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev,
 					     hotplug_slot);
 
-	switch (zdev->state) {
-	case ZPCI_FN_STATE_STANDBY:
-		*value = 0;
-		break;
-	default:
-		*value = 1;
-		break;
-	}
+	*value = zpci_is_device_configured(zdev) ? 1 : 0;
 	return 0;
 }
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 5.10 STABLE] s390/pci: fix zpci_zdev_put() on reserve
  2021-10-12  9:34 ` [PATCH 5.10 STABLE] s390/pci: fix zpci_zdev_put() on reserve Niklas Schnelle
@ 2021-10-13  9:09   ` Greg KH
  2021-10-13 10:26     ` Niklas Schnelle
  0 siblings, 1 reply; 4+ messages in thread
From: Greg KH @ 2021-10-13  9:09 UTC (permalink / raw)
  To: Niklas Schnelle; +Cc: stable, gor

On Tue, Oct 12, 2021 at 11:34:25AM +0200, Niklas Schnelle wrote:
> [ Upstream commit a46044a92add6a400f4dada7b943b30221f7cc80 ]
> 
> Since commit 2a671f77ee49 ("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.

Same for the 5.14 patch, please submit the series that resolves this,
not changing individual patches a lot.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 5.10 STABLE] s390/pci: fix zpci_zdev_put() on reserve
  2021-10-13  9:09   ` Greg KH
@ 2021-10-13 10:26     ` Niklas Schnelle
  0 siblings, 0 replies; 4+ messages in thread
From: Niklas Schnelle @ 2021-10-13 10:26 UTC (permalink / raw)
  To: Greg KH; +Cc: stable, gor

On Wed, 2021-10-13 at 11:09 +0200, Greg KH wrote:
> On Tue, Oct 12, 2021 at 11:34:25AM +0200, Niklas Schnelle wrote:
> > [ Upstream commit a46044a92add6a400f4dada7b943b30221f7cc80 ]
> > 
> > Since commit 2a671f77ee49 ("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.
> 
> Same for the 5.14 patch, please submit the series that resolves this,
> not changing individual patches a lot.
> 
> thanks,
> 
> greg k-h

For the 5.14 patch that makes total sense, though these were not
originally a series but just happened to touch the same area. For v5.10
we don't yet have the flag for tracking the PCI resources in the struct
zpci_dev which was introduced as part of a larger non-trivial
refactoring series which landed in mainline as part of 5.13.

This backport patch consequently does not include the change from
commit 023cc3cb1e4b ("s390/pci: cleanup resources only if necessary").
Instead it does an unconditional call to zpci_cleanup_bus_resources()
as done previously on 5.10. This is not releant for fixing the problem
targeted by this patch. Interestingly though due to a difference in
behavior of the common code while the problem exists on 5.10 from code
inspection it seems that at the point of releasing the device common
code does not hold a reference to the pci_dev and so the original crash
doesn't happen in that scenario but could possibly occur under
different circumstances.

Thanks,
Niklas


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-10-13 10:26 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-10 10:22 FAILED: patch "[PATCH] s390/pci: fix zpci_zdev_put() on reserve" failed to apply to 5.10-stable tree gregkh
2021-10-12  9:34 ` [PATCH 5.10 STABLE] s390/pci: fix zpci_zdev_put() on reserve Niklas Schnelle
2021-10-13  9:09   ` Greg KH
2021-10-13 10:26     ` Niklas Schnelle

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.