DF-4378 / chk_drm.c
/* * DF-4378 reachability probe — drm_drv.c minor/devnode teardown leak. * * The bug is entirely kernel-side: drm_sysfs_minor_alloc() (drm_sysfs.c:97) * make_dev()s "dri/cardN" and throws the cdev away; drm_minor_free() * (drm_drv.c:176) kfree()s the drm_minor without destroy_dev()ing the node * and without freeing minor->kdev (put_device is #if 0). Nothing in * sys/dev/drm ever calls destroy_dev. Each driver attach/detach (or module * load/unload) cycle therefore leaks one cdev + one struct device per minor * and leaves a stale /dev/dri/cardN whose si_drv1 dangles at the freed * drm_device. * * This probe documents the userland-observable symptom set: * 1. presence/absence of /dev/dri on this machine, * 2. whether a DRM driver is even loaded (kldstat), * 3. (on a machine with a GPU) whether /dev/dri/cardN entries persist * after `kldunload` of the display driver — the stale-node symptom. * * Expected on the audit QEMU guest: no /dev/dri, no drm module -> exit 2. */ #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> int main(void) { DIR *d; struct dirent *de; int fd, found = 0; d = opendir("/dev/dri"); if (d == NULL) { printf("DF-4378: /dev/dri absent: no DRM driver attached on " "this machine\n"); return (2); } while ((de = readdir(d)) != NULL) { if (de->d_name[0] == '.') continue; printf("DF-4378: /dev/dri/%s present\n", de->d_name); found++; } closedir(d); if (!found) return (2); fd = open("/dev/dri/card0", O_RDWR); if (fd < 0) printf("DF-4378: card0 open: %s\n", strerror(errno)); else { printf("DF-4378: card0 opened; leak check = kldunload/" "kldload cycle then compare `vmstat -m` M_DRM lines and " "ls /dev/dri\n"); close(fd); } return (0); } |