⬢ DragonFlyBSD Kernel Audit
DF-5656 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/u4b/usb_dynamic.c
+++ b/sys/bus/u4b/usb_dynamic.c
@@ -64,6 +64,16 @@
 usb_quirk_ioctl_t *usb_quirk_ioctl_p = &usb_quirk_ioctl_w;
 devclass_t usb_devclass_ptr = NULL;
 
+/*
+ * DF-5656: quiescence barrier for the dynamically rebound USB dispatch
+ * pointers.  Consumers hold this lock shared across the pointer read AND
+ * the indirect call; module unload hooks acquire it exclusive before
+ * resetting the pointers, which proves no CPU is still executing inside
+ * the module when the SYSUNINIT returns.  This replaces the old fixed
+ * tsleep(1s) "grace period", which was not a synchronization primitive.
+ */
+struct lock usb_dyn_lock = LOCK_INITIALIZER("usb dyn dispatch", 0, 0);
+
 static usb_error_t
 usb_temp_setup_by_index_w(struct usb_device *udev, uint16_t index)
 {
@@ -101,44 +111,44 @@
 void
 usb_quirk_unload(void *arg)
 {
+	/* wait for CPUs to exit the loaded functions, if any */
+
+	lockmgr(&usb_dyn_lock, LK_EXCLUSIVE);
+
 	/* reset function pointers */
 
 	usb_test_quirk_p = &usb_test_quirk_w;
 	usb_quirk_ioctl_p = &usb_quirk_ioctl_w;
 
-	/* wait for CPU to exit the loaded functions, if any */
-
-	/* XXX this is a tradeoff */
-
-	tsleep(usb_quirk_unload, 0, "WAIT", hz);
+	lockmgr(&usb_dyn_lock, LK_RELEASE);
 }
 
 void
 usb_temp_unload(void *arg)
 {
+	/* wait for CPUs to exit the loaded functions, if any */
+
+	lockmgr(&usb_dyn_lock, LK_EXCLUSIVE);
+
 	/* reset function pointers */
 
 	usb_temp_get_desc_p = &usb_temp_get_desc_w;
 	usb_temp_setup_by_index_p = &usb_temp_setup_by_index_w;
 	usb_temp_unsetup_p = &usb_temp_unsetup_w;
 
-	/* wait for CPU to exit the loaded functions, if any */
-
-	/* XXX this is a tradeoff */
-
-	tsleep(usb_quirk_unload, 0, "WAIT", hz);
+	lockmgr(&usb_dyn_lock, LK_RELEASE);
 }
 
 void
 usb_bus_unload(void *arg)
 {
-	/* reset function pointers */
+	/* wait for CPUs to exit the loaded functions, if any */
 
-	usb_devclass_ptr = NULL;
+	lockmgr(&usb_dyn_lock, LK_EXCLUSIVE);
 
-	/* wait for CPU to exit the loaded functions, if any */
+	/* reset function pointers */
 
-	/* XXX this is a tradeoff */
+	usb_devclass_ptr = NULL;
 
-	tsleep(usb_quirk_unload, 0, "WAIT", hz);
+	lockmgr(&usb_dyn_lock, LK_RELEASE);
 }
--- a/sys/bus/u4b/usb_dynamic.h
+++ b/sys/bus/u4b/usb_dynamic.h
@@ -45,6 +45,15 @@
 
 /* global function pointers */
 
+extern struct lock usb_dyn_lock;	/* DF-5656 quiescence barrier */
+
+/*
+ * Consumers must hold the dispatch lock shared across BOTH the pointer
+ * read and the indirect call, so an unloading module can prove quiescence.
+ */
+#define	USB_DYN_RLOCK()		lockmgr(&usb_dyn_lock, LK_SHARED)
+#define	USB_DYN_RUNLOCK()	lockmgr(&usb_dyn_lock, LK_RELEASE)
+
 extern usb_handle_req_t *usb_temp_get_desc_p;
 extern usb_temp_setup_by_index_t *usb_temp_setup_by_index_p;
 extern usb_temp_unsetup_t *usb_temp_unsetup_p;
--- a/sys/bus/u4b/usb_dev.c
+++ b/sys/bus/u4b/usb_dev.c
@@ -1707,7 +1707,9 @@
 		case USB_QUIRK_NAME_GET:
 		case USB_DEV_QUIRK_ADD:
 		case USB_DEV_QUIRK_REMOVE:
+			USB_DYN_RLOCK();
 			err = usb_quirk_ioctl_p(cmd, data, fflag, td);
+			USB_DYN_RUNLOCK();
 			break;
 		case USB_GET_TEMPLATE:
 			*(int *)data = usb_template;
--- a/sys/bus/u4b/usb_device.c
+++ b/sys/bus/u4b/usb_device.c
@@ -1724,7 +1724,9 @@
 		udev->curr_config_index = USB_UNCONFIG_INDEX;
 
 		/* Setup USB descriptors */
+		USB_DYN_RLOCK();
 		err = (usb_temp_setup_by_index_p) (udev, usb_template);
+		USB_DYN_RUNLOCK();
 		if (err) {
 			DPRINTFN(0, "setting up USB template failed maybe the USB "
 			    "template module has not been loaded\n");
@@ -2183,7 +2185,9 @@
 	usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX);
 
 	/* template unsetup, if any */
+	USB_DYN_RLOCK();
 	(usb_temp_unsetup_p) (udev);
+	USB_DYN_RUNLOCK();
 
 	/* 
 	 * Make sure that our clear-stall messages are not queued
@@ -2611,7 +2615,9 @@
 
 	/* search global quirk table, if any */
 
+	USB_DYN_RLOCK();
 	found = (usb_test_quirk_p) (&uaa->info, quirk);
+	USB_DYN_RUNLOCK();
 
 	return (found);
 }
--- a/sys/bus/u4b/usb_request.c
+++ b/sys/bus/u4b/usb_request.c
@@ -472,6 +472,7 @@
 	 */
 	usbd_sr_unlock(udev);
 
+	USB_DYN_RLOCK();
 	hr_func = usbd_get_hr_func(udev);
 
 	if (hr_func != NULL) {
@@ -485,6 +486,7 @@
 				DPRINTFN(1, "The handle request function "
 				    "does not support writing data!\n");
 				err = USB_ERR_INVAL;
+				USB_DYN_RUNLOCK();
 				goto done;
 			}
 		}
@@ -495,6 +497,8 @@
 		err = (hr_func) (udev, req, &desc, &temp);
 		USB_BUS_UNLOCK(udev->bus);
 
+		USB_DYN_RUNLOCK();
+
 		if (err)
 			goto done;
 
@@ -1236,6 +1240,7 @@
 	ptr = NULL;
 	len = 0;
 
+	USB_DYN_RLOCK();
 	hr_func = usbd_get_hr_func(udev);
 
 	if (hr_func == NULL)
@@ -1245,6 +1250,7 @@
 		err = (hr_func) (udev, &req, &ptr, &len);
 		USB_BUS_UNLOCK(udev->bus);
 	}
+	USB_DYN_RUNLOCK();
 
 	if (err)
 		ptr = NULL;