⬢ DragonFlyBSD Kernel Audit
DF-5688 / df5688_mod.c
← back to finding ↓ download raw
/*
 * DF-5688 — in-kernel proof module.  Loads with kldload(8), runs the
 * 4-op duplicate-id sequence (Part B of df5688_minimal.h) against the
 * REAL kernel idr implementation (sys/libkern/linux_idr.c, compiled
 * into X86_64_GENERIC), prints the outcome to the console/dmesg, and
 * unloads cleanly (no crash — the sequence never drives a counter
 * negative).
 *
 * Sequence (identical to the userland rig's minimal_dup):
 *   1. idr_alloc(&idr, A, 526, 0, 0)        -> 526   (bumps nodes[525])
 *   2. idr_remove(&idr, 525)                -> NULL  (525 NEVER allocated;
 *        the `allocated != 0' guard passes because nodes[525] is a
 *        cumulative counter -> PHANTOM DECREMENT)
 *   3. idr_alloc(&idr, B, 525, 532, 0)      -> 525
 *   4. idr_alloc(&idr, C, 525, 0, 0)        -> 525   AGAIN (still live!)
 *
 * Build: make -m /usr/src/share/mk KERNCONF=X86_64_GENERIC
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/idr.h>

static void
df5688_show(struct idr *idp, const char *tag)
{
	kprintf("df5688[%s]: count=%d nodes[525].allocated=%d "
	    "nodes[524].allocated=%d nodes[511].allocated=%d\n",
	    tag, idp->idr_count,
	    (525 < idp->idr_count) ? idp->idr_nodes[525].allocated : -999,
	    (524 < idp->idr_count) ? idp->idr_nodes[524].allocated : -999,
	    (511 < idp->idr_count) ? idp->idr_nodes[511].allocated : -999);
}

static void
df5688_run(void)
{
	struct idr idr;
	void *a = (void *)0xdead0001UL, *b = (void *)0xdead0002UL;
	void *c = (void *)0xdead0003UL;
	void *got;
	int id1, id2, id3, bad = 0;

	idr_init(&idr);
	df5688_show(&idr, "init");

	id1 = idr_alloc(&idr, a, 526, 0, 0);
	kprintf("df5688[1]: idr_alloc(A, 526, 0, 0) -> %d\n", id1);
	df5688_show(&idr, "after-alloc-526");

	got = idr_remove(&idr, 525);
	kprintf("df5688[2]: idr_remove(525) [NEVER allocated] -> %p "
	    "(Linux semantics: NULL no-op; DF: PHANTOM DECREMENT)\n", got);
	df5688_show(&idr, "after-phantom-remove-525");

	id2 = idr_alloc(&idr, b, 525, 532, 0);
	kprintf("df5688[3]: idr_alloc(B, 525, 532, 0) -> %d\n", id2);
	kprintf("df5688[3]: idr_find(%d) -> %p\n", id2, idr_find(&idr, id2));

	id3 = idr_alloc(&idr, c, 525, 0, 0);
	kprintf("df5688[4]: idr_alloc(C, 525, 0, 0) -> %d\n", id3);
	got = idr_find(&idr, id2);
	kprintf("df5688[4]: idr_find(%d) -> %p (object B was %p)\n",
	    id2, got, b);

	if (id3 == id2) {
		bad = 1;
		kprintf("df5688: *** DUPLICATE ID %d CONFIRMED IN-KERNEL: "
		    "two idr_alloc() calls returned the same id while "
		    "live; the first object pointer (%p) was silently "
		    "overwritten\n", id2, b);
	}
	if (got == c && got != b)
		kprintf("df5688: *** object handle aliasing confirmed: "
		    "handle %d now maps to C (%p), B (%p) leaked\n",
		    id2, c, b);

	idr_destroy(&idr);
	kprintf("df5688: %s\n", bad ?
	    "KERNEL MEMORY CORRUPTION (duplicate id) CONFIRMED" :
	    "no corruption observed");
}

static int
df5688_handler(module_t mod, int type, void *data)
{
	switch (type) {
	case MOD_LOAD:
		df5688_run();
		return (0);
	default:
		return (0);
	}
}

static moduledata_t df5688_mod = {
	"df5688",
	df5688_handler,
	NULL
};

DECLARE_MODULE(df5688, df5688_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
MODULE_VERSION(df5688, 1);