โฌข DragonFlyBSD Kernel Audit
DF-5688 / df5688_minimal.h
โ† back to finding โ†“ download raw
/*
 * df5688_minimal.h โ€” two deterministic minimized sequences, ddmin-shrunk
 * from fuzz runs against the UNMODIFIED kernel source.
 *
 * Part A (8 ops, from seed 6: 276 -> 8): two bogus idr_remove()s of
 *   never-allocated ids (139, 136) sitting on the Fenwick chains of live
 *   allocations corrupt the cumulative 'allocated' counters.  On the hard
 *   build (live KKASSERT) the subsequent LEGIT remove(142) drives a
 *   counter negative and trips the kernel's own
 *   KKASSERT(idr_nodes[id].allocated >= 0) (linux_idr.c:197) โ€” an
 *   in-kernel panic().  On the soft build (asserts off) the same
 *   sequence leaves id 135 LIVE but unfindable (idr_find(135) == NULL):
 *   the object pointer is lost and the id is never handed out again โ€”
 *   a permanent leak of the kernel object at the drm consumer layer.
 *
 * Part B (4 ops, from seed 3: 363 -> 4): the pure DUPLICATE-ID path โ€”
 *   no counter ever goes negative, so it reproduces identically with
 *   live KKASSERT (hard build, and in-kernel via df5688_mod.c):
 *
 *   chain(526) = {526, 525, 523, ...}: allocating 526 bumps nodes[525]
 *   to 1.  idr_remove(525) โ€” 525 was NEVER allocated โ€” passes the
 *   `allocated != 0' guard and phantom-decrements nodes[525] and its
 *   chain.  The tree now undercounts block 525: idr_alloc(525, 532)
 *   hands out 525, then idr_alloc(525, 0) hands out 525 AGAIN while it
 *   is live โ€” the previous kernel object pointer is silently
 *   overwritten.
 *
 * This is exactly the DRM_IOCTL_SYNCOBJ_CREATE / SYNCOBJ_DESTROY
 * interleaving an unprivileged DRI client can drive
 * (sys/dev/drm/drm_syncobj.c:333 idr_alloc(start=1); drm_syncobj.c:369
 * feeds the raw ioctl handle straight into idr_remove).
 */
	/* ---- Part A ---- */
	{ int id = idr_alloc(&idr, (void *)0x515000000220UL, 142, 194, 0);
	  printf("alloc(142,194) -> %d\n", id);
	  if (id >= 0) { mused[id] = 1; mval[id] = (void *)0x515000000220UL; } }

	printf("remove(139) [never allocated] -> %p\n", idr_remove(&idr, 139));
	printf("remove(139) [never allocated] -> %p\n", idr_remove(&idr, 139));

	{ int id = idr_alloc(&idr, (void *)0x515000008600UL, 135, 192, 0);
	  printf("alloc(135,192) -> %d\n", id);
	  if (id >= 0) { mused[id] = 1; mval[id] = (void *)0x515000008600UL; } }

	printf("remove(136) [never allocated] -> %p\n", idr_remove(&idr, 136));

	/* hard build: KKASSERT fires inside THIS legit remove (counter
	 * goes to -1); soft build continues */
	{ void *p = idr_remove(&idr, 142);
	  printf("remove(142) [live] -> %p\n", p);
	  mused[142] = 0; mval[142] = NULL; }	/* legit: update model */

	{ int id = -1, t;
	  for (t = 0; t < 4; t++) {
	      if (idr_pre_get(&idr, 0) == 0)
		  break;
	      if (idr_get_new_above(&idr, (void *)0x515000100000UL,
				    187, &id) == 0)
		  break;
	  }
	  printf("get_new(187) -> %d\n", id);
	  if (id >= 0) { mused[id] = 1; mval[id] = (void *)0x515000100000UL; } }

	{ int id = idr_alloc(&idr, (void *)0x515000113000UL, 187, 245, 0);
	  printf("alloc(187,245) -> %d\n", id);
	  if (id >= 0) {
	      if (mused[id]) {
		  n_dup++;
		  diverged = 1;
		  printf(">>> DUPLICATE ID %d (still allocated, ptr %p "
			 "overwritten)\n", id, mval[id]);
	      } else {
		  mused[id] = 1;
		  mval[id] = (void *)0x515000113000UL;
	      }
	  } }

	/* ---- Part B ---- */
	{ int id = idr_alloc(&idr, (void *)0x5150000f6000UL, 526, 0, 0);
	  printf("alloc(526,0) -> %d\n", id);
	  if (id >= 0) { mused[id] = 1; mval[id] = (void *)0x5150000f6000UL; } }

	printf("remove(525) [never allocated] -> %p\n", idr_remove(&idr, 525));

	{ int id = idr_alloc(&idr, (void *)0x5150014700UL, 525, 532, 0);
	  printf("alloc(525,532) -> %d\n", id);
	  if (id >= 0) { mused[id] = 1; mval[id] = (void *)0x5150014700UL; } }

	{ int id = idr_alloc(&idr, (void *)0x5150016a00UL, 525, 0, 0);
	  printf("alloc(525,0) -> %d\n", id);
	  if (id >= 0) {
	      if (mused[id]) {
		  n_dup++;
		  diverged = 1;
		  printf(">>> DUPLICATE ID %d: idr_alloc returned an id "
			 "STILL ALLOCATED (ptr %p) โ€” silently overwritten "
			 "with %p\n",
			 id, mval[id], (void *)0x5150016a00UL);
	      } else {
		  mused[id] = 1;
		  mval[id] = (void *)0x5150016a00UL;
	      }
	  } }