On Thu, Jul 9, 2026 at 2:11 PM Xiang Mei <[email protected]> wrote:
>
> The fuse-io-uring transport imports each ring entry's payload buffer at
> ring->max_payload_sz and bounds both copy directions against that value,
> ignoring the buffer length the server actually registered.  Both the
> server-supplied reply payload_sz (fuse_uring_copy_from_ring) and an
> oversized request payload such as a large FUSE_SETXATTR value
> (fuse_uring_args_to_ring) can then overrun the imported iterator and hit
> fuse_copy_fill()'s BUG_ON(!err):
>
>   kernel BUG at fs/fuse/dev.c:1053!
>   Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
>   RIP: 0010:fuse_copy_fill (fs/fuse/dev.c:1022)
>   Call Trace:
>    fuse_copy_args (fs/fuse/dev.c:1329 fs/fuse/dev.c:1351)
>    fuse_uring_copy_from_ring (fs/fuse/dev_uring.c:686)
>    fuse_uring_cmd (fs/fuse/dev_uring.c:1226)
>    io_uring_cmd (io_uring/uring_cmd.c:271)
>    __io_issue_sqe (io_uring/io_uring.c:1395)
>    io_issue_sqe (io_uring/io_uring.c:1418)
>    io_submit_sqes (io_uring/io_uring.c:1649 io_uring/io_uring.c:1934 
> io_uring/io_uring.c:2057)
>    __do_sys_io_uring_enter (io_uring/io_uring.c:2646)
>    do_syscall_64 (arch/x86/entry/syscall_64.c:63 
> arch/x86/entry/syscall_64.c:94)
>    entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
>
> The request path overruns the same way, via fuse_copy_args() ->
> fuse_uring_args_to_ring().
>
> Store the registered payload length (payload->iov_len) in the ring entry
> and use it for the import and both bounds checks, so the buffer the
> server provided is honoured and an oversized reply/request is rejected
> (-EINVAL for a reply, and -E2BIG/-EIO for a request, matching
> fuse_dev_do_read()) instead of panicking.
>
> Fixes: c090c8abae4b ("fuse: Add io-uring sqe commit and fetch support")
> Cc: [email protected]
> Reported-by: Weiming Shi <[email protected]>
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Xiang Mei <[email protected]>
> ---
> v3: propose the patch fixing another issue found by Bernd by Joanne suggested 
> way
>
>  fs/fuse/dev_uring.c   | 9 ++++++++-
>  fs/fuse/dev_uring_i.h | 1 +
>  2 files changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c
> index 0814681eb04b..248e5a3e340e 100644
> --- a/fs/fuse/dev_uring.c
> +++ b/fs/fuse/dev_uring.c
> @@ -650,7 +650,7 @@ static int setup_fuse_copy_state(struct fuse_copy_state 
> *cs,
>  {
>         int err;
>
> -       err = import_ubuf(dir, ent->payload, ring->max_payload_sz, iter);
> +       err = import_ubuf(dir, ent->payload, ent->payload_sz, iter);
>         if (err) {
>                 pr_info_ratelimited("fuse: Import of user buffer failed\n");
>                 return err;
> @@ -679,6 +679,9 @@ static int fuse_uring_copy_from_ring(struct fuse_ring 
> *ring,
>         if (err)
>                 return err;
>
> +       if (ring_in_out.payload_sz > ent->payload_sz)
> +               return -EINVAL;
> +
>         err = setup_fuse_copy_state(&cs, ring, req, ent, ITER_SOURCE, &iter);
>         if (err)
>                 return err;
> @@ -725,6 +728,9 @@ static int fuse_uring_args_to_ring(struct fuse_ring 
> *ring, struct fuse_req *req,
>                 num_args--;
>         }
>
> +       if (fuse_len_args(num_args, (struct fuse_arg *)in_args) > 
> ent->payload_sz)
> +               return args->opcode == FUSE_SETXATTR ? -E2BIG : -EIO;

Reviewed-by: Joanne Koong <[email protected]>

Not saying you have to do this, but if you wanted to, I think it'd be
nice to have a separate cleanup patch that deduplicates this setxattr
special casing logic between the /dev/fuse path and here.

Thanks,
Joanne

Reply via email to