⬢ DragonFlyBSD Kernel Audit
DF-2383 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/acpica/acpi_hp/acpi_hp.c b/sys/dev/acpica/acpi_hp/acpi_hp.c
--- a/sys/dev/acpica/acpi_hp/acpi_hp.c
+++ b/sys/dev/acpica/acpi_hp/acpi_hp.c
@@ -588,7 +588,18 @@
 	}
 	else {
 		if (sc->has_notify) {
-			ACPI_WMI_REMOVE_EVENT_HANDLER(dev,
+			/*
+			 * DF-2383: the acpi_wmi_if KBI dispatches on the
+			 * device_t passed here.  Passing our own dev
+			 * resolves to kobj_error_method() (ENXIO) because
+			 * acpi_hp does not implement acpi_wmi_if -- the
+			 * notify handler was in fact never unregistered
+			 * and stayed armed across detach/unload.  Target
+			 * the WMI provider device so its (draining)
+			 * remove path actually runs before our softc is
+			 * freed by newbus.
+			 */
+			ACPI_WMI_REMOVE_EVENT_HANDLER(sc->wmi_dev,
 			    ACPI_HP_WMI_EVENT_GUID);
 		}
 		sysctl_ctx_free(&sc->sysctl_ctx);
diff --git a/sys/dev/acpica/acpi_wmi/acpi_wmi.c b/sys/dev/acpica/acpi_wmi/acpi_wmi.c
--- a/sys/dev/acpica/acpi_wmi/acpi_wmi.c
+++ b/sys/dev/acpica/acpi_wmi/acpi_wmi.c
@@ -75,6 +75,7 @@
 	pid_t		wmistat_open_pid; /* pid operating on /dev/wmistat */
 	int		wmistat_bufptr;	/* /dev/wmistat ptr to buffer position */
 	TAILQ_HEAD(wmi_info_list_head, wmi_info) wmi_info_list;
+	int		wmi_dispatching; /* in-flight notify dispatches */
 };
 
 /*
@@ -456,6 +457,17 @@
 		winfo->event_handler = NULL;
 		winfo->event_handler_user_data = NULL;
 	}
+	/*
+	 * DF-2381/DF-2383: AcpiOsWaitEventsComplete() is an empty no-op on
+	 * DragonFly (contrib/dev/acpica/source/include/platform/
+	 * acdragonfly.h:219-220), and acpi_wmi_notify_handler invokes the
+	 * client handler AFTER releasing the WMI serial lock.  A dispatch
+	 * that copied the handler before it was NULLed above can therefore
+	 * still be running while our caller (e.g. acpi_hp_detach) frees
+	 * its softc.  Drain in-flight dispatches before returning.
+	 */
+	while (sc->wmi_dispatching > 0)
+		lksleep(sc, &acpi_wmi_serial, 0, "wmidrain", 0);
 	ACPI_SERIAL_END(acpi_wmi);
 
 	return (status);
@@ -642,6 +654,7 @@
 			if (winfo->event_handler) {
 				handler = winfo->event_handler;
 				handler_data = winfo->event_handler_user_data;
+				sc->wmi_dispatching++;
 				break;
 			}
 		}
@@ -649,6 +662,10 @@
 	ACPI_SERIAL_END(acpi_wmi);
 	if (handler) {
 		handler(h, notify, handler_data);
+		ACPI_SERIAL_BEGIN(acpi_wmi);
+		if (--sc->wmi_dispatching == 0)
+			wakeup(sc);
+		ACPI_SERIAL_END(acpi_wmi);
 	}
 }