⬢ DragonFlyBSD Kernel Audit
DF-4753 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/video/cxm/cxm.h
+++ b/sys/dev/video/cxm/cxm.h
@@ -37,6 +37,7 @@
  */
 
 #include <sys/event.h>
+#include <sys/lock.h>
 
 #include "pcidevs.h"
 
@@ -254,6 +255,7 @@
 
 	device_t	dev;		/* bus attachment */
 	cdev_t		cxm_dev_t;	/* control device */
+	struct lock	cxm_lock;	/* serializes cdev entry vs detach */
 	int		is_opened;
 	int		mpeg;
 
--- a/sys/dev/video/cxm/cxm.c
+++ b/sys/dev/video/cxm/cxm.c
@@ -1880,6 +1880,7 @@
 
 	sc->source = cxm_tuner_source;
 
+	lockinit(&sc->cxm_lock, "cxm", 0, 0);
 
 	/* make the device entries */
 	sc->cxm_dev_t = make_dev(&cxm_ops, unit,
@@ -1957,6 +1958,14 @@
 	dev_ops_remove_minor(&cxm_ops, /*0, */device_get_unit(dev));
 
 	/*
+	 * Drain any in-flight cdev operations (ioctls can sleep for
+	 * ~0.5 s in the audio/video lock waits) before tearing down
+	 * the softc they dereference.
+	 */
+	lockmgr(&sc->cxm_lock, LK_EXCLUSIVE);
+	lockmgr(&sc->cxm_lock, LK_RELEASE);
+
+	/*
 	 * Deallocate scatter / gather list and buffers.
 	 */
 	bus_dmamap_unload(sc->enc_sg.dmat, sc->enc_sg.dmamap);
@@ -2000,6 +2009,8 @@
 	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
 	bus_release_resource(dev, SYS_RES_MEMORY, CXM_RID, sc->mem_res);
 
+	lockuninit(&sc->cxm_lock);
+
 	return 0;
 }
 
@@ -2155,11 +2166,15 @@
 		return ENXIO;
 	}
 
-	if (sc->is_opened)
+	lockmgr(&sc->cxm_lock, LK_EXCLUSIVE);
+	if (sc->is_opened) {
+		lockmgr(&sc->cxm_lock, LK_RELEASE);
 		return EBUSY;
+	}
 
 	sc->is_opened = 1;
 	sc->mpeg = 1;
+	lockmgr(&sc->cxm_lock, LK_RELEASE);
 
 	/* Record that the device is now busy */
 	device_busy(devclass_get_device(cxm_devclass, unit));
@@ -2187,8 +2202,12 @@
 		return ENXIO;
 	}
 
-	if (cxm_stop_encoder(sc) < 0)
+	lockmgr(&sc->cxm_lock, LK_EXCLUSIVE);
+
+	if (cxm_stop_encoder(sc) < 0) {
+		lockmgr(&sc->cxm_lock, LK_RELEASE);
 		return ENXIO;
+	}
 
 	sc->enc_pool.offset = 0;
 	sc->enc_pool.read = 0;
@@ -2201,6 +2220,8 @@
 
 	sc->is_opened = 0;
 
+	lockmgr(&sc->cxm_lock, LK_RELEASE);
+
 	return 0;
 }
 
@@ -2305,7 +2326,33 @@
 static int
 cxm_ioctl(struct dev_ioctl_args *ap)
 {
-	cdev_t		dev = ap->a_head.a_dev;
+	int		unit;
+	struct cxm_softc *sc;
+	int		error;
+
+	unit = UNIT(minor(ap->a_head.a_dev));
+
+	/* Get the device data */
+	sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
+	if (sc == NULL) {
+		/* the device is no longer valid/functioning */
+		return ENXIO;
+	}
+
+	/*
+	 * Serialize against cxm_detach: the ioctl body may sleep
+	 * (audio/video lock waits) while dereferencing sc.
+	 */
+	lockmgr(&sc->cxm_lock, LK_EXCLUSIVE);
+	error = cxm_ioctl_locked(ap, sc);
+	lockmgr(&sc->cxm_lock, LK_RELEASE);
+
+	return error;
+}
+
+static int
+cxm_ioctl_locked(struct dev_ioctl_args *ap, struct cxm_softc *sc)
+{
 	int		brightness;
 	int		chroma_saturation;
 	int		contrast;
@@ -2313,24 +2360,13 @@
 	int		hue;
 	int		result;
 	int		status;
-	int		unit;
 	unsigned int	i;
 	unsigned int	sig;
 	unsigned long	freq;
-	struct cxm_softc *sc;
 	enum cxm_source	source;
 	struct bktr_capture_area *cap;
 	struct bktr_remote *remote;
 
-	unit = UNIT(minor(dev));
-
-	/* Get the device data */
-	sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
-	if (sc == NULL) {
-		/* the device is no longer valid/functioning */
-		return ENXIO;
-	}
-
 	switch (ap->a_cmd) {
 	case BT848_GAUDIO:
 		switch (cxm_msp_selected_source(sc)) {