# DF-2948 — `kern.ncallout` fetched after the only clamp → int-truncated callwheel sizing → boot panic / latent OOB init

`sys/kern/subr_param.c:327-330` clamps the *computed* `ncallout` to
`5*60*hz` and only then fetches the `kern.ncallout` tunable — which
therefore enters the kernel with **no bounds at all**. The consumer
`swi_softclock_setup()` (sys/kern/kern_timeout.c:374-413) does all its math
in `int`:

```c
target    = ncallout / ncpus + 16;                 /* :386 */
cwheelsize = 1;
while (cwheelsize < target) cwheelsize <<= 1;      /* :389-390 signed shift */
cwheelmask = cwheelsize - 1;
...
int wheel_sz;                                       /* :398 */
wheel_sz      = sizeof(*sc->callwheel) * cwheelsize;  /* :406  24 * 2^30 → 0 (mod 2^32) */
sc->callwheel = kmem_alloc3(kernel_map, wheel_sz, ...);  /* :407 zero-length alloc per CPU */
memset(sc->callwheel, 0, wheel_sz);
for (i = 0; i < cwheelsize; ++i)                   /* :410 2^30 OOB init writes */
        spin_init(&sc->callwheel[i].spin, "wheel");
```

`sizeof(struct wheel)` = 8 (spinlock) + 16 (TAILQ_HEAD) = 24 on x86_64, so
`wheel_sz = 24 * 2^30 = 25769803776 ≡ 0 (mod 2^32)` truncates to **0**.

* 6-vCPU guest (`kern.ncallout=2147483647`): target = 357913966 →
  cwheelsize = 2^30 → wheel_sz = 0 → six per-CPU zero-length
  `kmem_alloc3(kernel_map, 0)` calls collide → **`panic: vm_map_entry_link:
  dup addr`** during boot (demonstrated). Had the map not panicked first,
  the `2^30`-iteration init loop would `spin_init`/`TAILQ_INIT` ~25 GB past
  a zero-byte allocation — an out-of-bounds kernel write.
* ≤2-vCPU boxes: `target` can be pushed into (2^30, 2^31) where the
  doubling loop executes `1 << 31` (signed overflow → negative → 0) and
  spins forever: **infinite boot loop**.
* Degenerate corner (`target` wrapped negative via `ncallout + 16`
  overflow at INT_MAX on 1 CPU): cwheelsize stays 1 → every callout in one
  bucket → pathological softclock behavior.

Distinct from DF-0174 (ncallout overflow *via unbounded kern.maxfiles*):
this is the **direct tunable path**, which no maxfiles clamp can reach, and
the sink is the `cwheelsize`/`wheel_sz` int math in kern_timeout.c.

## Reproduce (host side, guest up & clean)

```sh
dfbsd-qemu/vm.sh run_root 'cp /boot/loader.conf /boot/loader.conf.df2948bak && printf "kern.ncallout=\"2147483647\"\n" >> /boot/loader.conf && sync'
dfbsd-qemu/vm.sh run_root 'shutdown -r now'
sleep 80; dfbsd-qemu/vm.sh status     # -> down
grep -n "panic" dfbsd-qemu/boot.log
dfbsd-qemu/vm.sh reset with-src
```

## Expected output

Guest never reaches ssh; serial console (panic.txt) shows, right after
"Initialize MI interrupts for 6 cpus":

```
panic: vm_map_entry_link: dup addr map 0xffffffff8159aea0 ent 0xffffffff81709b18
cpuid = 0
Trace beginning at frame 0xffffffff817a9e30 ...
Debugger("panic")
CPU0 stopping CPUs: 0x0000003e
Stopped at      -0x7f433c74:    movb   $0,0xbd77f9(%rip)
db>
```

Impact: unbootable system. Trust boundary: /boot/loader.conf
(root/loader prompt), same class as DF-0173/DF-0174.
