DF-4820 / harness_main.c
/* * 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 } |