⬢ DragonFlyBSD Kernel Audit
DF-4484 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4484",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Reproduced on the DragonFly guest with a crafted SSDT: a firmware 0x81 REPROBE notify followed by a failing _BCL re-evaluation leaves vo->vo_levels dangling (vo_get_brightness_levels returns 0 without writing *levelp after vo_bind freed the old array, acpi_video.c:597-600 + 984-1008). sysctl -w hw.acpi.video.lcd0.fullpower=-1 then reads vo_levels[0] out of freed kernel heap (acpi_video.c:834-837) and discloses it back to userspace (-1920985560 / -1925046824 across boots, not any legal level); kldunload acpi_video double-frees the dangling pointer in acpi_video_vo_destroy (acpi_video.c:630) causing panic: memory chunk 0xfffff8008d801580 is already free! with a trace through acpi_video_vo_destroy. No unpriv->root chain exists (sysctl write is root-only, notify is firmware-only), so impact ceiling is kernel heap corruption/DoS + root-visible 4-byte freed-heap disclosure.",
  "exploit_chain": "crafted SSDT (boot privilege) defines vgapci _DOD/_DOS + output devices; 2nd sysctl read of lcd0.active fires Notify(VGT0,0x81); reprobe rebinds, _BCL#2 fails, vo_levels freed-but-not-NULLed; root writes -1 to fullpower -> kernel reads freed chunk -> value stored and read back (UAF disclosure); kldunload -> second free of same chunk -> kernel panic (double free).",
  "evidence": "run.log steps 1-4 (levels 100 55 90 75 55 25 -> empty; fullpower 100 -> -1920985560), panic.txt (panic trace through acpi_video_vo_destroy+0x6a), dmesg evaluation of ..._BCL makes no sense, ssdt_dfa.asl choreography",
  "kernel_refs": [
    "sys/dev/acpica/acpi_video/acpi_video.c:597",
    "sys/dev/acpica/acpi_video/acpi_video.c:600",
    "sys/dev/acpica/acpi_video/acpi_video.c:984",
    "sys/dev/acpica/acpi_video/acpi_video.c:1002",
    "sys/dev/acpica/acpi_video/acpi_video.c:834",
    "sys/dev/acpica/acpi_video/acpi_video.c:627",
    "sys/dev/acpica/acpi_video/acpi_video.c:630"
  ],
  "poc_changes": "Wrote the SSDT trigger from scratch (seed had none): VGT0/_ADR 0x00020000 to bind vgapci0, _DOD/_DOS for probe, stateful _BCL (valid once, 1-entry package afterwards), _DCS firing Notify(VGT0,0x81) on 2nd eval; iasl quirks fixed (External(\\_SB.PCI0), literal 2, package-typed failure return).",
  "attempts": 3,
  "guest_uname": "DragonFly dfbsd 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": 900,
  "guest_dirty": 1,
  "build_cmd": "iasl ssdt_dfa.asl (on guest); QEMU_EXTRA=\"-acpitable file=ssdt_dfa.aml\" dfbsd-qemu/vm.sh up",
  "run_cmd": "vm.sh run_root sh /tmp/run_df4484.sh (then kldunload acpi_video for the double-free)",
  "code_hash": "0d33c64d9b47dff7",
  "notes": "Fix validated: fix.diff NULLs vo_levels/numlevels in vo_bind + vo_matched flag for reprobe; patched kernel: levels/fullpower write return ENODEV, kldunload clean, no panic (fix_run.log).",
  "recommended_fix": "NULL out vo->vo_levels (and vo->vo_numlevels) after AcpiOsFree in acpi_video_vo_bind; track reprobe matching with a flag instead of NULLing vo->handle",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Patched kernel (nativekernel build with fix.diff): same SSDT choreography -> lcd0.levels ENODEV, fullpower=-1 write ENODEV with readback unchanged (100), kldunload acpi_video returns 0 with no panic; baseline double-free panic gone.",
  "fix_evidence": "fix.diff; fix_run.log (patched choreography, clean unload); fix_build.log (kernel build tail)",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64"
}