DragonFlyBSD Kernel Audit
DF-2948 / fix.diff
← back to finding ↓ download raw
--- 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);