β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2827

ckpt_thaw_proc(): e_phnum == 0 makes kmalloc(0) return ZERO_LENGTH_PTR; phdr->p_filesz reads kernel VA 0x18 (unconditional fatal kernel page fault)

Field Value
ID DF-2827
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
CWE CWE-476 (near-NULL: ZERO_LENGTH_PTR)
File sys/kern/kern_checkpoint.c
Lines 232-240
Area kern
Confidence certain
Discovered 2026-08-31
Pass 2 (GLM 5.3 second pass)
Bucket memcorrupt
Reported pending
Known CVE none
CVE match novel

Summary

elf_gethdr() validates e_ehsize/e_phentsize/e_ident but never e_phnum. With e_phnum == 0, kmalloc(0, M_TEMP, M_WAITOK) returns ZERO_LENGTH_PTR = (void *)-8, and elf_getnotes(lp, fp, phdr->p_filesz) loads from 0xFFFFFFFFFFFFFFF8 + 0x20 = 0x18 β†’ supervisor page fault β†’ panic on any kernel config. Requires only a well-formed 64-byte ELF header; wheel-gated (kern.ckptgroup). The loaded value only feeds the nthreads sanity check β€” DoS, no leak. VERIFIED on the stock kernel (Fatal trap 12 ... fault virtual address = 0x18 ... Stopped at sys_sys_checkpoint+0x2f0); fixed kernel returns clean EINVAL.

Reject e_phnum == 0 before the allocation (row diff; validated).

Timeline

  • 2026-08-31 Discovered during pass-2 audit of kern_checkpoint.c (GLM 5.3); panic reproduced + fix validated same run.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2827 Β· 8 files
FileTypeDescriptionSize
README.md β€” 1.5 KB ↓ raw
VERDICT.md β€” 1.7 KB ↓ raw
gen.c β€” 7.4 KB view raw
trigger_fd3.c β€” 654 B view raw
build.sh β€” 801 B view raw
run.sh β€” 175 B view raw
panic.txt β€” 2.2 KB view raw
verdict.json β€” 2.8 KB view raw

DF-2827 β€” ckpt_thaw_proc: e_phnum == 0 β‡’ kmalloc(0) = ZERO_LENGTH_PTR deref (kernel page fault)

  • File: sys/kern/kern_checkpoint.c
  • Bug lines: kern_checkpoint.c:232-240 β€” nbyte = sizeof(Elf_Phdr) * ehdr->e_phnum with e_phnum == 0 β‡’ kmalloc(0, M_TEMP, M_WAITOK) returns ZERO_LENGTH_PTR = (void *)-8 (sys/kern/kern_slaballoc.c:888-891); elf_getphdrs(fp, phdr, 0) reads nothing, then phdr->p_filesz (kern_checkpoint.c:240) loads from (void*)-8 + 0x20 = kernel VA 0x18 β‡’ supervisor page fault β‡’ fatal trap / panic on any kernel config.
  • Gate: same as DF-2826 (kern.ckptgroup, wheel by default; no fhandle or further image content needed β€” the header alone is enough).

Build / Run (guest, root)

sh /root/df2826/build.sh                  # builds gen + triggers + images
cd /root/df2826
./gen df2827.ckpt stage2.bin zero         # e_phnum = 0 variant
./trigger_fd3 df2827.ckpt

Expected

Fatal user address access from kernel mode from trigger_fd3 at ...
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x18
fault code = supervisor read data, page not present
Stopped at sys_sys_checkpoint+0x2f0: movq 0x20(%rax),%rax
Debugger("panic")

(0x18 = ZERO_LENGTH_PTR(-8) + offsetof(Elf64_Phdr, p_filesz)(0x20); the instruction is exactly the phdr->p_filesz load feeding elf_getnotes.)

Fix

Rejected in ../DF-2826/fix.diff (e_phnum == 0 β†’ EINVAL before the kmalloc); validated on the patched kernel (no trap, clean EINVAL).

VERDICT.md
↓ download raw

