⬢ DragonFlyBSD Kernel Audit
DF-5149 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5149",
  "status": "not_reproduced",
  "reproduced": false,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Structural teardown gap confirmed in source; the runtime half cannot exist on this guest. ppsattach() discards the cdev from make_dev() (sys/dev/misc/pps/pps.c:103-104) and the method table has no device_detach (pps.c:190-197), so /dev/ppsN and the ppbus child are kernel-lifetime objects and a module-built pps can never unload (missing DETACH -> ENXIO -> unload aborts before any free) -- the same structurally-non-UAF-today conclusion as DF-5006/DF-5074/DF-5091, with the added twist that an open pps owns the ppbus and a registered ppsintr (pps.c:118-133), making any future naive detach an immediate interrupt-context UAF. On the guest: no pps/ppbus drivers registered anywhere (kldstat -v), no pps.ko in /boot/kernel (dev/misc/pps/pps.c is optional pps, sys/conf/files:1216; X86_64_GENERIC has only ulpt), and QEMU exposes no ISA parallel port -- kldload ppbus attached nothing and unloaded cleanly, so /dev/pps* can never appear on this snapshot (the audit conventions lpt/ppi/pps QEMU-live note is stale). Low severity; cheap fix belongs in-tree.",
  "exploit_chain": "",
  "evidence": "run.log (decisive: kldstat -v grep for pps/ppbus empty; ls /boot/kernel | grep ^pp shows only ppbus.ko; grep on X86_64_GENERIC matches only ulpt:308; kldload ppbus adds no dmesg attach lines and no /dev/pps*|/dev/lpt*|/dev/ppi*; kldunload ppbus rc=0; guest stays up). VERDICT.md for the full path:line trace plus the pass-2 killed candidates (ioctl size forging, intr teardown, SETPARAMS cap masking, offset misuse, FETCH timeout, NULL-softc-by-minor); fix.diff for the proposed detach (git apply --check clean, not applied).",
  "kernel_refs": [
    "sys/dev/misc/pps/pps.c:103",
    "sys/dev/misc/pps/pps.c:118",
    "sys/dev/misc/pps/pps.c:159",
    "sys/dev/misc/pps/pps.c:190",
    "sys/bus/ppbus/ppbconf.c:514",
    "sys/conf/files:1216",
    "sys/kern/kern_clock.c:1640"
  ],
  "poc_changes": "None -- structural finding; trigger is a plain /bin/sh runtime demonstration (driver/code/node absence + ppbus load-unload round-trip), no seed code to repair.",
  "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": 25,
  "guest_dirty": 0,
  "build_cmd": "none (structural kernel-source finding; ./build.sh only syntax-checks trigger.sh)",
  "run_cmd": "findings/poc/DF-5149/run.sh  (vm.sh run_root executes trigger.sh as root on the guest)",
  "code_hash": "2e4b89cb879e25556f506c4366a123543aaa3c409527e4ec09d3b899f0188c00",
  "notes": "Guest left up and clean (ppbus.ko load-tested then unloaded rc=0; all other probes read-only; no panic). Family: DF-5006 (acpi_cpu), DF-5074 (acpi_acad), DF-5091 (acpi_button) -- this is the pps instance. Known pass-1 findings for this file NOT re-reported: DF-2148 (KCBIND priv), DF-2149 (boolean pps_open); the pass-2 aggravation that ppsclose writes ppb_wdtr/ppb_wctr unconditionally and ignores ppb_release_bus()s return (pps.c:149-153) is recorded in VERDICT.md as a DF-2149 consequence, not re-filed. Conventions pps QEMU-live is stale for this snapshot -- noted for the orchestrator.",
  "recommended_fix": "Store the make_dev() cdev in the softc and add ppsdetach() that returns EBUSY while pps_open and otherwise destroy_dev()s the node -- see fix.diff.",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff authored and git apply --check clean against sys/ but not build-validated: the finding produced no runtime fault on the baseline guest (the pps driver is not compiled into any kernel or module here and no parallel port exists to attach a ppbus), so there is no bad behavior to A/B. Fix validation is mandatory only when a memory-corruption finding reproduces.",
  "fix_evidence": "findings/poc/DF-5149/fix.diff (proposed, unapplied; stores pps_cdev in struct pps_data, adds ppsdetach() with the pps_open EBUSY guard and destroy_dev(), registers DEVMETHOD(device_detach, ppsdetach))"
}