โฌข DragonFlyBSD Kernel Audit
DF-2773 / verdict.json
โ† back to finding โ†“ download raw
{
  "finding_id": "DF-2773",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "link_elf_obj_load_file's scan loop (sys/kern/link_elf_obj.c:517-539) increments ef->nreltab/nrelatab before the tracking arrays are allocated (:557-572); several rejections in between (nprogtab==0 at :540, nsym!=1 at :545, bad symstrindex at :551) goto out to linker_file_unload, which for a fresh refs==0 file with no modules tears straight through to ops->unload (kern_linker.c:549), where link_elf_obj_unload_file dereferences the NULL arrays in unguarded loops (:887-892). A 434-byte crafted ET_REL module with a symtab+strtab+one .rela and zero PROGBITS sections panics the stock guest: console shows 'kldload: /tmp/df2773.ko: file has no contents' immediately followed by 'Fatal trap 12: page fault, fault virtual address = 0x0, supervisor read data' stopped at link_elf_obj_unload_file+0x6e (movq (%rax),%rdi) โ€” the error path of the loader itself is the killer. NULL read only: no write, no disclosure, root-gated (SYSCAP_NOKLD) โ€” Low, consistent with DF-0040/0041/0042. fix.diff guards both loops with if (ef->reltab)/if (ef->relatab); on rebuilt kernel #1 the same module is rejected cleanly (RC=1, guest up, 0 panics).",
  "exploit_chain": "root crafts module (gen_module.py mode=nrel-nullderef: no PROGBITS + one .rela) -> kldload -> scan counts nrelatab=1, arrays unallocated -> 'file has no contents' ENOEXEC path -> linker_file_unload -> link_elf_obj_unload_file reads ef->relatab[0].rela via NULL -> fatal page fault at 0x0 -> kernel panic. NULL-deref read only; no unprivileged reach (SYSCAP_NOKLD); ceiling is deterministic panic/DoS.",
  "evidence": [
    "panic.txt โ€” baseline serial console: 'file has no contents' then Fatal trap 12 VA=0x0 stopped at link_elf_obj_unload_file+0x6e movq (%rax),%rdi",
    "run.log โ€” baseline (panic) vs patched (clean RC=1 rejection) transcripts",
    "gen_module.py โ€” trigger generator (mode nrel-nullderef): 5 sections, zero PROGBITS/NOBITS, one SHT_RELA",
    "build.log โ€” full untrimmed nativekernel build of the fix (-Werror clean)",
    "VERDICT.md โ€” path:line trace incl. all four abort paths between scan and allocation and the kern_linker.c:549 teardown",
    "fix.diff โ€” unload-loop NULL guards (plus the companion DF-2771/2772 changes), baseline-vs-patched validated"
  ],
  "kernel_refs": [
    "sys/kern/link_elf_obj.c:530",
    "sys/kern/link_elf_obj.c:534",
    "sys/kern/link_elf_obj.c:540",
    "sys/kern/link_elf_obj.c:557",
    "sys/kern/link_elf_obj.c:832",
    "sys/kern/link_elf_obj.c:887",
    "sys/kern/link_elf_obj.c:890",
    "sys/kern/kern_linker.c:549"
  ],
  "poc_changes": "n/a โ€” trigger authored fresh (hand-crafted ET_REL); no prior seed for this finding",
  "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": 600,
  "guest_dirty": 0,
  "build_cmd": "python3 gen_module.py df2773.ko nrel-nullderef",
  "run_cmd": "vm.sh run_root 'kldload /tmp/df2773.ko'  (expect 'file has no contents' then Fatal trap 12 VA=0x0 at link_elf_obj_unload_file on stock; clean RC=1 rejection on patched)",
  "code_hash": "352c5bb633e0445b61c03e12fee9bc20428b01d5c4debe40506de0c2410120d2",
  "notes": "Fix validated with a single combined kernel build covering DF-2771/2772/2773. Guest reset to clean-source snapshot after verification. The defect also means ANY future scan-time hardening of the REL/RELA counting (e.g. DF-2772's fix) would re-open this NULL-deref without the unload guard โ€” the two fixes are coupled.",
  "recommended_fix": "Guard the entry-free loops in link_elf_obj_unload_file with if (ef->reltab) / if (ef->relatab), mirroring the existing if (ef->progtab) guard.",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Tue Sep  1 01:04:57 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Patched kernel rejects the identical module with ENOEXEC (RC=1, no panic, guest up); baseline kernel #0 NULL-deref panics on the same input. fix.diff authored against read-only sys/ and applied only inside the guest's /usr/src copy.",
  "fix_evidence": "run.log (baseline vs patched), build.log (kernel #1 build), dmesg 'linker_load_file: Unsupported file type'"
}