Linux Btrfs filesystem development
 help / color / mirror / Atom feed
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

  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