โฌข DragonFlyBSD Kernel Audit
DF-5326 / mp_bootaddr_underflow.c
โ† back to finding โ†“ download raw
/*
 * DF-5326 โ€” standalone arithmetic reproducer for the mp_bootaddress()
 * integer underflow in sys/platform/pc64/x86_64/mp_machdep.c:206-219.
 *
 * The logic below is copied verbatim from the DragonFly kernel source,
 * preserving the kernel's 32-bit unsigned (u_int) wrap-around semantics.
 *
 * bootMP_size uses the REAL value from the audited guest kernel
 * (X86_64_GENERIC, DragonFly 6.5-DEVELOPMENT):
 *    ffffffff8113b8c0 D mptramp_start
 *    ffffffff8113b96a D mptramp_end
 *    bootMP_size = 0xAA = 170 bytes
 *
 * The returned value is stored by init386() straight into the kernel's
 * physical memory map:  sys/platform/pc64/x86_64/machdep.c:2270-2272
 *      base_memory = physmap[1] / 1024;
 *      physmap[1]  = mp_bootaddress(base_memory);
 * so a wrapped return makes the kernel believe the first usable RAM region
 * is [0, ~0xFFFFC000) โ€” i.e. ~4GB of low memory including all MMIO/ROM
 * holes โ€” corrupting the physmem map for the whole boot.
 *
 * Build: cc -O2 -o mp_bootaddr_underflow mp_bootaddr_underflow.c
 * Run:   ./mp_bootaddr_underflow
 */
#include <stdio.h>
#include <stdint.h>

typedef uint32_t u_int;         /* kernel u_int is 32-bit on pc64 */

#define PAGE_SIZE       4096u
#define PAGE_MASK       (PAGE_SIZE - 1)
#define trunc_page(x)   ((x) & ~PAGE_MASK)

/* real symbols from the guest kernel (nm /boot/kernel/kernel) */
#define MPTRAMP_START   0xffffffff8113b8c0ull
#define MPTRAMP_END     0xffffffff8113b96aull

static u_int bootMP_size, boot_address, mptramp_pagetables;

/* verbatim mp_machdep.c:206-219 */
static u_int
mp_bootaddress(u_int basemem)
{
	bootMP_size = (u_int)(MPTRAMP_END - MPTRAMP_START);
	boot_address = trunc_page(basemem * 1024); /* round down to 4k boundary */
	if (((basemem * 1024) - boot_address) < bootMP_size)
		boot_address -= PAGE_SIZE;	/* not enough, lower by 4k */
	/* 3 levels of page table pages */
	mptramp_pagetables = boot_address - (PAGE_SIZE * 3);

	return mptramp_pagetables;
}

int
main(void)
{
	/* basemem in KB: degenerate first-usable-region ends, plus a sane BIOS */
	u_int basemems[] = { 0, 1, 2, 4, 8, 9, 12, 13, 14, 15, 16, 17, 32, 638 };
	unsigned i;

	bootMP_size = (u_int)(MPTRAMP_END - MPTRAMP_START);
	printf("bootMP_size = %u (0x%x) bytes [from mptramp_end-mptramp_start]\n",
	    bootMP_size, bootMP_size);
	puts("basemem(KB)  boot_address  mptramp_pagetables   effect on physmap[1]");
	puts("-----------  ------------  ------------------   --------------------");
	for (i = 0; i < sizeof(basemems) / sizeof(basemems[0]); i++) {
		u_int b = basemems[i];
		u_int r = mp_bootaddress(b);

		printf("%10u   0x%08x      0x%08x        %s\n",
		    b, boot_address, r,
		    r >= 0x80000000u ?
		    "WRAPPED: physmap[1] ~= 4GB -> whole low 4GB marked RAM" :
		    (r == 0 ? "ZERO: trampoline PTs smash IVT/BDA at PA 0" :
		    "sane"));
	}
	return 0;
}