⬢ DragonFlyBSD Kernel Audit
DF-3941 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/video/cxm/cxm.h	2026-09-17 02:40:03.862476021 +0000
+++ b/sys/dev/video/cxm/cxm.h
@@ -37,6 +37,7 @@
  */
 
 #include <sys/event.h>
+#include <sys/lock.h>
 
 #include "pcidevs.h"
 
@@ -255,6 +256,7 @@
 	device_t	dev;		/* bus attachment */
 	cdev_t		cxm_dev_t;	/* control device */
 	int		is_opened;
+	struct lock	lock;		/* serializes ring/capture state machine */
 	int		mpeg;
 
 	int		encoding;
--- a/sys/dev/video/cxm/cxm.c	2026-09-17 02:40:03.858476074 +0000
+++ b/sys/dev/video/cxm/cxm.c
@@ -1116,9 +1116,11 @@
 				 CXM_FW_CMD_BEGIN_CAPTURE, parameters, 2) != 0)
 		return -1;
 
+	lockmgr(&sc->lock, LK_EXCLUSIVE);
 	sc->enc_pool.offset = 0;
 	sc->enc_pool.read = 0;
 	sc->enc_pool.write = 0;
+	lockmgr(&sc->lock, LK_RELEASE);
 
 	sc->encoding_eos = 0;
 
@@ -1642,6 +1644,7 @@
 	unit = device_get_unit(dev);
 
 	sc->dev = dev;
+	lockinit(&sc->lock, "cxmlock", 0, 0);
 	sc->type = cxm_iTVC15_type;
 
 	switch(pci_get_device(dev)) {
@@ -1888,6 +1891,7 @@
 	return 0;
 
 fail:
+	lockuninit(&sc->lock);
 	if (sc->enc_sg.baddr)
 		bus_dmamap_unload(sc->enc_sg.dmat, sc->enc_sg.dmamap);
 	if (sc->enc_sg.vaddr)
@@ -1911,6 +1915,7 @@
 
 	if (sc->parent_dmat)
 		bus_dma_tag_destroy(sc->parent_dmat);
+	lockuninit(&sc->lock);
 
 	/*
 	 * Detach the I2C bus.
@@ -2190,9 +2195,11 @@
 	if (cxm_stop_encoder(sc) < 0)
 		return ENXIO;
 
+	lockmgr(&sc->lock, LK_EXCLUSIVE);
 	sc->enc_pool.offset = 0;
 	sc->enc_pool.read = 0;
 	sc->enc_pool.write = 0;
+	lockmgr(&sc->lock, LK_RELEASE);
 
 	sc->enc_proc = NULL;
 	sc->enc_signal = 0;
@@ -2259,6 +2266,15 @@
 		buffers_available += CXM_SG_BUFFERS;
 	crit_exit();
 
+	/*
+	 * Serialize ring consumption: enc_pool.offset/read/write are one
+	 * state machine shared by all readers of this device and by the
+	 * capture start/stop paths.  Without the lock, racing readers
+	 * (single fd shared across fork()) can pair a saved offset with a
+	 * different buffer and underflow `size - offset` below.
+	 */
+	lockmgr(&sc->lock, LK_EXCLUSIVE);
+
 	offset = sc->enc_pool.offset;
 
 	for (buffers_read = 0, i = sc->enc_pool.read;
@@ -2267,6 +2283,10 @@
 
 		current = cxm_encoder_fixup_byte_order (sc, i, offset);
 
+		/* Defensive: never let a stale offset exceed this buffer */
+		if (offset > sc->enc_pool.bufs[i].size)
+			offset = 0;
+
 		nbytes = sc->enc_pool.bufs[current].size - offset;
 
 		/* Don't transfer more than requested */
@@ -2276,7 +2296,7 @@
 		error = uiomove(sc->enc_pool.bufs[current].vaddr + offset,
 				nbytes, ap->a_uio);
 		if (error)
-			return error;
+			goto out;
 
 		offset += nbytes;
 
@@ -2295,6 +2315,9 @@
 			    % CXM_SG_BUFFERS;
 	crit_exit();
 
+out:
+	lockmgr(&sc->lock, LK_RELEASE);
+
 	return 0;
 }