DF-5134 / leak_trigger.c
/* * leak_trigger.c -- userland PoC for DF-5134 * (dma_buf_fd() in sys/dev/drm/linux_dma-buf.c never drops the caller's * file reference after fsetfd(); Linux's fd_install() consumes it). * * Run as an UNPRIVILEGED user. hw.dmabug.* nodes are CTLFLAG_ANYBODY. * The kmod calls the real dma_buf_export()+dma_buf_fd() in our context, * which on real hardware is exactly what a render-node * DRM_IOCTL_PRIME_HANDLE_TO_FD does for an unprivileged process. */ #include <sys/types.h> #include <sys/sysctl.h> #include <stdio.h> #include <unistd.h> int main(void) { int fd = -1, val = -1; size_t l = sizeof(fd); if (sysctlbyname("hw.dmabug.mkfd", &fd, &l, NULL, 0) != 0) { perror("hw.dmabug.mkfd"); return 1; } printf("[leak_trigger] uid=%d pid=%d got dmabuf fd=%d " "(refs: 1 from dma_buf_export/falloc + 1 from fd table)\n", getuid(), getpid(), fd); printf("[leak_trigger] closing fd=%d; after this NO ONE holds the " "buffer in userland or kernel except the orphaned export ref\n", fd); close(fd); sleep(1); l = sizeof(val); if (sysctlbyname("hw.dmabug.check", &val, &l, NULL, 0) != 0) { perror("hw.dmabug.check"); return 1; } printf("[leak_trigger] check: orphan f_count=%d => %s\n", val, val >= 1 ? "LEAK: struct file + dma_buf never freed (repro)" : "no leak (freed correctly)"); return (val >= 1) ? 2 : 0; } |