From: Pavel Begunkov <asml.silence@gmail.com>
To: io-uring@vger.kernel.org
Cc: Jens Axboe <axboe@kernel.dk>
Subject: Re: [PATCH 2/2] io_uring: fix link traversal locking
Date: Fri, 26 Nov 2021 15:29:13 +0000 [thread overview]
Message-ID: <46bcbe8d-14a2-9d02-664f-fa9980d55a13@gmail.com> (raw)
In-Reply-To: <397f7ebf3f4171f1abe41f708ac1ecb5766f0b68.1637937097.git.asml.silence@gmail.com>
On 11/26/21 14:38, Pavel Begunkov wrote:
> 674ee8e1b4a41 ("io_uring: correct link-list traversal locking") fixed a
The problematic commit is marked stable, so
Cc: stable@kernel.org # 5.15+
> data race but introduced a possible deadlock and inconsistentcy in irq
> states. E.g.
>
> io_poll_remove_all()
> spin_lock_irq(timeout_lock)
> io_poll_remove_one()
> spin_lock/unlock_irq(poll_lock);
> spin_unlock_irq(timeout_lock)
>
> Another type of problem is freeing a request while holding
> ->timeout_lock, which may leads to a deadlock in
> io_commit_cqring() -> io_flush_timeouts() and other places.
>
> Having 3 nested locks is also too ugly. Add io_match_task_safe(), which
> would briefly take and release timeout_lock for race prevention inside,
> so the actuall request cancellation / free / etc. code doesn't have it
> taken.
>
> Reported-by: syzbot+ff49a3059d49b0ca0eec@syzkaller.appspotmail.com
> Reported-by: syzbot+847f02ec20a6609a328b@syzkaller.appspotmail.com
> Reported-by: syzbot+3368aadcd30425ceb53b@syzkaller.appspotmail.com
> Reported-by: syzbot+51ce8887cdef77c9ac83@syzkaller.appspotmail.com
> Reported-by: syzbot+3cb756a49d2f394a9ee3@syzkaller.appspotmail.com
> Fixes: 674ee8e1b4a41 ("io_uring: correct link-list traversal locking")
> Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
> ---
> fs/io_uring.c | 60 +++++++++++++++++++++++++++++++++++----------------
> 1 file changed, 42 insertions(+), 18 deletions(-)
>
> diff --git a/fs/io_uring.c b/fs/io_uring.c
> index 7dd112d44adf..75841b919dce 100644
> --- a/fs/io_uring.c
> +++ b/fs/io_uring.c
> @@ -1278,6 +1278,7 @@ static void io_refs_resurrect(struct percpu_ref *ref, struct completion *compl)
>
> static bool io_match_task(struct io_kiocb *head, struct task_struct *task,
> bool cancel_all)
> + __must_hold(&req->ctx->timeout_lock)
> {
> struct io_kiocb *req;
>
> @@ -1293,6 +1294,44 @@ static bool io_match_task(struct io_kiocb *head, struct task_struct *task,
> return false;
> }
>
> +static bool io_match_linked(struct io_kiocb *head)
> +{
> + struct io_kiocb *req;
> +
> + io_for_each_link(req, head) {
> + if (req->flags & REQ_F_INFLIGHT)
> + return true;
> + }
> + return false;
> +}
> +
> +/*
> + * As io_match_task() but protected against racing with linked timeouts.
> + * User must not hold timeout_lock.
> + */
> +static bool io_match_task_safe(struct io_kiocb *head, struct task_struct *task,
> + bool cancel_all)
> +{
> + bool matched;
> +
> + if (task && head->task != task)
> + return false;
> + if (cancel_all)
> + return true;
> +
> + if (head->flags & REQ_F_LINK_TIMEOUT) {
> + struct io_ring_ctx *ctx = head->ctx;
> +
> + /* protect against races with linked timeouts */
> + spin_lock_irq(&ctx->timeout_lock);
> + matched = io_match_linked(head);
> + spin_unlock_irq(&ctx->timeout_lock);
> + } else {
> + matched = io_match_linked(head);
> + }
> + return matched;
> +}
> +
> static inline bool req_has_async_data(struct io_kiocb *req)
> {
> return req->flags & REQ_F_ASYNC_DATA;
> @@ -5699,17 +5738,15 @@ static __cold bool io_poll_remove_all(struct io_ring_ctx *ctx,
> int posted = 0, i;
>
> spin_lock(&ctx->completion_lock);
> - spin_lock_irq(&ctx->timeout_lock);
> for (i = 0; i < (1U << ctx->cancel_hash_bits); i++) {
> struct hlist_head *list;
>
> list = &ctx->cancel_hash[i];
> hlist_for_each_entry_safe(req, tmp, list, hash_node) {
> - if (io_match_task(req, tsk, cancel_all))
> + if (io_match_task_safe(req, tsk, cancel_all))
> posted += io_poll_remove_one(req);
> }
> }
> - spin_unlock_irq(&ctx->timeout_lock);
> spin_unlock(&ctx->completion_lock);
>
> if (posted)
> @@ -9565,19 +9602,8 @@ static bool io_cancel_task_cb(struct io_wq_work *work, void *data)
> {
> struct io_kiocb *req = container_of(work, struct io_kiocb, work);
> struct io_task_cancel *cancel = data;
> - bool ret;
>
> - if (!cancel->all && (req->flags & REQ_F_LINK_TIMEOUT)) {
> - struct io_ring_ctx *ctx = req->ctx;
> -
> - /* protect against races with linked timeouts */
> - spin_lock_irq(&ctx->timeout_lock);
> - ret = io_match_task(req, cancel->task, cancel->all);
> - spin_unlock_irq(&ctx->timeout_lock);
> - } else {
> - ret = io_match_task(req, cancel->task, cancel->all);
> - }
> - return ret;
> + return io_match_task_safe(req, cancel->task, cancel->all);
> }
>
> static __cold bool io_cancel_defer_files(struct io_ring_ctx *ctx,
> @@ -9588,14 +9614,12 @@ static __cold bool io_cancel_defer_files(struct io_ring_ctx *ctx,
> LIST_HEAD(list);
>
> spin_lock(&ctx->completion_lock);
> - spin_lock_irq(&ctx->timeout_lock);
> list_for_each_entry_reverse(de, &ctx->defer_list, list) {
> - if (io_match_task(de->req, task, cancel_all)) {
> + if (io_match_task_safe(de->req, task, cancel_all)) {
> list_cut_position(&list, &ctx->defer_list, &de->list);
> break;
> }
> }
> - spin_unlock_irq(&ctx->timeout_lock);
> spin_unlock(&ctx->completion_lock);
> if (list_empty(&list))
> return false;
>
--
Pavel Begunkov
next prev parent reply other threads:[~2021-11-26 15:37 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-11-26 14:38 [PATCH 0/2] 5.16 fixes for syz reports Pavel Begunkov
2021-11-26 14:38 ` [PATCH 1/2] io_uring: fail cancellation for EXITING tasks Pavel Begunkov
2021-11-26 15:32 ` Pavel Begunkov
2021-11-26 14:38 ` [PATCH 2/2] io_uring: fix link traversal locking Pavel Begunkov
2021-11-26 15:29 ` Pavel Begunkov [this message]
2021-11-26 15:36 ` [PATCH 0/2] 5.16 fixes for syz reports Jens Axboe
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=46bcbe8d-14a2-9d02-664f-fa9980d55a13@gmail.com \
--to=asml.silence@gmail.com \
--cc=axboe@kernel.dk \
--cc=io-uring@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.