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

cothread_wakeup()'s SIGINT vector forces a process-wide empty-handler latch that permanently disables the vkernel console's ^C handling

Summary

The wakeup vector is SIGINT - a signal with existing process-wide meaning (vconsinit installs vconssignal for it early in boot), and making it survivable requires vktimer_thread to install an empty SA_NODEFER handler process-wide from the cothread, stomping the console handler forever. Traced live on a real boot (install sequence with nm-confirmed addresses) and behaviorally confirmed: 3x kill -INT post-boot leaves the vkernel alive, SIGTERM control still works. Operators lose the vconssignal terminal-restore path; any future SIGINT consumer breaks identically. Move-the-vector fix (dedicated blocked signal, or chain to the saved disposition).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5608 Β· 6 files
FileTypeDescriptionSize
sigtrace.c β€” 1.4 KB view raw
run.sh β€” 100 B view raw
run.log β€” 2.2 KB view raw
run.driver.log β€” 996 B view raw
verdict.json β€” 2.7 KB view raw
VERDICT.md β€” 2.8 KB ↓ raw
VERDICT.md
↓ download raw

DF-5608 β€” VERDICT

Status: reproduced (runtime, one traced boot + one confirmation boot). Impact: none (functional/operational regression β€” no memory safety, no DoS; SIGTERM/SIGKILL still work). Confidence: certain.

What was proven

On a real VKERNEL64 boot with an LD_PRELOAD sigaction() tracer:

  1. The console subsystem installs vconssignal for SIGINT early (console.c:344-360; traced install handler=0x72b6d0 flags=0, nm: vconssignal).
  2. At initclocks the vktimer cothread β€” a different pthread β€” installs the empty SA_NODEFER latch (handler=0x72aa00 flags=0x10, nm: vktimer_sigint; systimer.c:223-227), which being a process-wide sigaction() replaces vconssignal. This replacement is REQUIRED by cothread_wakeup()'s choice of SIGINT as the thread-directed wakeup vector (cothread.c:196): SIGINT is not in cpu_mask_all_signals()'s set (machintr.c:205), so without hijacking the disposition the wakeup SIGINT would run whatever handler the process had β€” in the cothread, with %gs=0 (see DF-5607 for what a faulting handler does there).
  3. Behavioral consequence, live: three kill -INTs to the booted vkernel child leave it ALIVE β€” host ^C is dead post-boot (vconssignal never runs: no terminal restore, no clean re-raise). Control: SIGTERM still reaches the console handler path ("Caught SIGTERM from host system. Shutting down...").

Root cause is shared between cothread.c (the SIGINT vector choice at cothread.c:196 β€” a signal the process ALSO uses for console control) and systimer.c (the process-wide latch at :227). The console's ^C feature is silently lost after boot; the operator must use SIGTERM (wrapper forwards it, init.c:216-219) or SIGKILL.

Fix direction

The wakeup vector should not be a signal with existing process-wide meaning. Options: use a dedicated unused signal (e.g. one of the realtime signals) for cothread_wakeup() β€” blocking it in every cothread via cpu_mask_all_signals() (machintr.c) β€” or save/restore the previous SIGINT disposition and chain to vconssignal from vktimer_sigint when the signal is process-directed rather than thread-directed. No fix.diff authored (Low severity, no memory-safety impact; behavioral evidence only).

Environment

Same rig as DF-5607 (guest DragonFly 6.5-DEVELOPMENT, VKERNEL64 in-guest build, /vkroot.img, -n 2).

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.

(No fix.diff for this item: documented recommendation only - see Fix above.)

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched clean

No fix build for a Low/operational finding (contract skips Low hardening); behavioral fix direction documented in VERDICT.md.

per-fix-DF-5608

Confirmed kernel references

Detail

Evidence (decisive lines)

findings/poc/DF-5608/run.driver.log (SIGTRACE install sequence with nm-confirmed symbol addresses 0x72b6d0=vconssignal / 0x72aa00=vktimer_sigint; AFTER_SIGINT_3 STILL ALIVE; SIGTERM shutdown in console tail); run.log (full traced console)

PoC changes

Authored from scratch (no seed): LD_PRELOAD sigaction tracer + child-vs-wrapper targeting (the init.c fork wrapper SIG_IGNs SIGINT itself, so the behavioral test must target the vkernel child pid).

Verified recommended fix

Move the cothread wakeup vector off SIGINT (dedicated signal, blocked in cothreads via cpu_mask_all_signals) or chain to the previous disposition from vktimer_sigint.

Verdict

cothread_wakeup() pthread_kills SIGINT into cothreads (cothread.c:196); to make that survivable the vktimer cothread installs an empty SA_NODEFER SIGINT handler process-wide (systimer.c:223-227), which permanently REPLACES the vkernel console's SIGINT handler vconssignal (console.c:355, installed earlier at console init). Traced live on a real VKERNEL64 boot: install sequence SIG_IGN(init.c:209) -> vconssignal(console.c:355) -> vktimer_sigint(systimer.c:227, from the cothread pthread, flags=SA_NODEFER); after boot, 3x kill -INT to the vkernel child leaves it alive (host ^C dead -- no terminal restore, no clean termination), while SIGTERM still shuts it down. Operational regression owned by the SIGINT vector choice; no memory-safety impact.