⬢ DragonFlyBSD Kernel Audit
DF-5091 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/acpica/acpi_button.c
+++ b/sys/dev/acpica/acpi_button.c
@@ -57,6 +57,7 @@
 
 static int	acpi_button_probe(device_t dev);
 static int	acpi_button_attach(device_t dev);
+static int	acpi_button_detach(device_t dev);
 static int	acpi_button_suspend(device_t dev);
 static int	acpi_button_resume(device_t dev);
 static void 	acpi_button_notify_handler(ACPI_HANDLE h, UINT32 notify,
@@ -75,6 +76,7 @@
     /* Device interface */
     DEVMETHOD(device_probe,	acpi_button_probe),
     DEVMETHOD(device_attach,	acpi_button_attach),
+    DEVMETHOD(device_detach,	acpi_button_detach);
     DEVMETHOD(device_suspend,	acpi_button_suspend),
     DEVMETHOD(device_shutdown,	acpi_button_suspend),
     DEVMETHOD(device_resume,	acpi_button_resume),
@@ -173,6 +175,37 @@
 }
 
 static int
+acpi_button_detach(device_t dev)
+{
+    struct acpi_button_softc	*sc;
+    int				event;
+
+    sc = device_get_softc(dev);
+    event = (sc->button_type == ACPI_SLEEP_BUTTON) ?
+	ACPI_EVENT_SLEEP_BUTTON : ACPI_EVENT_POWER_BUTTON;
+
+    /*
+     * Undo acpi_button_attach(): without this the ACPICA notify handler
+     * (or fixed-event handler) keeps a pointer to our softc forever,
+     * pinning the driver/module and turning any future detach into a
+     * use-after-free.
+     *
+     * NOTE: AcpiRemoveNotifyHandler() waits for in-progress handler
+     * invocations but not for callbacks already queued to
+     * AcpiOsExecute(); a complete fix would drain those before
+     * returning (e.g. a generation counter checked by
+     * acpi_button_notify_sleep/wakeup).
+     */
+    if (sc->fixed)
+	AcpiRemoveFixedEventHandler(event, acpi_button_fixed_handler);
+    else
+	AcpiRemoveNotifyHandler(sc->button_handle, ACPI_DEVICE_NOTIFY,
+	    acpi_button_notify_handler);
+
+    return (0);
+}
+
+static int
 acpi_button_suspend(device_t dev)
 {
     return (0);