DF-3941 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | --- 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; } |