DF-2827 VERDICT β€” ZERO_LENGTH_PTR dereference on e_phnum == 0

REPRODUCED (certain). ckpt_thaw_proc() validates e_ehsize, e_phentsize and e_ident in elf_gethdr() (kern_checkpoint.c:117-143) but never e_phnum. With e_phnum == 0:

  • nbyte = sizeof(Elf_Phdr) * e_phnum = 0 (kern_checkpoint.c:232);
  • kmalloc(0, M_TEMP, M_WAITOK) hits the degenerate-size case and returns ZERO_LENGTH_PTR = (void *)-8 (kern_slaballoc.c:882-891 β€” "Return a special pointer");
  • elf_getphdrs() reads 0 bytes (harmless);
  • elf_getnotes(lp, fp, phdr->p_filesz) (kern_checkpoint.c:240) evaluates phdr->p_filesz β‡’ load from 0xFFFFFFFFFFFFFFF8 + 0x20 = 0x18.

Observed on the stock guest (fresh with-src boot):

Fatal user address access from kernel mode from trigger_fd3 at ffffffff8061e1f0
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x18
fault code = supervisor read data, page not present
Stopped at sys_sys_checkpoint+0x2f0: movq 0x20(%rax),%rax

The movq 0x20(%rax),%rax with rax = 0xfffffffffffffff8 is precisely the phdr->p_filesz read. This is an unconditional kernel-mode fault on a canonical-low address β€” panic on any config (no INVARIANTS needed).

Impact: local kernel panic / DoS of the checkpoint path, gated by kern.ckptgroup (default wheel-only; verified EPERM for a non-wheel user). No info leak (the loaded value only feeds the nthreads sanity check in elf_getnotes, kern_checkpoint.c:185-189, which rejects it).

Fix: reject e_phnum == 0 in ckpt_thaw_proc() before the kmalloc β€” included and validated with DF-2826's fix.diff (patched kernel returns EINVAL, no trap; see ../DF-2826/fix_run.log).

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

With the e_phnum==0 rejection in the patched kernel, sys_checkpoint returns a clean error (-1/EINVAL per the trigger) and no Fatal trap occurs; kernel stays up. Baseline panic captured in panic.txt.

findings/poc/DF-2827/panic.txt; findings/poc/DF-2826/fix_run.log; findings/poc/DF-2826/fix.diff
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Tue Sep 1 20:00:55 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

Confirmed kernel references

Detail

Evidence (decisive lines)

['panic.txt: Fatal trap 12, fault VA 0x18, instruction movq 0x20(%rax),%rax with rax=(void*)-8', 'fix_run.log (in ../DF-2826): patched kernel returns clean error, no trap', "gen.c: 'zero' mode generates the e_phnum == 0 image"]

PoC changes

Reuses the DF-2826 generator with phnum_mode=zero (e_phnum=0, no phdr table written); trigger identical to DF-2826's fd3 trigger.

Verified recommended fix

Reject e_phnum == 0 (and sanity-bound it) in ckpt_thaw_proc() before the kmalloc - included and validated in ../DF-2826/fix.diff.

Verdict

ckpt_thaw_proc() never validates ehdr->e_phnum. With e_phnum == 0, kmalloc(0, M_TEMP, M_WAITOK) returns ZERO_LENGTH_PTR ((void *)-8) and the subsequent 'phdr->p_filesz' load (kern_checkpoint.c:240) reads kernel VA 0x18, producing an unconditional supervisor page fault: 'Fatal trap 12: page fault while in kernel mode, fault virtual address = 0x18 ... Stopped at sys_sys_checkpoint+0x2f0: movq 0x20(%rax),%rax' (0x20 = offsetof(Elf64_Phdr, p_filesz)). Panic reproduces on the stock kernel from a fresh snapshot; requires only the 64-byte ELF header to be well-formed, no file handles or note content. Gate: kern.ckptgroup (wheel by default); non-wheel users get EPERM before parsing. DoS only - the loaded value feeds the nthreads sanity check in elf_getnotes which rejects garbage.