⬢ DragonFlyBSD Kernel Audit
DF-2352 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-2352",
  "status": "reproduced",
  "verdict": "REPRODUCED \u2014 deterministic divide-by-zero kernel panic triggered by an UNPRIVILEGED local user through the full production ioctl dispatch. Kernel log first proves the parse half: production uvc_drv_parse_data left a bFrameIntervalType==0 frames interval slots at min=0 max=0 step=0 although the descriptor carried 2000000/10000000/3333 (the driver never reads dFrameInterval[] for continuous frames, uvc_drv.c:1922). Then uid-1001 ioctl(VIDIOC_S_PARM) on /dev/video0 (0666, production uvc_v4l2_reg) executed uvc_v4l2_ioctl -> uvc_drv_set_streampar -> uvc_drv_try_frame_interval continuous branch: Fatal trap 18 (integer divide fault), stopped at uvc_drv_try_frame_interval.isra.2+0x60: divl %r8d,%eax \u2014 the /step at uvc_drv.c:504. The division precedes any USB traffic (uvc_drv.c:1386-1387), so a single unprivileged ioctl suffices. FIX VALIDATED: patched parser reads min/max/step (slots printed 2000000/10000000/3333), step!=0 guard present; full unprivileged S_PARM path returns 0 with no panic.",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "exploit_chain": "Malicious webcam descriptor with continuous-interval frame -> attach parse leaves step==0 (M_ZERO slots) -> cur_frm = that frame via production uvc_drv_init_cur_fmt_frm (device answers GET_DEF/GET_CUR: attacker-controlled) -> any local user opens /dev/video0 (0666) -> VIDIOC_S_PARM -> production dispatch divides by step==0 -> x86 #DE -> kernel panic. Trigger needs no privilege and no device interaction at trigger time.",
  "evidence": "panic.txt: slots after production uvc_drv_parse_data: min=0 max=0 step=0 + (descriptor carried min=2000000 max=10000000 step=3333), uvc_v4l2_set_streamparm, Setting frame interval to 1/30 (333333)., Fatal trap 18: integer divide fault while in kernel mode, Stopped at uvc_drv_try_frame_interval.isra.2+0x60: divl %r8d,%eax. fix_run.log: step slot=3333, returned 3333200 -- NO DIV-BY-ZERO, trigger_sparm: ioctl returned 0 errno=0.",
  "kernel_refs": [
    "sys/bus/u4b/uvc/uvc_drv.c:1917",
    "sys/bus/u4b/uvc/uvc_drv.c:1922",
    "sys/bus/u4b/uvc/uvc_drv.c:500",
    "sys/bus/u4b/uvc/uvc_drv.c:504",
    "sys/bus/u4b/uvc/uvc_drv.c:1386",
    "sys/bus/u4b/uvc/uvc_v4l2.c:503",
    "sys/bus/u4b/uvc/uvc_v4l2.c:783"
  ],
  "poc_changes": "No PoC seed existed. Built from scratch. trigger_sparm.c mirrors the kernels own sys/contrib/v4l/videodev2.h ioctl encoding \u2014 DragonFlys VIDIOC_S_PARM is _IOWR(V, 22, ...) (NOT Linuxs 75); structs mirrored byte-exact. The rig needed the descriptor-blob corrections noted in DF-2351 (shared blob builder).",
  "attempts": 1,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "runtime_sec": 300,
  "guest_dirty": 0,
  "build_cmd": "./build.sh  (setup.sh: uvc_rig.ko via make in guest rig dir; cc -O2 -o /tmp/trigger_sparm trigger_sparm.c)",
  "run_cmd": "kldload uvc_rig.ko; sysctl hw.usb.uvcrig=2352 (arms /dev/video0 0666); run_user /tmp/trigger_sparm   -> panic (see run.sh)",
  "code_hash": "34e72cb25322fdb5c2a8a81f5fb7bcb858b6a435ba38e818bbf681f853b9bc56",
  "notes": "Evidence class: production-handler KLD dispatch \u2014 the entire path from the 0666 cdev through uvc_v4l2_ioctl to the faulting division is unmodified in-tree code; only the USB transport and the devices attach-time answers are synthesized (no emulated UVC webcam available for QEMU). Post-fix, the follow-on probe request against the fake transport fails gracefully (USB_ERR_NOT_CONFIGURED) \u2014 a rig artifact; a production device answers it. Guest left up and clean.",
  "recommended_fix": "Read dFrameInterval min/max/step DWORDs for bFrameIntervalType==0 frames in uvc_drv_parse_data (when bLength permits, else 1/1/1) and guard step==0 (plus min/max clamps) in uvc_drv_try_frame_interval",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "With fix.diff applied (guest /usr/src, module rebuilt -Werror clean): direct call prints step slot=3333 and uvc_drv_try_frame_interval returns 3333200 without #DE; the full unprivileged VIDIOC_S_PARM path returns 0 and the guest stays up (probe fails gracefully against the rigs fake transport).",
  "fix_evidence": "fix_run.log: RIG2352direct: step slot=3333 ... returned 3333200 -- NO DIV-BY-ZERO; slots after production uvc_drv_parse_data: min=2000000 max=10000000 step=3333; trigger_sparm: ioctl returned 0 errno=0; fix_build.log: clean patched build."
}