DragonFlyBSD Kernel Audit
DF-2791 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-2791",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "sys_rtprio() (kern_resource.c:704) dereferences FIRST_LWP_IN_PROC(p) without a NULL check. A process is pfind()-visible with zero lwps during its fork/SIDL window (allproc insertion at kern_fork.c:491 until the first lwp tree insert at kern_fork.c:848), and fork1's p_token is dropped whenever the forking thread deschedules (lwkt_relalltokens), which an unprivileged mmap/munmap churn thread in the forker forces inside vm_fork(). Unprivileged rtprio(RTP_LOOKUP) then reads &((struct lwp*)0)->lwp_rtprio (0x198) inside copyout -> EFAULT (3 hits in 105 syscalls as uid 1001). Privileged rtprio(RTP_SET) executes the raw store lp->lwp_rtprio = rtp at 0x198 with pcb_onfault == NULL -> 'Fatal user address access from kernel mode' -> panic: verified on the serial console ('Stopped at sys_rtprio+0x1b0: movl %eax,0x198(%rdx)'). Unprivileged callers cannot reach the write (blocked at kern_resource.c:718-723), so impact ceiling for a non-root user is the EFAULT side channel; the panic requires an already-privileged caller — severity Low.",
  "exploit_chain": "unpriv: fork-bomber (p_thread churn via mmap/munmap widens the SIDL zero-lwp window) + pid-predicting rtprio(RTP_LOOKUP) sprayer -> kernel NULL deref read contained by copyout onfault -> EFAULT observability only. priv (root/SYSCAP_NOSCHED): same race with RTP_SET -> 4-byte semi-controlled write at fixed unmappable user address 0x198 -> fatal trap -> panic. No path to uid0: the write address is not mappable (page 0) and not controllable.",
  "evidence": [
    "run.log: '[hit] pid 10115 EFAULT after 97/100/103 syscalls' + 'REPRODUCED' (unpriv leg, kernel #0)",
    "panic.txt: 'Fatal user address access from kernel mode from rtprio_sidl', 'fault virtual address = 0x198', 'supervisor write data', 'Stopped at sys_rtprio+0x1b0: movl %eax,0x198(%rdx)' (priv leg, kernel #0)",
    "fixed_lookup.log: 'syscalls=1015185 ok=261860 EFAULT=0' on fix.diff-patched kernel #1",
    "fixed_set.log: root RTP_SET race >=65s on patched kernel #1, guest alive",
    "VERDICT.md: full path:line trace of the SIDL window and token-drop mechanism"
  ],
  "kernel_refs": [
    "sys/kern/kern_resource.c:704",
    "sys/kern/kern_resource.c:707",
    "sys/kern/kern_resource.c:718",
    "sys/kern/kern_resource.c:748",
    "sys/kern/kern_fork.c:491",
    "sys/kern/kern_fork.c:848",
    "sys/kern/kern_proc.c:524",
    "sys/platform/pc64/x86_64/trap.c:917",
    "sys/kern/lwkt_token.c:539"
  ],
  "poc_changes": "Original seed sketch assumed the SIDL window was reachable by mere forking; in fact fork1 holds p2->p_token and only deschedules drop it. Added a sibling pthread churning mmap/munmap so vm_fork()'s vm_map-token acquisition blocks mid-window (this made the race hit on the first attempt after 0 hits across two earlier variants), fixed zombie reaping (maxprocperuid EAGAIN), pid prediction from a shared ring buffer, and added signal.h/pthread linkage.",
  "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": 420,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -pthread -o rtprio_sidl rtprio_sidl.c",
  "run_cmd": "./rtprio_sidl lookup ; ./rtprio_sidl set",
  "code_hash": "5536c9af2c72242f6f6a21eedba94ce41a1171672317b6e42ad51b2d0b9dac9c",
  "notes": "Guest was reset (vm.sh reset with-src) after verification; guest_dirty refers to the returned state. offsetof(struct lwp, lwp_rtprio) = 0x198 confirmed by the panic fault address.",
  "recommended_fix": "In sys_rtprio(), return ESRCH when FIRST_LWP_IN_PROC(p) is NULL and LWPHOLD/LWPRELE the lwp across the blocking copyout, mirroring sys_lwp_rtprio() (fix.diff, validated).",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Tue Sep  1 10:01:19 UTC 2026  root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff (NULL check -> ESRCH + LWPHOLD/LWPRELE) applied to guest /usr/src, make nativekernel -j6 + make installkernel, reboot into kernel #1. Baseline behaviors gone: lookup 0 EFAULT over 1,015,185 probes (was 3 in 105); root RTP_SET race ran 65+s with the guest alive (baseline panicked <50s).",
  "fix_evidence": [
    "fixed_lookup.log",
    "fixed_set.log",
    "fix.diff"
  ]
}