⬢ DragonFlyBSD Kernel Audit
DF-5367 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/u4b/serial/ufoma.c
+++ b/sys/bus/u4b/serial/ufoma.c
@@ -184,6 +184,7 @@
 	uint8_t	sc_msr;
 	uint8_t	sc_modetoactivate;
 	uint8_t	sc_currentmode;
+	uint8_t	sc_dying;
 	uint8_t	sc_name[16];
 };
 
@@ -477,6 +478,16 @@
 {
 	struct ufoma_softc *sc = device_get_softc(dev);
 
+	/*
+	 * Wake any cfg callback blocked waiting for a mode-switch ACK so
+	 * that the usb_proc_drain() inside ucom_detach() below is not
+	 * stalled for the full ACK timeout by a dead/unplugged device.
+	 */
+	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
+	sc->sc_dying = 1;
+	cv_broadcast(&sc->sc_cv);
+	lockmgr(&sc->sc_lock, LK_RELEASE);
+
 	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
 	usbd_transfer_unsetup(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX);
 	usbd_transfer_unsetup(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX);
@@ -509,11 +520,31 @@
 	return (desc);
 }
 
+/*
+ * Wait for the device ACK, at most "timo" seconds in total, but in
+ * one-second slices so that a detach (sc_dying) aborts the wait
+ * immediately.  Without this, ucom_detach() -> usb_proc_drain() stalls
+ * the USB hub event thread for the full UFOMA_MAX_TIMEOUT whenever the
+ * device is removed while a mode-switch ACK is pending (which, with an
+ * unresponsive or malicious device, is every open/close).
+ */
+static void
+ufoma_cfg_wait_ack(struct ufoma_softc *sc, int timo)
+{
+	int error = EWOULDBLOCK;
+
+	while ((timo-- > 0) && !sc->sc_dying) {
+		error = cv_timedwait(&sc->sc_cv, &sc->sc_lock, hz);
+		if (error == 0)
+			return;			/* ACK received */
+	}
+	DPRINTF("no or aborted response\n");
+}
+
 static void
 ufoma_cfg_link_state(struct ufoma_softc *sc)
 {
 	struct usb_device_request req;
-	int32_t error;
 
 	req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
 	req.bRequest = UMCPC_SET_LINK;
@@ -524,18 +555,13 @@
 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
 	    &req, sc->sc_modetable, 0, 1000);
 
-	error = cv_timedwait(&sc->sc_cv, &sc->sc_lock, hz);
-
-	if (error) {
-		DPRINTF("NO response\n");
-	}
+	ufoma_cfg_wait_ack(sc, 1);
 }
 
 static void
 ufoma_cfg_activate_state(struct ufoma_softc *sc, uint16_t state)
 {
 	struct usb_device_request req;
-	int32_t error;
 
 	req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
 	req.bRequest = UMCPC_ACTIVATE_MODE;
@@ -546,11 +572,7 @@
 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
 	    &req, NULL, 0, 1000);
 
-	error = cv_timedwait(&sc->sc_cv, &sc->sc_lock,
-	    (UFOMA_MAX_TIMEOUT * hz));
-	if (error) {
-		DPRINTF("No response\n");
-	}
+	ufoma_cfg_wait_ack(sc, UFOMA_MAX_TIMEOUT);
 }
 
 static void