⬢ DragonFlyBSD Kernel Audit
DF-5518 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/gpio/gpio_acpi/gpio_acpi.c
+++ b/sys/bus/gpio/gpio_acpi/gpio_acpi.c
@@ -42,6 +42,7 @@
 #include <sys/errno.h>
 #include <sys/lock.h>
 #include <sys/bus.h>
+#include <machine/atomic.h>
 
 #include "opt_acpi.h"
 #include "acpi.h"
@@ -61,6 +62,8 @@
 struct acpi_gpio_handler_data {
 	struct acpi_connection_info info;
 	device_t dev;
+	int	busy;			/* in-flight space-handler count */
+	int	detaching;
 };
 
 struct gpio_acpi_softc {
@@ -191,6 +194,8 @@
 
 	handle = acpi_get_handle(sc->parent);
 	data->dev = sc->parent;
+	data->busy = 0;
+	data->detaching = 0;
 	s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
 	    &gpio_acpi_space_handler, NULL, data);
 	if (ACPI_FAILURE(s)) {
@@ -236,6 +241,21 @@
 	if (BitWidth == 0 || BitWidth > 64)
 		return (AE_BAD_PARAMETER);
 
+	/*
+	 * Refuse dispatches once detach has started and account for
+	 * in-flight ones: AcpiRemoveAddressSpaceHandler() does not wait
+	 * for running handlers (they execute with the AML interpreter
+	 * exited, see AcpiEvAddressSpaceDispatch), and detach tears down
+	 * everything they touch.
+	 */
+	if (data->detaching)
+		return (AE_ABORT_METHOD);
+	atomic_add_int(&data->busy, 1);
+	if (data->detaching) {
+		atomic_subtract_int(&data->busy, 1);
+		return (AE_ABORT_METHOD);
+	}
+
 	s = AcpiBufferToResource(info->Connection, info->Length, &Resource);
 	if (ACPI_FAILURE(s)) {
 		device_printf(dev, "AcpiBufferToResource failed\n");
@@ -299,6 +319,8 @@
 
 err:
 	ACPI_FREE(Resource);
+	atomic_subtract_int(&data->busy, 1);
+	wakeup(&data->busy);
 	return (s);
 }
 
@@ -487,12 +509,22 @@
 gpio_acpi_detach(device_t dev)
 {
 	struct gpio_acpi_softc *sc = device_get_softc(dev);
+	struct acpi_gpio_handler_data *data = &sc->space_handler_data;
+
+	/*
+	 * Stop new space-handler dispatches and drain in-flight ones
+	 * before anything they touch is torn down (the parent driver's
+	 * detach frees the provider softc, resources and lock that a
+	 * running gpio_acpi_space_handler still uses).
+	 */
+	data->detaching = 1;
+	gpio_acpi_remove_address_space_handler(sc);
+	while (data->busy != 0)
+		tsleep(&data->busy, 0, "gpodet", 1);
 
 	if (sc->infos != NULL)
 		gpio_acpi_unmap_aei(sc);
 
-	gpio_acpi_remove_address_space_handler(sc);
-
 	return (0);
 }