# DF-5473 — vkernel64 backtrace fault outside a DDB session longjmps into the BSS-zeroed global db_jmpbuf → libc longjmp restores rsp=0/rbp=0 → vkernel process dies of SIGILL inside longjmp (pc64 DF-5471 twin, vkernel flavor)

**File:** `sys/platform/vkernel64/x86_64/db_trace.c` (pass 2; mechanism spans
`sys/platform/vkernel64/x86_64/db_interface.c`, `sys/ddb/db_command.c`)
**Severity:** Medium · **Confidence:** certain · **Verified:** reproduced on the QEMU guest (in-vkernel rig), twice

## What

Every dereference the vkernel backtracer performs goes through
`db_get_value()` → `db_read_bytes()`, which arms the global no-fault recovery
`db_nofault = &db_jmpbuf` (vkernel db_interface.c:236) around a raw pointer
dereference. On a fault, the vkernel signal path
(`exc_segfault` → `kern_trap` → `trap_pfault` → `trap_fatal` → `kdb_trap`,
trap.c:959) executes `longjmp(*no_fault, 1)` (db_interface.c:138-142).

`db_jmpbuf` (sys/ddb/db_command.c:62, BSS-zeroed) is only initialized by
`setjmp()` inside an established DDB session (db_command.c:467
`db_command_loop`, db_trap.c:68). But the vkernel runs the backtracer:

1. **On every panic, before DDB exists** — VKERNEL64 config has `options
   DDB_TRACE` (sys/config/VKERNEL64:59), so `trace_on_panic=1` and `panic()`
   calls `print_backtrace(6)` (kern_shutdown.c:880-881) *before*
   `Debugger("panic")` — with `db_jmpbuf` still all-zero. The harness prints
   the state right before the walk: `db_active=0 db_jmpbuf[0]=0 db_jmpbuf[3]=0`.
2. **From live kernel debug print sites with no debugger involved** — the
   same unguarded `print_backtrace(-1)` sites as pc64 (uipc_mbuf.c:1339,
   kern_jail.c:785, indefinite2.h:173, busdma_machdep.c:205, ...).

The vkernel is a host userland process, so this `longjmp`/`setjmp` pair is
**host libc's**, and the fault semantics differ from pc64 in the worst way:
the fault arrives as an asynchronous host SIGSEGV *inside libc's memcpy-ish
loop in db_read_bytes*, and the recovery longjmps out of a signal handler
into a buffer that has never been set.

## Observed crash chain (guest-proven)

```
db_stack_trace_cmd adopts guest-user VA 0x100000000 (DF-5472)
  → db_get_value/f_retaddr read (db_trace.c:231) faults
  → host SIGSEGV → exc_segfault (exception.c:263) → kern_trap (trap.c:583)
  → T_PAGEFLT → trap_pfault(FALSE) → vm_fault(kernel_map, 0x100000008) fails
  → trap_fatal (trap.c:887) → kdb_trap (trap.c:959)
  → prints "kernel: type 12 trap, code=0" (db_interface.c:136)
  → db_nofault armed → longjmp(db_jmpbuf, 1) on the ZEROED buffer
  → libc longjmp restores rsp=0, rbp=0 (rbx=0) → faults at longjmp+115
  → process killed by SIGILL: pid (kernel.stripped), uid 0: exited on
    signal 4 (core dumped)     [guest host dmesg]
  → core: #0 0x102903f3 <longjmp+115> in /lib/libc.so.8, rsp=0x0, rbp=0x0
```

After the longjmp the vkernel produces **zero** further output: no DDB
prompt, no second "Fatal trap", no reboot — the original panic's evidence
(the whole point of `trace_on_panic`) is annihilated and the process leaves
an 11 MB core instead of a backtrace. Reproduced twice (accidental first
run, then deliberate); the isolated mechanism was confirmed with a standalone
`longjmp`-on-zeroed-jmp_buf test on the guest (exit 139, SIGSEGV — in the
vkernel the surrounding context turns it into SIGILL inside libc longjmp).

## Proof (this pack)

- `vkrun_clean.log` — control (chain stays in kernel KVA): walk completes,
  sysctl returns, vkernel survives (`VK_EXIT=0`, clean halt).
- `vkrun_bad.log` — the crash run: 54 lines, ends at `kernel: type 12 trap,
  code=0`; nothing after. Guest host dmesg: `exited on signal 4 (core
  dumped)`. `core_gdb.txt`: `#0 <longjmp+115> in /lib/libc.so.8, rsp=0x0,
  rbp=0x0`.
- `vkrun_fixed.log` — with `fix.diff` (walk refuses non-kernel frames) the
  identical trigger prints two frames and returns; no fault, no signal, clean
  halt. The walk-side fix removes the *reachable* trigger; the full
  remediation additionally wants `db_interface.c` to arm a jmp_buf that is
  valid whenever `db_nofault` can fire (e.g. `setjmp` a local in
  `print_backtrace()`/`db_stack_trace_cmd()` entry, or fall back to
  `db_global_jmpbuf` when no DDB loop ever ran) — noted as out-of-file scope.

## Build / run

```
# on the guest (see README steps in full in run_vk.sh/build_image.sh):
sysctl -w vm.vkernel_enable=1
sh /root/vkrig/build_image.sh           # UFS root image with vktrigger
sh /root/vkrig/run_vk.sh clean          # control: survives
sh /root/vkrig/run_vk.sh bad            # crash: SIGILL in libc longjmp
sh /root/vkrig/run_vk.sh fixed          # after fix.diff: survives
```

The vkernel binary is `make nativekernel KERNCONF=VKERNEL64` (requires the
DF-5474 build fix first) with the rig harness
`sys/platform/vkernel64/x86_64/vk_trace_harness.c` compiled in.
