Re: [BUG REPORT] btrfs/io_uring: GPF in tctx_task_work_run after encoded read error completion

Jens Axboe <[email protected]>
Newsgroups org.kernel.vger.io-uring,org.kernel.vger.linux-btrfs,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.