DF-2770 / pty_pkt.c
/* DF-2770: ptcread TIOCPKT_IOCTL path discards the uiomove error. * * Phase A (sanity): slave tcsetattr -> master full-buffer read returns * control byte 0x40 (TIOCPKT_IOCTL) + termios bytes. * Phase B (test): second tcsetattr -> master reads 2 bytes into * [1 valid byte][1 PROT_NONE byte]: ureadc(0x40) succeeds, then * uiomove(termios,1) faults -> error swallowed, pt_send consumed. * expect (vulnerable): read() == 1, errno 0, only control byte * delivered, termios event lost. * expect (patched): read() == -1, errno 14 EFAULT (retryable). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <termios.h> #include <signal.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/mman.h> static void slave_driver(int sfd) { struct termios tio; int ext = 1, i; /* EXTPROC on: TIOCSETA now queues TIOCPKT_IOCTL packets */ ioctl(sfd, TIOCEXT, &ext); for (i = 0; i < 2; i++) { if (tcgetattr(sfd, &tio) == 0) { tio.c_cc[VMIN] = 1 + i; tcsetattr(sfd, TCSANOW, &tio); } sleep(2); } sleep(2); _exit(0); } int main(void) { int mfd, sfd, rv, st; char good[128]; unsigned char *twopg, *buf; pid_t pid; int pkt = 1; mfd = posix_openpt(O_RDWR | O_NOCTTY); if (mfd < 0) { perror("posix_openpt"); return 1; } if (grantpt(mfd) || unlockpt(mfd)) { perror("grantpt"); return 1; } if (ioctl(mfd, TIOCPKT, &pkt) < 0) { perror("TIOCPKT"); return 1; } pid = fork(); if (pid == 0) { sfd = open(ptsname(mfd), O_RDWR | O_NOCTTY); if (sfd < 0) _exit(1); slave_driver(sfd); } sleep(1); /* Phase A: good-buffer read of the first TIOCPKT_IOCTL event */ memset(good, 0, sizeof(good)); errno = 0; rv = read(mfd, good, sizeof(good)); printf("DF2770 A: full read rv=%d errno=%d ctrl=0x%02x " "(expect 0x40) bytes1..4=%02x %02x %02x %02x\n", rv, errno, rv > 0 ? (unsigned char)good[0] : 0, rv > 4 ? (unsigned char)good[1] : 0, rv > 4 ? (unsigned char)good[2] : 0, rv > 4 ? (unsigned char)good[3] : 0, rv > 4 ? (unsigned char)good[4] : 0); /* Phase B: faulting read of the second TIOCPKT_IOCTL event */ twopg = mmap(NULL, 8192, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (twopg == MAP_FAILED) { perror("mmap"); return 1; } if (mprotect(twopg + 4096, 4096, PROT_NONE) < 0) { perror("mprotect"); return 1; } buf = twopg + 4096 - 1; buf[0] = 0xAA; alarm(6); errno = 0; rv = read(mfd, buf, 2); printf("DF2770 B: faulting read(2) rv=%d errno=%d (%s)\n", rv, errno, strerror(errno)); printf("DF2770 B: byte0=0x%02x (0x40=event byte copied before " "EFAULT swallow; 0xAA=nothing copied)\n", buf[0]); kill(pid, SIGKILL); waitpid(pid, &st, 0); return 0; } |