⬢ DragonFlyBSD Kernel Audit
DF-5477 / df5477_fixed.c
← back to finding ↓ download raw
/*
 * DF-5477 fix-validation harness — the same verbatim efi_get_table() walk
 * with ONLY the fix.diff changes applied (entry-count cap + PHYS_TO_DMAP
 * translation of the physical st_cfgtbl + ct_data bound check).
 *
 * Part A' (sysctl debug.df5477f=1): same lied-about st_entries=200 ->
 * fixed code returns EOVERFLOW, no OOB walk, nothing found.
 * Part B' (sysctl debug.df5477f=2): physical cfgtbl 0x7FB1C000 ->
 * translated into the DMAP before dereference (no wild-pointer fault;
 * on real EFI hardware this reads the actual firmware config table).
 * Part B2' (sysctl debug.df5477f=3): same, using a guest-backed physical
 * address (0x1000000) so the dereference demonstrably completes.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/uuid.h>
#include <sys/efi.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/pmap.h>
#include <machine/vmparam.h>

#define	DF_CFGTBL_MAX	256

static struct efi_systbl *df_systbl;
static struct efi_cfgtbl *df_cfgtbl;

static MALLOC_DEFINE(M_DF5477F, "df5477f", "DF-5477 fix validation");

/* efi_get_table() WITH fix.diff applied */
static int
df_efi_get_table_fixed(struct uuid *uuid, void **ptr)
{
	struct efi_cfgtbl *ct;
	u_long count;

	if (df_cfgtbl == NULL)
		return (ENXIO);
	count = df_systbl->st_entries;
	if (count > DF_CFGTBL_MAX)			/* FIX: cap the walk */
		return (EOVERFLOW);
	ct = (struct efi_cfgtbl *)PHYS_TO_DMAP(		/* FIX: physical -> DMAP */
	    (vm_paddr_t)(uintptr_t)df_systbl->st_cfgtbl);
	while (count--) {
		if (!bcmp(&ct->ct_uuid, uuid, sizeof(*uuid))) {
			if (ct->ct_data >= DMAP_MAX_ADDRESS - DMAP_MIN_ADDRESS)
				return (ERANGE);	/* FIX: bound ct_data */
			*ptr = (void *)PHYS_TO_DMAP(ct->ct_data);
			return (0);
		}
		ct++;
	}
	return (ENOENT);
}

static const struct uuid df_query = {
	0x0DF54, 0x0770, 0x0007, 0x77, 0x77,
	{0x77, 0x77, 0x77, 0x77, 0x77, 0x77}
};

static int
df_part_a(void)
{
	struct efi_systbl *st;
	const int lie_entries = 512;		/* > DF_CFGTBL_MAX of 256 */
	int error;
	void *ptr = NULL;

	st = kmalloc(sizeof(*st), M_DF5477F, M_WAITOK | M_ZERO);
	st->st_entries = lie_entries;
	st->st_cfgtbl = 0x1000000UL;		/* guest-backed phys */
	df_systbl = st;
	df_cfgtbl = (struct efi_cfgtbl *)(uintptr_t)st->st_cfgtbl;

	kprintf("DF-5477A': st_entries=%d lie (> cap %d), FIXED code: "
	    "expect EOVERFLOW (no OOB walk)\n", lie_entries, DF_CFGTBL_MAX);
	error = df_efi_get_table_fixed((__DECONST(struct uuid *, &df_query)),
	    &ptr);
	kprintf("DF-5477A': result error=%d (EOVERFLOW=%d), ptr=%p -> %s\n",
	    error, EOVERFLOW, ptr,
	    error == EOVERFLOW ? "FIX HOLDS (walk refused)" :
	    error == 0 ? "FOUND (fix failed)" : "other");
	return (0);
}

static void
df_part_b(vm_paddr_t phys)
{
	static struct efi_systbl st;
	struct uuid q;
	void *p = NULL;

	memset(&q, 0, sizeof(q));
	st.st_entries = 2;
	st.st_cfgtbl = phys;
	df_systbl = &st;
	df_cfgtbl = (struct efi_cfgtbl *)(uintptr_t)phys;

	kprintf("DF-5477B': st_cfgtbl physical %#jx -> FIXED code DMAPs it to "
	    "%p before deref\n", (uintmax_t)phys,
	    (void *)PHYS_TO_DMAP(phys));
	df_efi_get_table_fixed(&q, &p);		/* no fault expected */
	kprintf("DF-5477B': returned without fault (error path normal)\n");
}

static int
df_sysctl(SYSCTL_HANDLER_ARGS)
{
	int val, error;

	val = 0;
	error = sysctl_handle_int(oidp, &val, 0, req);
	if (error != 0 || req->newptr == NULL)
		return (error);
	switch (val) {
	case 1:
		return (df_part_a());
	case 2:
		df_part_b(0x7FB1C000UL);	/* firmware-style phys, ~2GB */
		break;
	case 3:
		df_part_b(0x1000000UL);		/* guest RAM-backed phys */
		break;
	}
	return (0);
}

static int
df_modevent(module_t m, int event, void *arg __unused)
{
	switch (event) {
	case MOD_LOAD: {
		SYSCTL_PROC(_debug, OID_AUTO, df5477f,
		    CTLTYPE_INT | CTLFLAG_RW, NULL, 0, df_sysctl, "I",
		    "1=cap test 2=fw-phys DMAP 3=ram-phys DMAP");
		kprintf("DF-5477 fix-validation harness loaded "
		    "(debug.df5477f)\n");
		return (0);
	}
	case MOD_UNLOAD:
		return (0);
	default:
		return (EOPNOTSUPP);
	}
}

static moduledata_t df5477f_mod = { "df5477_fixed", df_modevent, NULL };
DECLARE_MODULE(df5477_fixed, df5477f_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);