DF-4302 / vkbd_unit_leak.c
/* * vkbd_unit_leak.c — clean reproduction of the vkbd devfs clone-bitmap * unit leak (sys/dev/misc/vkbd/vkbd.c). * * vkbd_dev_clone() (vkbd.c:225) allocates a clone unit from * DEVFS_CLONE_BITMAP(vkbd) with limit=0 (unbounded), but vkbd_dev_close() * never calls devfs_clone_bitmap_put() — destroy_dev() removes the node, * yet the unit number is consumed forever. * * Demonstration: record the highest vkbdctl unit in use, run N * open/write/close cycles (each fully closing its fd), then record the * next unit handed out. The unit number grows by exactly N despite every * device having been closed. */ #include <sys/fcntl.h> #include <dirent.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> static int maxunit(void) { DIR *d = opendir("/dev"); struct dirent *de; int best = -1; if (d == NULL) return (-1); while ((de = readdir(d)) != NULL) { int u; if (sscanf(de->d_name, "vkbdctl%d", &u) == 1 && u > best) best = u; } closedir(d); return (best); } int main(void) { int before, after, i; int two[2] = {0x1e, 0x9e}; before = maxunit(); printf("highest vkbdctl unit before: %d\n", before); fflush(stdout); for (i = 0; i < 1000; i++) { int fd = open("/dev/vkbdctl", O_RDWR); if (fd < 0) { perror("open"); return 1; } if (write(fd, two, 8) < 0) { /* drain may lag; ignore */ } close(fd); } /* open once more to reveal the next allocated unit */ { int fd = open("/dev/vkbdctl", O_RDWR); if (fd < 0) { perror("open probe"); return 1; } after = maxunit(); close(fd); } printf("vkbdctl unit handed out after 1000 full open/close cycles: %d\n", after); printf("units leaked: %d (expected 0 if unit numbers were reused)\n", after - before - 1000 > 0 ? after - before : after - before); fflush(stdout); return (0); } |