{
  "finding_id": "DF-5305",
  "status": "reproduced",
  "reproduced": true,
  "impact": "leak",
  "confidence": "certain",
  "verdict": "Reproduced on the QEMU guest with a KLD harness that compiles the real uvc driver sources and drives the real uvc_drv_parse_ctrl()/uvc_drv_parse_data()/uvc_drv_enum_v4l2_fmt() with a crafted 72-byte config blob. strlcpy() at uvc_drv.c:1888 walks off the non-NUL-terminated 16-byte bGuidFmt, crosses the kmalloc()ed cdesc buffer and copies 36 bytes of adjacent kernel heap into fmt->name in each of 3 runs (groom markers 0x43/0x45/0x47 land exactly as predicted). The real uvc_drv_enum_v4l2_fmt() (uvc_drv.c:833) then places leaked bytes into the VIDIOC_ENUM_FMT description field, and the cdev is 0666 (uvc_v4l2.c:783), so any local user can read them. Read primitive only (no write), severity Medium per the audit rubric (limited kernel-memory info leak; evil-USB precondition within the declared attacker model).",
  "exploit_chain": "crafted USB webcam descriptor (VS_FORMAT_FRAME_BASED with unknown non-NUL GUID at the end of the config blob) -> strlcpy at uvc_drv.c:1888 runs past the wTotalLength-sized cdesc allocation -> up to 63 bytes of adjacent kernel heap copied into fmt->name[64] -> unprivileged VIDIOC_ENUM_FMT on the 0666 /dev/videoN returns up to 31 leaked bytes per query (uvc_drv.c:833)",
  "evidence": "run.log (3 baseline rounds: name strlen=63 ... OOB READ PAST ALLOCATION CONFIRMED: 36 bytes, description ends with groom-marker bytes), leak_sample.txt (hexdumps), run_patched.log/fix.log (post-fix strlen=16, no marker bytes), VERDICT.md, panic-free guest",
  "kernel_refs": [
    "sys/bus/u4b/uvc/uvc_drv.c:1888",
    "sys/bus/u4b/uvc/uvc_drv.c:833",
    "sys/bus/u4b/uvc/uvc_drv.h:350",
    "sys/bus/u4b/uvc/uvc_v4l2.c:783",
    "sys/bus/u4b/usb_request.c:1366"
  ],
  "poc_changes": "No finding seed existed; harness written from scratch. It #includes the unmodified guest /usr/src uvc sources (DRIVER_MODULE/MODULE_DEPEND/MODULE_VERSION neutered) and feeds a fake usb_device (cdesc+ifaces) so the real static parse functions run against crafted descriptors at load time.",
  "attempts": 4,
  "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": 2100,
  "guest_dirty": 0,
  "build_cmd": "cp uvcharness.c Makefile /root/build/ && cd /root/build && make",
  "run_cmd": "kldload /root/build/uvcharness.ko; dmesg | tail -40",
  "code_hash": "9425426536799947ba46b93c33c288a70afeb9064800e79373dab40ec681d9cc",
  "notes": "Distinct from DF-2350 (frame-interval parse OOB read): different descriptor subtype, different sink and disclosure channel. First run with a 68-byte blob (run.2.log) also crossed the buffer end but the first OOB byte was NUL; shrinking the logical end to a 72-byte exact kmalloc zone with an all-non-NUL tail made the disclosure deterministic. Guest reset to clean with-src snapshot after verification.",
  "recommended_fix": "Replace the strlcpy() of bGuidFmt with a bounded memcpy() of exactly sizeof(bGuidFmt) bytes plus an explicit NUL terminator (see fix.diff).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff applied to the guest /usr/src copy; harness rebuilt against the patched source and re-run 3x: fmt->name strlen=16 (exactly the 16 GUID bytes + NUL), no OOB read, ENUM_FMT description contains only GUID bytes. Baseline (strlen=63, 36 leaked bytes) vs patched (strlen=16, none) recorded in run.log vs run_patched.log/fix.log.",
  "fix_evidence": "run_patched.log, fix.log, leak_sample.txt (post-fix section)"
}