Skip to content

Commit 900fad4

Browse files
isilenceaxboe
authored andcommitted
io_uring: fix racy REQ_F_LINK_TIMEOUT clearing
io_link_timeout_fn() removes REQ_F_LINK_TIMEOUT from the link head's flags, it's not atomic and may race with what the head is doing. If io_link_timeout_fn() doesn't clear the flag, as forced by this patch, then it may happen that for "req -> link_timeout1 -> link_timeout2", __io_kill_linked_timeout() would find link_timeout2 and try to cancel it, so miscounting references. Teach it to ignore such double timeouts by marking the active one with a new flag in io_prep_linked_timeout(). Signed-off-by: Pavel Begunkov <asml.silence@gmail.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent 4d52f33 commit 900fad4

1 file changed

Lines changed: 13 additions & 4 deletions

File tree

fs/io_uring.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@ enum {
585585
REQ_F_BUFFER_SELECTED_BIT,
586586
REQ_F_NO_FILE_TABLE_BIT,
587587
REQ_F_WORK_INITIALIZED_BIT,
588+
REQ_F_LTIMEOUT_ACTIVE_BIT,
588589

589590
/* not a real bit, just to check we're not overflowing the space */
590591
__REQ_F_LAST_BIT,
@@ -614,7 +615,7 @@ enum {
614615
REQ_F_CUR_POS = BIT(REQ_F_CUR_POS_BIT),
615616
/* must not punt to workers */
616617
REQ_F_NOWAIT = BIT(REQ_F_NOWAIT_BIT),
617-
/* has linked timeout */
618+
/* has or had linked timeout */
618619
REQ_F_LINK_TIMEOUT = BIT(REQ_F_LINK_TIMEOUT_BIT),
619620
/* regular file */
620621
REQ_F_ISREG = BIT(REQ_F_ISREG_BIT),
@@ -628,6 +629,8 @@ enum {
628629
REQ_F_NO_FILE_TABLE = BIT(REQ_F_NO_FILE_TABLE_BIT),
629630
/* io_wq_work is initialized */
630631
REQ_F_WORK_INITIALIZED = BIT(REQ_F_WORK_INITIALIZED_BIT),
632+
/* linked timeout is active, i.e. prepared by link's head */
633+
REQ_F_LTIMEOUT_ACTIVE = BIT(REQ_F_LTIMEOUT_ACTIVE_BIT),
631634
};
632635

633636
struct async_poll {
@@ -1871,6 +1874,12 @@ static bool __io_kill_linked_timeout(struct io_kiocb *req)
18711874
link = list_first_entry(&req->link_list, struct io_kiocb, link_list);
18721875
if (link->opcode != IORING_OP_LINK_TIMEOUT)
18731876
return false;
1877+
/*
1878+
* Can happen if a linked timeout fired and link had been like
1879+
* req -> link t-out -> link t-out [-> ...]
1880+
*/
1881+
if (!(link->flags & REQ_F_LTIMEOUT_ACTIVE))
1882+
return false;
18741883

18751884
list_del_init(&link->link_list);
18761885
wake_ev = io_link_cancel_timeout(link);
@@ -6106,10 +6115,9 @@ static enum hrtimer_restart io_link_timeout_fn(struct hrtimer *timer)
61066115
if (!list_empty(&req->link_list)) {
61076116
prev = list_entry(req->link_list.prev, struct io_kiocb,
61086117
link_list);
6109-
if (refcount_inc_not_zero(&prev->refs)) {
6118+
if (refcount_inc_not_zero(&prev->refs))
61106119
list_del_init(&req->link_list);
6111-
prev->flags &= ~REQ_F_LINK_TIMEOUT;
6112-
} else
6120+
else
61136121
prev = NULL;
61146122
}
61156123

@@ -6166,6 +6174,7 @@ static struct io_kiocb *io_prep_linked_timeout(struct io_kiocb *req)
61666174
if (!nxt || nxt->opcode != IORING_OP_LINK_TIMEOUT)
61676175
return NULL;
61686176

6177+
nxt->flags |= REQ_F_LTIMEOUT_ACTIVE;
61696178
req->flags |= REQ_F_LINK_TIMEOUT;
61706179
return nxt;
61716180
}

0 commit comments

Comments
 (0)