/*
 * DF-0207 harness (kernel module) - directly exercises the bug.
 *
 * clist_alloc_cblocks() (sys/kern/tty_subr.c:48) leaks the old c_data
 * buffer on every resize.  This module allocates a private clist and
 * resizes it repeatedly, so each clist_alloc_cblocks() call with a new
 * ccmax orphans the previous kmalloc()'d buffer.  We measure the leak
 * with `vmstat -m | grep ttys` (M_TTYS MemUse / Requests) before and
 * after kldload.
 *
 * Build (in /root/df0207_kld):  make
 * Load (root):                  kldload ./df0207_harness.ko
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/tty.h>

static int
df0207_modevent(module_t mod, int type, void *data)
{
	struct clist cl;
	int i;

	switch (type) {
	case MOD_LOAD:
		bzero(&cl, sizeof(cl));
		/* initial allocation: ccmax 0 -> 1024 */
		clist_alloc_cblocks(&cl, 1024);
		/* every subsequent resize leaks the previous buffer */
		for (i = 0; i < 3000; i++) {
			clist_alloc_cblocks(&cl, 4096); /* leaks 1024/prev */
			clist_alloc_cblocks(&cl, 1024); /* leaks 4096 */
		}
		/* frees only the LAST live buffer (1024); the other ~3000
		 * are permanently orphaned -> M_TTYS leak. */
		clist_free_cblocks(&cl);
		kprintf("DF0207 harness: 3000 leak iterations done; "
			"~3000 c_data buffers orphaned in M_TTYS\n");
		return (0);
	case MOD_UNLOAD:
		return (0);
	default:
		return (EOPNOTSUPP);
	}
}

static moduledata_t df0207_mod = { "df0207", df0207_modevent, NULL };
DECLARE_MODULE(df0207, df0207_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
MODULE_VERSION(df0207, 1);
