⬢ DragonFlyBSD Kernel Audit
DF-4605 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4605",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "CONFIRMED by verbatim-source control-flow reproduction on the DragonFly guest (4/4 deterministic): the port-time #if 0 gutting of the event guard in amdgpu_atif_handler (sys/dev/drm/amd/amdgpu/amdgpu_acpi.c:374-385) leaves an if with an EMPTY body, so control falls through to atif->functions.sbios_requests (line 387) \u2014 a load at NULL+0x12 whenever adev->atif == NULL. The proof sed-extracts lines 374-385 and 387 from the guests live /usr/src kernel tree, compiles them with the byte-exact struct amdgpu_atif layout (functions.sbios_requests at offset 18, confirmed at runtime) and observes SIGSEGV at address 0x12 exactly as predicted; the fixed variant (same lines, #if 0 block replaced by return;) returns cleanly. adev->atif is NULL on every ATIF-less amdgpu system (amdgpu_acpi.c:751-753 goto out; also :755 kzalloc failure, :763 verify failure) while amdgpu_acpi_init installs the ACPI notify handler unconditionally (amdgpu_acpi.c:808-812) \u2014 so the first firmware ACPI_DEVICE_NOTIFY on the GPU node panics the kernel (unmapped page-0 read in ACPICA dispatch context). CAVEAT: the in-kernel end-to-end path could not be executed on this guest (no AMD GPU \u2014 virtio-vga only per env.txt \u2014 and amdgpu is not built into any guest kernel config); reproduction is at the verbatim-extraction control-flow level, which is deterministic and source-faithful. No escalation surface: fixed NULL+0x12 read, value never consumed \u2014 pure DoS. Distinct from pass-1 DF-1749 (freed-adev UAF post-detach), DF-1750 (parser OOB/type-confusion), DF-1751 (allocator mismatch).",
  "exploit_chain": "trigger chain (no privilege escalation \u2014 DoS only): amdgpu device probes on ATIF-less firmware (any discrete desktop AMD GPU) -> amdgpu_acpi_init installs AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, amdgpu_acpi_event, adev) with adev->atif still NULL (amdgpu_acpi.c:751-753 -> out: -> 808-812) -> firmware issues any device Notify on the GPU ACPI node (hotplug/dock/thermal) -> ACPICA dispatches amdgpu_acpi_event -> amdgpu_atif_handler(adev, type) -> gutted guard is a no-op -> kernel-mode read at vaddr 0x12 (page 0 unmapped) -> trap -> kernel panic.",
  "evidence": "run.log (decisive run: RESULT: SIGSEGV at address 0x12 + PROOF-OK: baseline faults at NULL+0x12; fixed variant returns cleanly); run.stress.log (3 further identical runs, 4/4 total); guard.inc / deref.inc (the sed-extracted kernel lines 374-385 / 387); generated_guard_test.c (final harness as compiled on the guest); extract_and_test.sh (the whole proof, runs on the guest); env.txt (guest has NO AMD GPU \u2014 virtio-vga 0x1111:1234 \u2014 and amdgpu not built: in-kernel path not runnable there); VERDICT.md section 2 (why two earlier harness iterations were wrongly clean: gcc-8 UB dead-code elision of a provable-NULL deref and of a side-effect-free if \u2014 both impossible in the real kernel build); fix.diff (git-apply --check clean against sys/).",
  "kernel_refs": [
    "sys/dev/drm/amd/amdgpu/amdgpu_acpi.c:374",
    "sys/dev/drm/amd/amdgpu/amdgpu_acpi.c:379",
    "sys/dev/drm/amd/amdgpu/amdgpu_acpi.c:387",
    "sys/dev/drm/amd/amdgpu/amdgpu_acpi.c:751",
    "sys/dev/drm/amd/amdgpu/amdgpu_acpi.c:808",
    "sys/dev/drm/amd/amdgpu/amdgpu_acpi.c:811",
    "sys/dev/drm/amd/amdgpu/amdgpu_kms.c:215",
    "sys/dev/drm/amd/amdgpu/amdgpu.h:1045"
  ],
  "poc_changes": "No seeded PoC existed (pass-2 finding). The proof was authored during verification; two iterations were silently gutted by gcc 8.3 (call deleted as UB when atif was provably NULL even through a volatile initializer; whole if-statement deleted when its body was side-effect-free \u2014 confirmed via -O0 disassembly, 0x4a-byte function with no 0x12 displacement). Final harness derives NULL from getenv() at runtime and gives the deref-if a printf side effect mirroring the kernels real amdgpu_atif_get_sbios_requests() call at :391; fault then appears deterministically at 0x12.",
  "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": 900,
  "guest_dirty": 0,
  "build_cmd": "scp -F dfbsd-qemu/config findings/poc/DF-4605/extract_and_test.sh dfbsd:/tmp/ && vm.sh run_root sh /tmp/df4605_extract_and_test.sh (script compiles on guest: cc -O2 -Wall -o guard_test guard_test.c)",
  "run_cmd": "dfbsd-qemu/vm.sh run_root sh /tmp/df4605_extract_and_test.sh",
  "code_hash": "0748aff7a3281f90bdfd609bee7a0aed66c451e7fbba0be9524433f5313bf8f0",
  "notes": "Guest stayed up throughout (userland harness faults only); no reset required. Fixed variant validated 4/4 in the same harness. Secondary logic defect from the same root cause (recorded in the finding): non-matching or disabled notifications are no longer ignored \u2014 ATIF GET_SYSTEM_BIOS_REQUESTS is evaluated for unrelated device notifies. Functional (non-security) port artifact also noted: handler is installed on the dGPUs handle while ATIF typically lives in the iGPU namespace on PX systems, so ATIF notifications may never arrive on the node where this handler listens.",
  "recommended_fix": "Restore the early return in amdgpu_atif_handlers guard (replace the #if 0 block with return;), mirroring upstreams NOTIFY_DONE early-exit \u2014 see fix.diff.",
  "fix_status": "inconclusive",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_kernel_uname": null,
  "fix_verdict": "Kernel-build fix validation not testable on this guest: amdgpu is not part of any buildable kernel config (absent from sys/conf/files; module tree not present) and the guest has no AMD GPU to probe, so no baseline kernel panic and no patched-kernel run are possible there. Validated instead at the verbatim-extraction level inside the same proof: the extracted guard+deref with the fix applied (the #if 0 block replaced by return;) returns cleanly in 4/4 runs while the unpatched extraction faults at 0x12 in 4/4 runs. fix.diff additionally passes git apply --check against the unmodified sys/ tree. fix_baseline_reproduced/fix_patched_reproduced are 0 because they describe the kernel-level validation run, which did not occur.",
  "fix_evidence": "run.log and run.stress.log lines fixed exit=0 + PROOF-OK: baseline faults at NULL+0x12; fixed variant returns cleanly (4/4); guard_fixed.inc (extracted guard with fix applied, as compiled); git apply --check findings/poc/DF-4605/fix.diff -> APPLIES-CLEAN (host sys/ tree)."
}