โฌข DragonFlyBSD Kernel Audit
DF-4820 / harness_main.c
โ† back to finding โ†“ download raw
/*
 * DF-4820 main: drive the verbatim radeon_ring_restore() kernel body
 * (extracted mechanically from the kernel tree by build.sh) in the two
 * kernel-proven error modes, and measure leaked allocations.
 *
 * Each iteration simulates one GPU reset cycle:
 *   radeon_ring_backup()  -> kvmalloc_array(size)     [tracked]
 *   radeon_ring_restore() -> early `return r` w/o kvfree  [the bug]
 *   caller ignores retval (radeon_device.c:1881-1882)
 */
/* NOTE: no #include here โ€” build.sh concatenates harness_env.h + the
 * verbatim kernel body + this file into a single translation unit. */

int main(int argc, char **argv)
{
	struct radeon_device rdev = { 0 };
	struct radeon_ring ring = {
		.ring_size = 64 * 1024,		/* typical CP ring */
		.align_mask = 0xf,
		.ptr_mask = (64 * 1024 / 4) - 1,
		.ring_free_dw = 64 * 1024 / 4,
	};
	/* saturated-ring backup size: ring 100% full minus 1 dword
	 * (radeon_ring_alloc guarantees >= 1 spare dw; commit padding can
	 * bring free_dw to the align boundary โ€” 16383 is the max pending) */
	unsigned size = (ring.ring_size / 4) - 1;
	int iters = argc > 1 ? atoi(argv[1]) : 1000;
	int mode = argc > 2 ? atoi(argv[2]) : 1;	/* 1=-EDEADLK 2=-ENOENT */
	int i, r, failed = 0;

	block_bytes = (unsigned long)size * sizeof(uint32_t);
	lock_err = (mode == 2) ? -ENOENT : -EDEADLK;

	printf("DF-4820 radeon_ring_restore() backup-buffer leak harness\n");
	printf("ring_size=%u dw=%u align_mask=0x%x backup_size=%u dw (%lu B)\n",
	       ring.ring_size, ring.ring_size / 4, ring.align_mask,
	       size, block_bytes);
	printf("injected radeon_ring_lock error: %d (%s)\n", lock_err,
	       mode == 2 ? "-ENOENT (no pending fences)" :
			   "-EDEADLK (needs_reset still true during restore)");
	printf("iterations: %d%s\n\n", iters,
#ifdef FIXED
	       "  [FIXED build: kvfree(data) added on error path]"
#else
	       "  [BASELINE build: stock kernel body]"
#endif
	      );

	for (i = 0; i < iters; i++) {
		uint32_t *data = kvmalloc_array(size, sizeof(uint32_t), 0);
		if (!data) {
			fprintf(stderr, "oom at iter %d\n", i);
			return 2;
		}
		/* radeon_device.c:1881: return value ignored */
		r = radeon_ring_restore(&rdev, &ring, size, data);
		if (r)
			failed++;
		if (i == 0)
			printf("  iter 0: radeon_ring_restore -> %d\n", r);
	}

	printf("\ntotal restore calls : %d\n", iters);
	printf("returned error      : %d\n", failed);
	printf("allocations made    : %lu (%lu bytes)\n",
	       total_allocs, total_allocs * block_bytes);
	printf("allocations freed   : %lu\n", total_allocs - live_allocs);
	printf("LIVE (leaked) blocks: %lu  (%lu bytes = %lu KiB)\n",
	       live_allocs, live_allocs * block_bytes,
	       live_allocs * block_bytes / 1024);

#ifdef FIXED
	if (live_allocs != 0) {
		printf("RESULT: FIXED build still leaks โ€” FAILURE\n");
		return 1;
	}
	printf("RESULT: FIXED build leaks nothing โ€” fix VALIDATED\n");
#else
	if (live_allocs == (unsigned long)iters && failed == iters) {
		printf("RESULT: LEAK REPRODUCED โ€” every failed restore leaks "
		       "its %lu-byte kvmalloc backup buffer\n", block_bytes);
		return 0;
	}
	if (live_allocs == 0 && failed == 0) {
		printf("RESULT: no failure injected โ€” harness misconfigured\n");
		return 2;
	}
	printf("RESULT: unexpected state (live=%lu failed=%d)\n",
	       live_allocs, failed);
	return 2;
#endif
}