⬢ DragonFlyBSD Kernel Audit
DF-5134 / leak_trigger.c
← back to finding ↓ download raw
/*
 * 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;
}