DF-2352 / 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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | diff -ruN a/sys/bus/u4b/uvc/uvc_drv.c b/sys/bus/u4b/uvc/uvc_drv.c --- a/sys/bus/u4b/uvc/uvc_drv.c 2026-10-14 18:40:54.648294861 +0000 +++ b/sys/bus/u4b/uvc/uvc_drv.c 2026-10-14 18:41:11.748074091 +0000 @@ -499,11 +499,15 @@ } else { const uint32_t min = frame->interval[0].val; const uint32_t max = frame->interval[1].val; - const uint32_t step = frame->interval[2].val; + uint32_t step = frame->interval[2].val; + if (step == 0) + step = 1; interval = min + (interval - min + step/2) / step * step; if (interval > max) interval = max; + if (interval < min) + interval = min; } return interval; @@ -1460,6 +1464,10 @@ break; } } + if (fmt == NULL) { + kprintf("No format desc found.\n"); + return EINVAL; + } if (fmt->nfrm == 0) { kprintf("No frame desc found for default format.\n"); return EINVAL; @@ -1532,6 +1540,10 @@ } } + if (fmt == NULL) { + DPRINTF("No format desc found.\n"); + goto done; + } if (fmt->nfrm == 0) { DPRINTF("No frame desc found for default format.\n"); goto done; @@ -1919,8 +1931,28 @@ itv->val = interval ? interval : 1; itv++; } - if (frm->interval_type == 0) + if (frm->interval_type == 0) { + /* + * Continuous frame: dFrameInterval[] carries + * min/max/step; read them instead of leaving + * the (M_ZERO) slots at 0 -- step==0 later + * divides by zero in + * uvc_drv_try_frame_interval(). + */ + if (frmd->bLength >= 26 + 12) { + interval = UGETDW(frmd->dFrameInterval[0]); + itv->val = interval ? interval : 1; + interval = UGETDW(frmd->dFrameInterval[1]); + (itv + 1)->val = interval ? interval : 1; + interval = UGETDW(frmd->dFrameInterval[2]); + (itv + 2)->val = interval ? interval : 1; + } else { + itv->val = 1; + (itv + 1)->val = 1; + (itv + 2)->val = 1; + } itv += 3; + } fmt->nfrm++; frm++; break; @@ -1950,8 +1982,22 @@ itv->val = interval ? interval : 1; itv++; } - if (frm->interval_type == 0) + if (frm->interval_type == 0) { + /* same as above: read min/max/step */ + if (fbrmd->bLength >= 26 + 12) { + interval = UGETDW(fbrmd->dFrameInterval[0]); + itv->val = interval ? interval : 1; + interval = UGETDW(fbrmd->dFrameInterval[1]); + (itv + 1)->val = interval ? interval : 1; + interval = UGETDW(fbrmd->dFrameInterval[2]); + (itv + 2)->val = interval ? interval : 1; + } else { + itv->val = 1; + (itv + 1)->val = 1; + (itv + 2)->val = 1; + } itv += 3; + } fmt->nfrm++; frm++; break; @@ -2494,11 +2540,32 @@ uvc_drv_detach(device_t self) { struct uvc_softc *sc = device_get_softc(self); + int i; DPRINTF("%s-%d-%p\n", __func__, __LINE__, self); //delete sysfs //sysfs_video_del_device(sc->udev, sc->iface); + /* + * Destroy the /dev/videoX cdev FIRST and let outstanding users + * drain before freeing any memory. destroy_dev() is + * asynchronous (devfs_destroy_dev -> devfs_msg_send_dev) and + * open file descriptors keep dispatching ioctls into the driver + * until the devfs core processes the destroy; freeing + * sc->data/sc->video before that turns concurrent V4L2 ioctls + * (VIDIOC_ENUM_FMT etc., which read video->data->fmt without + * holding the mutex) into use-after-free reads. + */ + if (sc->video) { + /* entry close */ + uvc_v4l2_unreg(sc->video); + for (i = 0; i < 1000; i++) { + if (atomic_load_64(&sc->video->users) == 0) + break; + tsleep(sc->video, 0, "uvcdt", 1); + } + } + /* destroy video hard code only one */ uvc_drv_destroy_video(sc->video); @@ -2507,12 +2574,13 @@ uvc_drv_destroy_ctrl(sc->ctrl); if (sc->video) { - /* entry close */ - uvc_v4l2_unreg(sc->video); /* data free */ kfree(sc->video, M_UVC); sc->video = NULL; } + sc->data = NULL; + sc->ctrl = NULL; + DPRINTF("%s-%d-%p\n", __func__, __LINE__, self); return 0; } diff -ruN a/sys/bus/u4b/uvc/uvc_drv.h b/sys/bus/u4b/uvc/uvc_drv.h --- a/sys/bus/u4b/uvc/uvc_drv.h 2026-10-14 18:40:54.648294861 +0000 +++ b/sys/bus/u4b/uvc/uvc_drv.h 2026-10-14 18:41:11.752074039 +0000 @@ -659,10 +659,9 @@ struct usb_xfer *xfer[UVC_N_BULKTRANSFER]; /* data format */ - uint8_t nfmt; - uint8_t nfrm; - uint8_t nitv; - uint8_t unuse[3]; + uint16_t nfmt; + uint16_t nfrm; + uint16_t nitv; struct uvc_data_format *fmt; }; |