DF-2556 / leak_mod.c
/* * DF-2556 diagnostic module — directly exercises clist_alloc_cblocks() with * alternating sizes to confirm the c_data leak at the function level. * * On MOD_LOAD it runs N resize cycles on a private clist, each cycle calling * clist_alloc_cblocks(SIZE_A) then clist_alloc_cblocks(SIZE_B). With the bug, * each resize leaks the previous c_data M_TTYS buffer (never freed). We print * M_TTYS usage before and after via the kmem stats so growth is observable. * * Build: see build_mod.sh. Run (root): kldload ./leak_mod.ko */ #include <sys/param.h> #include <sys/module.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/malloc.h> #include <sys/tty.h> static MALLOC_DEFINE(M_DF2556T, "df2556t", "DF2556 leak test marker"); static int leak_mod_load(module_t mod, int what, void *arg) { struct clist cl; int i, n; const int sizeA = 1024; /* shorts */ const int sizeB = 16384;/* shorts; different bucket -> forces realloc */ switch (what) { case MOD_LOAD: /* prime: allocate an initial buffer so the first resize has an old one */ bzero(&cl, sizeof(cl)); clist_alloc_cblocks(&cl, sizeA); n = 20000; kprintf("DF2556: running %d clist resize cycles (sizeA=%d sizeB=%d)\n", n, sizeA, sizeB); for (i = 0; i < n; i++) { /* alternate sizes; each change leaks the previous c_data */ clist_alloc_cblocks(&cl, (i & 1) ? sizeB : sizeA); } kprintf("DF2556: done. On the buggy kernel ~%d M_TTYS buffers were " "leaked (never kfree'd); check 'vmstat -m | grep ttys'.\n", n); clist_free_cblocks(&cl); /* frees only the last buffer */ return 0; case MOD_UNLOAD: return 0; } return 0; } static moduledata_t leak_mod = { "df2556_leak", leak_mod_load, NULL }; DECLARE_MODULE(df2556_leak, leak_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); /* clist_alloc_cblocks() is built into the kernel; no MODULE_DEPEND needed */ |