--- a/sys/kern/subr_param.c +++ b/sys/kern/subr_param.c @@ -322,11 +322,17 @@ init_param2(int physpages) /* * Calculate the size of the callout wheel. Limit to approximately * 5 minutes worth of table (maxproc would have to be pretty huge), * as more is not likely to gain us anything. + * + * NOTE: The clamp MUST run after the kern.ncallout fetch. Fetching + * the tunable last let an unbounded value reach the int callwheel + * math in swi_softclock_setup() (kern_timeout.c), truncating the + * wheel allocation to zero bytes (DF-2948). */ ncallout = 16 + maxproc + maxfiles; - if (ncallout > 5*60*hz) - ncallout = 5*60*hz; TUNABLE_INT_FETCH("kern.ncallout", &ncallout); + if (ncallout < 16 + maxproc + maxfiles) + ncallout = 16 + maxproc + maxfiles; + if (ncallout > 5*60*hz) + ncallout = 5*60*hz; } /* --- a/sys/kern/kern_timeout.c +++ b/sys/kern/kern_timeout.c @@ -383,13 +383,18 @@ swi_softclock_setup(void *arg) * ncallout is primarily based on available memory, don't explode * the allocations if the system has a lot of cpus. */ target = ncallout / ncpus + 16; + /* sanity: keep target inside int and the wheel allocation sane */ + if (target < 16) + target = 16; + if (target > 1 << 20) + target = 1 << 20; cwheelsize = 1; while (cwheelsize < target) cwheelsize <<= 1; cwheelmask = cwheelsize - 1; @@ -397,11 +402,12 @@ swi_softclock_setup(void *arg) for (cpu = 0; cpu < ncpus; ++cpu) { softclock_pcpu_t sc; - int wheel_sz; + size_t wheel_sz; sc = (void *)kmem_alloc3(kernel_map, sizeof(*sc), VM_SUBSYS_GD, KM_CPU(cpu)); memset(sc, 0, sizeof(*sc)); TAILQ_INIT(&sc->freelist); softclock_pcpu_ary[cpu] = sc; - wheel_sz = sizeof(*sc->callwheel) * cwheelsize; + wheel_sz = sizeof(*sc->callwheel) * (size_t)cwheelsize; sc->callwheel = (void *)kmem_alloc3(kernel_map, wheel_sz, VM_SUBSYS_GD, KM_CPU(cpu)); + KKASSERT(sc->callwheel != NULL); memset(sc->callwheel, 0, wheel_sz);