{
  "finding_id": "DF-2803",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "Reproduced on the stock INVARIANTS guest: one initiated-but-stalled root reader of /dev/udev plus one unprivileged pty-churn process (user maxx, /dev/ptmx 0666) grew the kernel udev event queue to 240,136 live events (~90 MB across propdict/prop_dictionary/propstng/udev malloc domains) in 20 seconds, linearly and without bound (udev_event_insert at sys/kern/kern_udev.c:500-532 increments udev_evqlen with no cap and no backpressure; udev_clean_events_locked reaps only up to the oldest reader marker). Kernel memory exhaustion DoS; reader side gated by /dev/udev 0600 root:wheel (same gate as DF-0055), producer side fully unprivileged.",
  "exploit_chain": "",
  "evidence": [
    "run.log: baseline propdict 288 in-use -> 72.4K (t+6s) -> 167K (t+14s) -> 226K (t+19s) while reader stalled; udev domain 139 -> 226K event structs",
    "run.log: 'churn: 120068 pty cycles in 20s' as user maxx",
    "run.log: 'harness: backlog drained: 240136 events (185144856 bytes)'",
    "run.log: post-drain sample returns to baseline (propdict 333) - growth was queued events, not a leak",
    "harness.c stallcount mode (initiate via UDEVPROP getdevs, stall 30s, then non-blocking drain-count), churn.c (posix_openpt loop)"
  ],
  "kernel_refs": [
    "sys/kern/kern_udev.c:500-532",
    "sys/kern/kern_udev.c:517",
    "sys/kern/kern_udev.c:534-545",
    "sys/kern/kern_udev.c:826-830",
    "sys/kern/kern_udev.c:973-979",
    "sys/kern/kern_udev.c:1039-1041",
    "sys/vfs/devfs/devfs_core.c:1428",
    "sys/vfs/devfs/devfs_core.c:1451",
    "sys/kern/tty_pty.c:176"
  ],
  "poc_changes": "Harness written from scratch for this finding (no seed). Fixed two of my own bugs during bring-up: BSD signal() sets SA_RESTART so alarm could not interrupt the blocking /dev/udev read (switched to sigaction with sa_flags=0), and churn arg parsing accepted only '-n N'. Added stallcount mode (stall then non-blocking drain) to quantify the backlog without triggering the DF-2804 leak in the same run.",
  "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": 42,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -o harness harness.c -lprop && cc -O2 -Wall -o churn churn.c",
  "run_cmd": "sh /tmp/df2803_run.sh",
  "code_hash": "181b484caf4890bb7d42d37a80de710ec7ec40003203d0999f4a6364a607abdc",
  "notes": "Guest reset (with-src) after verification. Event production rate measured at ~12k events/s per unprivileged churn process (~370B retained per event in propdict + ~190B dict copy). Even a diligently-reading consumer measured only ~10k events/s drain, so any reader lag accumulates. devd holds only /dev/devctl on this system (verified via fstat), so udev queueing starts only when something initiates /dev/udev.",
  "recommended_fix": "Cap the queue: check udev_evqlen >= UDEV_EVQ_MAX under udev_lk in udev_event_insert() and drop (not queue) excess events, waking UDEVWAIT/kqueue consumers so the drop is observable.",
  "fix_status": "not_testable",
  "fix_kernel_uname": "",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff authored from line-accurate root cause (see pack). Kernel rebuild validation not performed: DoS-class finding; the mandatory fix-build cycle applies to reproduced memory-corruption findings. Guest was reset to the clean-source snapshot after evidence collection.",
  "fix_evidence": "fix.diff"
}
