Even though it should be safe to poke into req->link after io_issue_sqe() in terms of races, it may end up retiring a request, e.g. when someone calls io_req_complete(). It'll be placed into an internal request cache, so the memory would be valid with other guarantees, but the request will be actually dismantled and with requests linked removed and enqueued. Hence, don't forget to remove REQ_F_ARM_LTIMEOUT after a linked timeout got disarmed, otherwise following io_prep_linked_timeout() will expect req->link to be not-zero and so fault. Fixes: 19bfc9a0d26c5 ("io_uring: optimise io_prep_linked_timeout()") Reported-by: syzbot+2b85e9379c34945fe38f@xxxxxxxxxxxxxxxxxxxxxxxxx Signed-off-by: Pavel Begunkov <asml.silence@xxxxxxxxx> --- Not sure whether it fixes the syzbot report, but hopefully it'll find a repro soon. fs/io_uring.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/io_uring.c b/fs/io_uring.c index 17d0125c331a..29e3ec6e9dbf 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -1311,6 +1311,9 @@ static inline void io_unprep_linked_timeout(struct io_kiocb *req) static struct io_kiocb *__io_prep_linked_timeout(struct io_kiocb *req) { + if (WARN_ON_ONCE(!req->link)) + return NULL; + req->flags &= ~REQ_F_ARM_LTIMEOUT; req->flags |= REQ_F_LINK_TIMEOUT; @@ -1935,6 +1938,7 @@ static bool io_disarm_next(struct io_kiocb *req) if (req->flags & REQ_F_ARM_LTIMEOUT) { struct io_kiocb *link = req->link; + req->flags &= ~REQ_F_ARM_LTIMEOUT; if (link && link->opcode == IORING_OP_LINK_TIMEOUT) { io_remove_next_linked(req); io_cqring_fill_event(link->ctx, link->user_data, -- 2.32.0