DF-3163 / fionread_rdwr.c
/* * DF-3163 PoC: FIONREAD on an O_RDWR fifo descriptor always reports 0. * * Root cause: fifo_ioctl() (sys/vfs/fifofs/fifo_vnops.c:436-461) forwards the * ioctl to BOTH sockets when the fd is FREAD|FWRITE. For FIONREAD, * soo_ioctl() sets *(int*)data = so->so_rcv.ssb_cc * (sys/kern/sys_socket.c:152-155). The FREAD pass stores the true byte * count from the read socket; the FWRITE pass then OVERWRITES it with the * write socket's so_rcv.ssb_cc, which is always 0 (the write socket of the * internal socket pair never receives anything). */ #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(void) { const char *path = "/tmp/df3163_fifo"; int n = -1, n_ctl = -1, rw, rfd, wfd, bad = 0; unlink(path); if (mkfifo(path, 0600) < 0) { perror("mkfifo"); exit(1); } /* O_RDWR fifo open never blocks (own counters satisfy the rendezvous) */ rw = open(path, O_RDWR); rfd = open(path, O_RDONLY | O_NONBLOCK); wfd = open(path, O_WRONLY); if (rw < 0 || rfd < 0 || wfd < 0) { perror("open"); exit(1); } if (write(wfd, "hello", 5) != 5) { perror("write"); exit(1); } usleep(100000); if (ioctl(rw, FIONREAD, &n) < 0) { perror("ioctl(FIONREAD, O_RDWR fd)"); exit(1); } if (ioctl(rfd, FIONREAD, &n_ctl) < 0) { perror("ioctl(FIONREAD, O_RDONLY fd)"); exit(1); } printf("FIONREAD on O_RDWR fd = %d (expected 5)\n", n); printf("FIONREAD on O_RDONLY fd = %d (control, expected 5)\n", n_ctl); if (n == 0 && n_ctl == 5) { printf("BUG-DF-3163: O_RDWR FIONREAD clobbered by the " "write-socket pass\n"); bad = 1; } printf("VERDICT: %s\n", bad ? "reproduced" : "not_reproduced"); unlink(path); return (bad ? 0 : 1); } |