DF-4749 / nmdm_probe.c
/* * DF-4749 / DF-4750 PoC — DragonFlyBSD nmdm(4) null-modem driver logic bugs. * * Run as an UNPRIVILEGED user with /dev/nmdm0A,B at 0666 (make_dev default * in nmdminit(), sys/dev/misc/nmdm/nmdm.c:127-130). * * TEST 1 (DF-4749): pt_prison is never assigned in nmdmopen() * (sys/dev/misc/nmdm/nmdm.c:211: `else if (pti->pt_prison != * ap->a_cred->cr_prison) return EBUSY;`). pt_prison is kmalloc'd * M_ZERO -> stays NULL forever (grep: only init sites are lines 88/211). * For an unjailed process cr_prison == &prison0 != NULL, so the check * `NULL != &prison0` is ALWAYS true -> any second open() of an * already-open half returns EBUSY. FreeBSD's pty heritage sets * `pti->pt_prison = ap->a_cred->cr_prison` on first open; DFly's nmdm * dropped that assignment. Expected correct behavior: reopen by a * same-prison process succeeds. * * TEST 2 (DF-4750): TIOCCDTR handler at nmdm.c:564-566 does * `ourpart->modemsignals &= TIOCM_DTR;` (missing ~) which KEEPS DTR and * clears every other signal bit. Expected: clears DTR only, leaving * RTS/LE intact. */ #include <sys/types.h> #include <sys/ioctl.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <stdio.h> #include <string.h> int main(void) { int fd1, fd2, r, sig; printf("== DF-4749: reopen of an open nmdm half ==\n"); fd1 = open("/dev/nmdm0A", O_RDWR | O_NONBLOCK); if (fd1 < 0) { printf("FAIL setup: open#1(/dev/nmdm0A): %s\n", strerror(errno)); return (1); } printf("open#1(/dev/nmdm0A) = %d [ok, TS_ISOPEN set]\n", fd1); fd2 = open("/dev/nmdm0A", O_RDWR | O_NONBLOCK); if (fd2 < 0) printf("open#2(/dev/nmdm0A) = -1 errno=%d (%s)" " <-- BUG: unjailed reopen must succeed\n", errno, strerror(errno)); else printf("open#2(/dev/nmdm0A) = %d [correct: reopen allowed]\n", fd2); if (fd2 >= 0) close(fd2); close(fd1); fd2 = open("/dev/nmdm0A", O_RDWR | O_NONBLOCK); printf("open#3 after close(#1) = %d [%s: fresh first-open works," " proves EBUSY above came from the pt_prison check]\n", fd2, fd2 >= 0 ? "ok" : strerror(errno)); if (fd2 >= 0) close(fd2); printf("\n== DF-4750: TIOCCDTR inverted mask ==\n"); fd1 = open("/dev/nmdm0A", O_RDWR | O_NONBLOCK); if (fd1 < 0) { printf("FAIL setup: %s\n", strerror(errno)); return (1); } sig = TIOCM_LE | TIOCM_DTR | TIOCM_RTS; r = ioctl(fd1, TIOCMSET, &sig); printf("TIOCMSET(LE|DTR|RTS) = %d\n", r); r = ioctl(fd1, TIOCMGET, &sig); printf("TIOCMGET after SET = 0x%04x (LE=%d DTR=%d RTS=%d)\n", sig, !!(sig & TIOCM_LE), !!(sig & TIOCM_DTR), !!(sig & TIOCM_RTS)); r = ioctl(fd1, TIOCCDTR); printf("TIOCCDTR = %d\n", r); r = ioctl(fd1, TIOCMGET, &sig); printf("TIOCMGET after TIOCCDTR = 0x%04x -> DTR=%s RTS=%s\n", sig, (sig & TIOCM_DTR) ? "SET <-- BUG: must be cleared" : "clear [correct]", (sig & TIOCM_RTS) ? "SET [correct]" : "clear <-- BUG: must stay set"); close(fd1); return (0); } |