#include "../config-host.h" /* SPDX-License-Identifier: MIT */ /* * Description: test for a struct file reference leak in IORING_OP_NOP. * * When an SQE carries IOSQE_FIXED_FILE (which sets REQ_F_FIXED_FILE at * prep time) together with IORING_NOP_FILE but *without* * IORING_NOP_FIXED_FILE, io_nop() takes a normal file reference via * io_file_get_normal(), but io_put_file() won't drop the ref as * REQ_F_FIXED_FILE is set. * * Reported-by: syzbot+2cd473471e77bda12b0e@syzkaller.appspotmail.com * Fixes: a85f31052bce ("io_uring/nop: add support to fixed and normal files") */ #include #include #include #include #include #include #include "liburing.h" #include "helpers.h" #ifndef IORING_NOP_FILE #define IORING_NOP_FILE (1U << 1) #endif #ifndef IORING_NOP_FIXED_FILE #define IORING_NOP_FIXED_FILE (1U << 2) #endif int main(int argc, char *argv[]) { struct io_uring ring; struct io_uring_sqe *sqe; struct io_uring_cqe *cqe; int pipefd[2]; char c = 'x'; ssize_t n; int ret; if (argc > 1) return T_EXIT_SKIP; /* don't die on the EPIPE write in the non-buggy case */ signal(SIGPIPE, SIG_IGN); ret = io_uring_queue_init(8, &ring, 0); if (ret) { fprintf(stderr, "queue_init: %d\n", ret); return T_EXIT_FAIL; } if (pipe(pipefd) < 0) { perror("pipe"); return T_EXIT_FAIL; } sqe = io_uring_get_sqe(&ring); io_uring_prep_nop(sqe); sqe->nop_flags = IORING_NOP_FILE; sqe->fd = pipefd[0]; sqe->flags |= IOSQE_FIXED_FILE; ret = io_uring_submit(&ring); if (ret != 1) { fprintf(stderr, "submit: %d\n", ret); return T_EXIT_FAIL; } ret = io_uring_wait_cqe(&ring, &cqe); if (ret) { fprintf(stderr, "wait_cqe: %d\n", ret); return T_EXIT_FAIL; } io_uring_cqe_seen(&ring, cqe); /* * Drop our own reference on the read end. If the kernel leaked the * NOP's reference, the read end stays alive and the write below * succeeds. If correct, the read end is gone and we get -EPIPE. */ close(pipefd[0]); n = write(pipefd[1], &c, 1); if (n == 1) { fprintf(stderr, "BUG: write to pipe with closed read end " "succeeded -- NOP leaked a file reference\n"); ret = T_EXIT_FAIL; } else if (n < 0 && errno == EPIPE) { ret = T_EXIT_PASS; } else { fprintf(stderr, "unexpected write result: n=%zd errno=%d\n", n, errno); ret = T_EXIT_FAIL; } close(pipefd[1]); io_uring_queue_exit(&ring); return ret; }