DragonFlyBSD Kernel Audit
DF-2804 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-2804",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "leak",
  "confidence": "certain",
  "verdict": "Reproduced on the stock INVARIANTS guest: a root reader that initiates the udev queue (UDEVPROP getdevs) and closes /dev/udev without reading leaks every undelivered event dictionary permanently. 30k unprivileged pty cycles left 58.8K prop-dict allocations (~24 MB across propdict/prop_dictionary/propstng/prop_string) after close, zero reclaim after 10s idle, exactly doubled after a second cycle (117K / 22.0M + 20.2M), while the identical churn drained by an active reader leaked nothing (control flat). Root cause: the event's single ev_dict reference (sys/kern/kern_udev.c:508-512) is released only on the read path (:571); the close path reaps events with a bare objcache_put and no prop_object_release (:534-545, :721-726) and abandons events beyond the marker when the last reader leaves. Repeatable without limit -> kernel memory exhaustion; root/wheel gated (same /dev/udev 0600 gate as DF-0055), event source unprivileged.",
  "exploit_chain": "",
  "evidence": [
    "run.log: S0 propdict 333 (62.4K) -> S1 58.8K (11.0M) after close-without-read cycle 1; S2 identical after 10s idle (permanent)",
    "run.log: S3 propdict 117K (22.0M) after cycle 2 (~2x monotone accumulation)",
    "run.log: control S4 propdict 117K (22.0M) flat after same churn with an active draining reader ('read 60000 events') - churn+drain does not leak",
    "run.log: udev malloc domain returns to ~baseline after close (event structs reaped by objcache) while propdict stays elevated - proves objcache_put without prop_object_release",
    "harness.c stall mode (initiate, sleep, close without reading), churn.c (posix_openpt loop as unprivileged user)"
  ],
  "kernel_refs": [
    "sys/kern/kern_udev.c:508-512",
    "sys/kern/kern_udev.c:571",
    "sys/kern/kern_udev.c:534-545",
    "sys/kern/kern_udev.c:709-744",
    "sys/kern/kern_udev.c:721-726",
    "sys/vfs/devfs/devfs_core.c:1428",
    "sys/vfs/devfs/devfs_core.c:1451"
  ],
  "poc_changes": "Harness written from scratch (no seed); same bring-up fixes as DF-2803 (sigaction instead of signal for the read-mode alarm; churn arg parsing).",
  "attempts": 3,
  "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": 95,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -o harness harness.c -lprop && cc -O2 -Wall -o churn churn.c",
  "run_cmd": "sh /tmp/df2804_run.sh",
  "code_hash": "181b484caf4890bb7d42d37a80de710ec7ec40003203d0999f4a6364a607abdc",
  "notes": "Guest reset (with-src) after verification. ~400B leaked per undelivered event (~24MB per 4-second cycle). Marker protocol: reader markers are softc-embedded udev_event_kernel entries with ev_dict == NULL (skipped by the reaper predicate), so the leak set is exactly the events no reader externalized. The fix.diff release-ownership change also neutralizes DF-0055's multi-reader UAF (single-sited release in the reaper) and drains the full queue when udev_initiated_count hits 0.",
  "recommended_fix": "Move the single prop_object_release(ev->ev.ev_dict) from udev_event_externalize() into udev_clean_events_locked() (release-and-NULL before objcache_put), and when udev_initiated_count == 0 drain the entire remaining queue so undelivered dictionaries are released on last-reader close.",
  "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 (release ownership single-sited in the reaper + full drain on last-reader close; also structurally resolves DF-0055). Kernel rebuild validation not performed: leak-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"
}