DF-3162 / revoke_fifo_noop.c
/* * DF-3162 PoC: revoke(2) on a FIFO is a silent no-op on DragonFlyBSD. * * Root cause: vrevoke() forwards non-VCHR vnodes to fdrevoke(vp, DTYPE_VNODE) * (sys/kern/vfs_subr.c:1450-1452), but fifo file pointers are * created with f_type = DTYPE_FIFO by vop_stdopen() * (sys/kern/vfs_default.c:1172-1175), and fdrevoke's matcher * compares f_type (sys/kern/kern_descrip.c:1999). The match * never succeeds, so no fp is marked FREVOKED and no descriptor * is closed. * * The fifo is owned by the invoking user, so revoke(2) passes the * ownership/caps check (sys/kern/vfs_syscalls.c:4741-4742) and returns 0. * * Expected (bug present): revoke() returns 0 and the previously-opened * descriptors STILL PASS DATA. * Expected (fixed): descriptors are revoked - write() fails * (EPIPE/EBADF...), read() does not see data. */ #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/df3162_fifo"; char buf[8]; ssize_t n; int rfd, wfd, unrevoked = 0; unlink(path); if (mkfifo(path, 0600) < 0) { perror("mkfifo"); exit(1); } rfd = open(path, O_RDONLY | O_NONBLOCK); wfd = open(path, O_WRONLY); if (rfd < 0 || wfd < 0) { perror("open"); exit(1); } printf("fifo opened: rfd=%d wfd=%d (uid=%d)\n", rfd, wfd, getuid()); if (revoke(path) < 0) { perror("revoke"); exit(1); } printf("revoke(%s) returned 0 (owner is allowed to revoke)\n", path); usleep(200000); /* give any async revocation machinery a chance */ if (write(wfd, "X", 1) == 1) { n = read(rfd, buf, sizeof(buf)); if (n == 1 && buf[0] == 'X') { printf("BUG-DF-3162: fds still functional after revoke(2) " "(data rfd<->wfd still flows)\n"); unrevoked = 1; } else { printf("PARTIAL: write ok, read n=%zd errno=%s\n", n, strerror(errno)); } } else { printf("OK: write after revoke failed: %s\n", strerror(errno)); } unlink(path); if (unrevoked) printf("VERDICT: reproduced\n"); else printf("VERDICT: not_reproduced\n"); return (unrevoked ? 0 : 1); } |