vkernel shutdown fallback calls the HOST libc reboot(2): a SIGTERM landing in the boot SYSINIT sliver asks the host kernel to reboot/power off the physical machine
Summary
The else-branch (initproc==NULL) calls reboot(RB_POWEROFF) - but the DFly kernel defines no symbol 'reboot' and the vkernel links as an ordinary host program, so the call binds to the HOST libc reboot(2): a userland-hosted kernel asking the host kernel to reboot the machine it runs on (nm shows U reboot@DF404.0). Three independent crash cores put a frame at shutdown.c:97 calling into the ld.so resolver / libc's reboot wrapper, proving the branch executes and enters the host path - only the trigger storm's own SIGILL re-entrancy (the DF-5587/5607 class) prevented the syscall from retiring on the rig; no kernel-side check exists downstream. Window = the SYSINIT sliver before create_init (initshutdown installs the handler, making it signalable); any same-uid process able to signal the vkernel (the documented monitoring path) + a kill-storm squeezes hits in. Route-through-shutdown_nice + SIGISMEMBER-gate fix (3 hunks incl. kern_shutdown hardening) validated: baseline host-reboot-path execution vs patched clean kernel-side halt.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5660 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| shutrig.c | β | 5.4 KB | view raw | |
| build.sh | β | 195 B | view raw | |
| run.sh | β | 98 B | view raw | |
| build_image.sh | β | 1.0 KB | view raw | |
| nm_reboot_proof.txt | β | 0 B | β download | |
| core_backtraces.txt | β | 1.2 KB | view raw | |
| sigill_deaths.txt | β | 480 B | view raw | |
| bootlog_excerpt.txt | β | 3.5 KB | view raw | |
| env5660.txt | β | 220 B | view raw | |
| fix.diff | β | 2.8 KB | view raw | |
| VERDICT.md | β | 5.1 KB | β raw | |
| verdict.json | β | 4.9 KB | view raw |
DF-5660 β VERDICT
Status: reproduced (impact: dos β host-wide reboot/poweroff attempt;
the primitive is the host reboot(2) request itself, executed from
kernel context; no kernel-side check exists downstream).
What was proven, and how
1. The symbol binding (certain)
- No kernel object defines
reboot: the syscall entry issys_reboot(kern_shutdown.c:182) and the shutdown engine isstatic void boot(int)(kern_shutdown.c:158).grep -rn "[^_]reboot(" sys/finds exactly one bare caller: shutdown.c:97. - The vkernel links as a host program:
SYSTEM_LD= @${CC} ${DEBUG} -Wl,--export-dynamic -o ${.TARGET} ${SYSTEM_OBJS} vers.o -pthread(sys/platform/vkernel64/conf/Makefile:23) β libc resolvesexit,sigaction,closefrom, β¦ andreboot. nm/readelfon the built kernel.debug (nm_reboot_proof.txt):U reboot/reboot@DF404.0 UNDβ a dynamic libc import.- shutdown.c:54 includes
<unistd.h>(host header, viaINCLUDES+= -I/usr/include, kern.pre.mk:68), which declaresint reboot(int);at /usr/include/unistd.h:594 β the same declaration conflict my interposer hit.
2. The call executes (certain β three cores)
The initproc == NULL window is the SYSINIT sliver between
initshutdown (SI_BOOT2_PROC0, SI_ORDER_ANY β runs after the two
SI_ORDER_FIRST entries of its subsystem) and create_init
(SI_SUB_CREATE_INIT β the immediately following subsystem; kernel.h has
no subsystem in between). Ordinary signal timing cannot land there
(signalβithread latency β« sliver; the DF-5661 runs showed hundreds of
SIGTERMs all executing shutdown_intr after create_init), but a
same-uid kill-storm (a forked child spamming kill -TERM at full rate,
which any local user of the vkernel's uid can do) squeezes hits into the
window. Three runs died with SIGILL and every core shows:
#1 0x0000000000721280 in ?? () at shutdown.c:97 <- reboot(RB_POWEROFF) #0 0x00007fffffdfe000 (ld.so resolver) / libc umtx_wakeup
i.e. shutdown_intr's reboot(RB_POWEROFF) executed and entered the host
libc reboot path; the process died inside ld.so/libc because the storm
re-entered signal handlers mid-PLT-resolution/mid-libc-prologue β a
trigger artifact. The tripwire variant (interposed reboot() that logs,
silences the storm, masks signals, then issues syscall(55, howto))
never got a clean in-window shot before collateral death; that is a rig
limitation, not a mitigation: nothing in the kernel rejects the call
(the vkernel runs as root; reboot(2)'s only gate is privilege).
3. End-to-end host reboot
Not completed in the lab: each time the branch fired, the storm's own
re-entrancy killed the process inside libc before the syscall retired.
On a production host a single lucky SIGTERM (no storm, no re-entrancy)
completes reboot(2) unimpeded. Severity therefore accounts for the
narrow window (AC:H) but the impact ceiling is the physical host.
Why not uid0
This is not a memory-corruption primitive: it is a wrong-context host syscall. The ceiling is host reboot/poweroff (availability catastrophe, potential data loss on every mounted filesystem of the host), not code execution. On the audit guest the "host" is the VM, so the identical call would cycle the VM.
Fix validation (fixed)
fix.diff (authored after verification, never applied to the read-only
sys/ tree):
- shutdown.c: gate
ksignal(initproc, SIGUSR2)on init actually catching SIGUSR2 (ps_sigcatch, the kernel's own SIG_CATCH/SIG_DFL delivery criterion, kern_sig.c:2110-2146); elseshutdown_nice(RB_POWEROFF | RB_HALT)β the in-kernel path. - kern_shutdown.c:
shutdown_nice()gets the same gate (its blindksignal(initproc, SIGINT)had the identical no-op defect). - kern_shutdown.c:
shutdown_cleanup_proc()only callskern_closefrom(0)whenp == curprocβ boot() entered from an ithread (which is what shutdown_intr is) hascurproc == NULLand KKASSERTs at kern_descrip.c:1420 (hit live during validation:panic: assertion "p" failed in kern_closefrom).
Applied to the guest's /usr/src validation copy, kernel rebuilt:
- Baseline: storm trigger β shutdown.c:97 β host libc reboot (cores above); SIGTERM mid-boot β silent no-op (DF-5661).
- Patched: same storm trigger β tripwire silent, guest host never
cycles; whenever shutdown_intr executes it takes the kernel path:
boot() called on cpu#0 β system threads stopped β Uptime β The operating system has halted. β cpu halt, exiting vkernel β exit(EX_OK) β monitor exits(full transcript: DF-5661/run.fixboot.log). Bad behavior gone.
Environment
env.txt β DragonFly 6.5-DEVELOPMENT X86_64_GENERIC (guest host), VKERNEL64 built in-guest with the DF-5474 defsym workaround; rig lineage DF-5337/5579/5607 (fifo console, /vkroot.img, -n 2/-n 1 SMP variants).
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedBaseline: storm trigger drove shutdown.c:97 into the host libc reboot path (three cores at the callsite). Patched (fix.diff applied to guest /usr/src, VKERNEL64 rebuilt): identical storm trigger produced NO reboot() call (interposer tripwire silent), the guest host never rebooted, and every shutdown_intr execution took the in-kernel halt path instead.
fix.diff; run fix transcript in ../DF-5661/run.fixboot.log (boot() called on cpu#0 -> The operating system has halted. -> cpu halt, exiting vkernel); bootlog_excerpt.txt
Confirmed kernel references
Detail
Exploit chain
same-uid local process (operator script / monitoring / host shutdown) -> SIGTERM storm against the booting root-owned vkernel -> one hit processed between register_int_virtual(2) and fork1's initproc assignment -> shutdown_intr else-branch -> host libc reboot(2) request -> host kernel reboot/poweroff attempt (full machine availability loss; on the audit guest this cycles the VM)
Evidence (decisive lines)
nm_reboot_proof.txt (UND reboot@DF404.0 vs kernel sys_reboot/shutdown_nice); core_backtraces.txt (3x gdb: frame at shutdown.c:97 -> ld.so resolver / libc umtx_wakeup); sigill_deaths.txt (host log of the three crashing runs); bootlog_excerpt.txt (serial console); VERDICT.md narrative
PoC changes
Interposer evolved through v1-v7: v1 single kill inside the sigaction() wrapper was silently dropped (sched_ithd_intern drops ISTATE_NOTHREAD interrupts, kern_intr.c:585) because register_int_virtual(2) runs after the sigaction; v2/v3 fork-storm reached the branch (cores) but storm re-entrancy killed the process inside ld.so/libc; v4/v5 pthread-sniper bursts always executed shutdown_intr post-create_init (latency); final rig = fork-storm + reboot() interposer tripwire (logs howto, silences storm, masks signals, issues syscall(55) = the same syscall libc's wrapper issues) + LD_BIND_NOW. Guest-side sed capped the storm loop at 4M kills in the last build (hash of the local shutrig.c is authoritative).
Verified recommended fix
Gate ksignal(initproc, SIGUSR2) on SIGISMEMBER(initproc->p_sigacts->ps_sigcatch, SIGUSR2) and make the fallback shutdown_nice(RB_POWEROFF|RB_HALT); never call bare reboot() from vkernel kernel context (binds to host libc reboot(2)).
Verdict
shutdown.c:97's reboot(RB_POWEROFF) binds to the HOST libc reboot(2) (no kernel symbol reboot exists; the vkernel links as an ordinary host program -- nm: U reboot@DF404.0) and demonstrably executes from shutdown_intr's initproc==NULL branch: three independent vkernel cores show frame #1 at shutdown.c:97 calling into ld.so/libc's reboot path (one died at the lazy-PLT resolver, one inside libc reboot->umtx_wakeup). The branch requires a SIGTERM processed in the microsecond SYSINIT sliver between initshutdown (SI_BOOT2_PROC0) and create_init (SI_SUB_CREATE_INIT); ordinary kill timing cannot land there (ithread latency >> sliver) but a same-uid kill-storm does -- all three cores were produced that way. End-to-end completion of the host reboot was prevented in the lab only by the storm trigger's own userspace re-entrancy damage (SIGILL inside ld.so/libc mid-call), not by any kernel-side check: the vkernel runs as root and nothing downstream rejects reboot(2). With the fix (fix.diff, 3 hunks) the reboot() call is structurally gone: the storm trigger never trips the interposed reboot, the guest host never cycles, and every shutdown_intr execution takes the in-kernel path (shutdown_nice -> boot() -> cpu_halt -> exit(EX_OK), full transcript in DF-5661/run.fixboot.log).
No comments yet.