⬢ DragonFlyBSD Kernel Audit
DF-5436 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/u4b/serial/ulpt.c
+++ b/sys/bus/u4b/serial/ulpt.c
@@ -422,29 +422,35 @@ ulpt_open(struct usb_fifo *fifo, int fflags
 static int
 unlpt_open(struct usb_fifo *fifo, int fflags)
 {
 	struct ulpt_softc *sc = usb_fifo_softc(fifo);
+	int error = 0;
+
+	/* DF-5436: /dev/ulptN and /dev/unlptN are two independent cdevs
+	 * sharing one softc; open(2) on them is only serialized per vnode,
+	 * so the sc_fflags gate must itself be atomic. */
+	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
 
 	if (sc->sc_fflags & fflags) {
-		return (EBUSY);
+		error = EBUSY;
+		goto done;
 	}
 	if (fflags & FREAD) {
 		/* clear stall first */
-		lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
 		usbd_xfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_RD]);
-		lockmgr(&sc->sc_lock, LK_RELEASE);
 		if (usb_fifo_alloc_buffer(fifo,
 		    usbd_xfer_max_len(sc->sc_xfer[ULPT_BULK_DT_RD]),
 		    ULPT_IFQ_MAXLEN)) {
-			return (ENOMEM);
+			error = ENOMEM;
+			goto done;
 		}
 		/* set which FIFO is opened */
 		sc->sc_fifo_open[USB_FIFO_RX] = fifo;
 	}
 	if (fflags & FWRITE) {
 		/* clear stall first */
-		lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
 		usbd_xfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_WR]);
-		lockmgr(&sc->sc_lock, LK_RELEASE);
 		if (usb_fifo_alloc_buffer(fifo,
 		    usbd_xfer_max_len(sc->sc_xfer[ULPT_BULK_DT_WR]),
 		    ULPT_IFQ_MAXLEN)) {
-			return (ENOMEM);
+			error = ENOMEM;
+			goto done;
 		}
 		/* set which FIFO is opened */
 		sc->sc_fifo_open[USB_FIFO_TX] = fifo;
 	}
 	sc->sc_fflags |= fflags & (FREAD | FWRITE);
-	return (0);
+done:
+	lockmgr(&sc->sc_lock, LK_RELEASE);
+	return (error);
 }
 
 static void
 ulpt_close(struct usb_fifo *fifo, int fflags)
 {
 	struct ulpt_softc *sc = usb_fifo_softc(fifo);
 
+	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
 	sc->sc_fflags &= ~(fflags & (FREAD | FWRITE));
 
-	if (fflags & (FREAD | FWRITE)) {
+	if (fflags & FREAD) {
+		sc->sc_fifo_open[USB_FIFO_RX] = NULL;
+		usb_fifo_free_buffer(fifo);
+	}
+	if (fflags & FWRITE) {
+		sc->sc_fifo_open[USB_FIFO_TX] = NULL;
 		usb_fifo_free_buffer(fifo);
 	}
+	lockmgr(&sc->sc_lock, LK_RELEASE);
 }