From: Jens Axboe <axboe@kernel.dk>
To: Yue Sun <samsun1006219@gmail.com>, Chris Mason <clm@fb.com>,
David Sterba <dsterba@suse.com>
Cc: linux-btrfs@vger.kernel.org, io-uring@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [BUG REPORT] btrfs/io_uring: GPF in tctx_task_work_run after encoded read error completion
Date: Tue, 30 Jun 2026 13:00:06 -0600 [thread overview]
Message-ID: <8c8b9ace-dc84-46bf-8495-44bf2f2b0680@kernel.dk> (raw)
In-Reply-To: <20260630091609.3414-1-samsun1006219@gmail.com>
On 6/30/26 3:16 AM, Yue Sun wrote:
> Hello,
>
> I can reproduce a general protection fault on current upstream master by using
> IORING_OP_URING_CMD with BTRFS_IOC_ENCODED_READ on a loop-backed btrfs image
> while fail_make_request injects read errors.
>
> Summary
> -------
>
> The crash happens while io_uring is running task_work for a btrfs encoded read
> completion:
>
> tctx_task_work_run()
> mutex_lock(&ctx->uring_lock)
>
> The faulting mutex address is poisoned:
>
> RDI: dead000000001129
> KASAN: maybe wild-memory-access in range [0xdead000000001128-0xdead00000000112f]
>
> The root cause might be a double-completion/use-after-free race in the
> btrfs io_uring encoded read error path.
>
> The timing appears to be:
>
> # CPU0: userspace task issues IORING_OP_URING_CMD.
> io_uring_enter()
> btrfs_uring_cmd()
> btrfs_uring_encoded_read()
> ret = btrfs_encoded_read(...)
> if (ret == -EIOCBQUEUED)
> btrfs_uring_read_extent(..., cmd)
>
> btrfs_uring_read_extent()
> priv->cmd = cmd
> ret = btrfs_encoded_read_regular_fill_pages(..., priv)
>
> # In this helper, priv is struct btrfs_encoded_read_private.
> # uring_ctx points to the caller's struct btrfs_uring_priv.
> btrfs_encoded_read_regular_fill_pages(..., uring_ctx=priv)
> refcount_set(&priv->pending_refs, 1)
> priv->uring_ctx = uring_ctx
> refcount_inc(&priv->pending_refs)
> btrfs_submit_bbio(bbio, 0)
>
> # CPU1: the submitted bio fails quickly, before CPU0 drops its owner ref.
> btrfs_encoded_read_endio()
> WRITE_ONCE(priv->status, bbio->bio.bi_status)
> refcount_dec_and_test(&priv->pending_refs)
> # pending_refs goes 2 -> 1, so this context does not queue completion.
>
> # CPU0: btrfs_submit_bbio() has returned and the uring branch continues.
> btrfs_encoded_read_regular_fill_pages(..., uring_ctx=priv)
> if (refcount_dec_and_test(&priv->pending_refs)) {
> ret = blk_status_to_errno(READ_ONCE(priv->status))
> btrfs_uring_read_extent_endio(uring_ctx, ret)
> kfree(priv)
> return ret
> }
>
> # Here priv is the caller's struct btrfs_uring_priv.
> btrfs_uring_read_extent_endio(priv, err)
> bc->priv = priv
> io_uring_cmd_complete_in_task(priv->cmd, btrfs_uring_read_finished)
>
> # CPU0: task_work is queued, but the helper returns a normal error instead
> # of -EIOCBQUEUED, so the caller takes the synchronous failure path.
> btrfs_uring_read_extent()
> if (ret && ret != -EIOCBQUEUED)
> goto out_fail
> out_fail:
> btrfs_unlock_extent(...)
> btrfs_inode_unlock(...)
> kfree(priv)
> __free_page(...)
> kfree(pages)
> return ret
>
> # Later, the same task waits for io_uring completions and runs task_work.
> io_uring_enter()
> io_cqring_wait()
> io_run_task_work()
> task_work_run()
> tctx_task_work()
> tctx_task_work_run()
> req = container_of(node, struct io_kiocb, io_task_work.node)
> ctx = req->ctx
> mutex_lock(&ctx->uring_lock)
> # Crash: req->ctx appears poisoned/stale before
> # btrfs_uring_read_finished() is reached.
If the work is passed to task_work, then btrfs must return -EIOCBQUEUED.
Looks like a basic bug in btrfs, see below. Caveat - entirely
untested/compiled/whatever. On vacation, btrfs guys can figure this out.
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 272598f6ae77..51c06618c733 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -9460,7 +9460,6 @@ int btrfs_encoded_read_regular_fill_pages(struct btrfs_inode *inode,
ret = blk_status_to_errno(READ_ONCE(priv->status));
btrfs_uring_read_extent_endio(uring_ctx, ret);
kfree(priv);
- return ret;
}
return -EIOCBQUEUED;
--
Jens Axboe
next prev parent reply other threads:[~2026-06-30 19:00 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-30 9:16 [BUG REPORT] btrfs/io_uring: GPF in tctx_task_work_run after encoded read error completion Yue Sun
2026-06-30 19:00 ` Jens Axboe [this message]
2026-06-30 20:22 ` David Sterba
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=8c8b9ace-dc84-46bf-8495-44bf2f2b0680@kernel.dk \
--to=axboe@kernel.dk \
--cc=clm@fb.com \
--cc=dsterba@suse.com \
--cc=io-uring@vger.kernel.org \
--cc=linux-btrfs@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=samsun1006219@gmail.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox