⬢ DragonFlyBSD Kernel Audit
DF-5640 / df5640.c
← back to finding ↓ download raw
/*
 * DF-5640 — harness for the unvalidated tsc_frequency division in
 * tsc_cputimer_register().
 *
 * sys/cpu/x86_64/misc/cputimer_tsc.c:145-156 does:
 *
 *	freq = tsc_frequency;		// hardware/calibration-derived, unvalidated
 *	tsc_cputimer.freq = freq;
 *	kprintf("TSC: cputimer freq %ju\n", (uintmax_t)freq);
 *	...
 *	cputimer_register(&tsc_cputimer);
 *	cputimer_select(&tsc_cputimer, 0);
 *
 * and kern/kern_cputimer.c:99 unconditionally calls
 * cputimer_set_frequency(timer, timer->freq), which computes
 *
 *	timer->freq64_usec = (1000000LL << 32) / freq;		// :190  #DE if freq==0
 *	timer->freq64_nsec = (1000000000LL << 32) / freq;	// :191
 *
 * NOTE on methodology: a boot-loaded KLD cannot put the kernel globals into
 * the finding's precondition state before tsc_cputimer_register runs, because
 * preloaded modules are linked at SI_BOOT2_KLD (0x1e00000), which is AFTER
 * SI_BOOT2_POST_SMP (0x1cc0000).  This was established empirically: a forcing
 * SYSINIT at SI_BOOT2_FINISH_SMP from a loader-loaded module executed after
 * the whole timer-registration pass (dmesg ordering, see VERDICT.md).
 * Therefore this harness REPLAYS the body of tsc_cputimer_register()
 * (cputimer_tsc.c:137-156) verbatim at kldload time, using the real
 * cputimer_register()/cputimer_select() production entry points, on a
 * replica struct with freq = 0 (the value tsc_frequency has when
 * startrtclock()'s calibration fails, clock.c:980-1043, combined with
 * tsc_mpsync forced to 1 -- hw.tsc_cputimer_force tunable, clock.c:871/1522,
 * or the ncpus==1 early-out at clock.c:1513-1516).
 *
 * The replica uses pri = CPUTIMER_PRI_DUMMY-1 so that, on a kernel where the
 * division is guarded, cputimer_select() would not install it as sys_cputimer
 * (the pri check at kern_cputimer.c:106 rejects it).  The division at
 * kern_cputimer.c:99 happens BEFORE that check, so the unpatched kernel
 * divides by zero regardless.
 *
 * Expected result:
 *   unpatched kernel: "DF5640: replaying ..." then
 *	Fatal trap 6: divide error while in kernel mode   -> panic
 *   patched kernel (fix.diff): warning, module loads, system keeps running.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/systimer.h>
#include <machine/cpufunc.h>

#include <machine/clock.h>	/* tsc_frequency, tsc_mpsync, tsc_invariant */

static sysclock_t df5640_count(void);

/* Replica of tsc_cputimer (cputimer_tsc.c:55-66), freq=0 variant. */
static struct cputimer df5640_timer = {
	.name		= "DF5640",
	.pri		= CPUTIMER_PRI_DUMMY - 1,	/* never selected */
	.type		= CPUTIMER_TSC,
	.count		= df5640_count,
	.fromhz		= cputimer_default_fromhz,
	.fromus		= cputimer_default_fromus,
	.construct	= cputimer_default_construct,
	.destruct	= cputimer_default_destruct,
	.freq		= 0,				/* == tsc_frequency (failed cal.) */
};

static sysclock_t
df5640_count(void)
{
	return (rdtsc() + df5640_timer.base);
}

static int
df5640_modevent(module_t mod __unused, int type, void *data __unused)
{
	uint64_t freq;

	switch (type) {
	case MOD_LOAD:
		kprintf("DF5640: live state: tsc_mpsync=%d tsc_invariant=%d "
		    "tsc_frequency=%ju sys_cputimer=%s (%ju Hz)\n",
		    tsc_mpsync, tsc_invariant, (uintmax_t)tsc_frequency,
		    sys_cputimer->name, (uintmax_t)sys_cputimer->freq);

		/*
		 * --- verbatim replay of tsc_cputimer_register(),
		 * --- cputimer_tsc.c:145-156, with tsc_frequency == 0.
		 */
		tsc_mpsync = 1;		/* the finding's precondition, as if  */
		tsc_frequency = 0;	/* hw.tsc_cputimer_force=1 w/ failed  */
					/* calibration (or UP + invariant)    */
		freq = tsc_frequency;
		df5640_timer.freq = freq;

		kprintf("DF5640: replaying cputimer_tsc.c:145-156 with "
		    "freq=0 (divide error at kern_cputimer.c:190 expected)\n");
		kprintf("TSC: cputimer freq %ju\n", (uintmax_t)freq);

		cputimer_register(&df5640_timer);	/* cputimer_tsc.c:155 */
		cputimer_select(&df5640_timer, 0);	/* cputimer_tsc.c:156 */

		kprintf("DF5640: NOT REACHED on an unpatched kernel\n");
		return (0);
	default:
		return (EOPNOTSUPP);
	}
}

static moduledata_t df5640_mod = {
	"df5640",
	df5640_modevent,
	NULL
};
DECLARE_MODULE(df5640, df5640_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);