# DF-5587 — cothread faults die silently through unbounded exc_segfault recursion (SA_NODEFER + empty sa_mask + %gs==0)

## What

`init_exceptions()` (`sys/platform/vkernel64/x86_64/exception.c:222-229`)
installs `exc_segfault` for SIGBUS/SIGSEGV/SIGTRAP/SIGFPE process-wide with
`SA_SIGINFO | SA_NODEFER` and an **empty** `sa_mask`. The vkernel's
cothreads run with:

- `%gs = 0` (`tls_set_gs(0, ...)`, `sys/platform/vkernel64/platform/cothread.c:143-148`
  — "%gs (aka mycpu) is illegal in cothreads"), and
- only the 8 *interrupt* signals masked
  (`cpu_mask_all_signals()`, `sys/platform/vkernel64/platform/machintr.c:205-211`
  — SIGSEGV/SIGBUS/SIGTRAP/SIGFPE are NOT in the set).

So any fault in any cothread (disk/net/console I/O threads executing
guest-issued requests) is delivered to `exc_segfault`, whose first real
work is `kern_trap()` — whose first statement is
`struct globaldata *gd = mycpu;` (`trap.c:585`) — a `%gs`-relative load
that **faults when %gs==0**. With SA_NODEFER and an empty mask the signal
is re-delivered immediately on the same stack: unbounded recursion, no
nesting guard, no SA_ONSTACK, until the host cannot push the signal frame
and kills the whole vkernel process — **no panic, no dump path, no
diagnostic** (a real kernel turns this class into #DF → dblfault_handler
→ panic with register state, trap.c:969-997).

Latent trigger (requires a cothread-side fault, i.e. a vkernel driver bug
or bad DMA pointer): severity Info / hardening. The sibling deterministic
recursion for *lwkt* threads with a valid %gs is DF-5338 (trap.c
fall-through); this one never even reaches trap.c's switch.

## Reproducer

`coresig.c` — installs the exact `init_exceptions` signal configuration
(SA_SIGINFO|SA_NODEFER, empty mask) and a handler whose first dereference
uses a NULL "%gs base" (the cothread condition), then takes one fault.

Result on the DragonFly guest (3/3 deterministic):

```
child wait status: exited=0 exit=-1 signaled=1 termsig=4 core=128
RECURSION_DEPTH=307833
SILENT_DEATH_CONFIRMED: handler re-entered 307833 times before the host
killed the process — no panic, no dump path, no diagnostic
```

`corefixed.c` — fix-semantics demo (the `exc_nesting` guard from
fix.diff): the recursion is caught at depth 2, one diagnostic line is
printed, the disposition is reset to SIG_DFL, and the child dies of the
*true* faulting signal (SIGSEGV, core dumped) after exactly 2 handler
entries.

## Fix

`fix.diff` — re-entrancy guard + honest death (diagnostic, SIG_DFL,
re-raise) in `exc_segfault`. Applied together with DF-5586's guard on the
guest's /usr/src; VKERNEL64 rebuilt cleanly (build+link OK) and the
in-vkernel fault storm still passes (STORM_RC=0, ALIVE).

## Verdict summary

- status: **reproduced** (mechanism: 307,833 silent re-entries → host
  kill, 3/3; fix semantics: 2 entries + diagnostic + true SIGSEGV)
- impact: dos-class *within the VM* (whole-vkernel death, silent), but
  latent (needs a cothread fault to fire); severity Info, confidence
  certain (mechanism), trigger speculative.

## 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.
