The fuse-io-uring transport copies req->in.h out to the ring in
fuse_uring_copy_to_ring() and req->out.h back in fuse_uring_commit().
Both headers live inside the fuse_request slab object, whose cache
(fuse_req_cachep) is created without a usercopy whitelist, so copying
them directly to/from userspace trips CONFIG_HARDENED_USERCOPY and
panics:

  usercopy: Kernel memory exposure attempt detected from SLUB object
  'fuse_request' (offset 56, size 40)!
  kernel BUG at mm/usercopy.c:102!
  RIP: 0010:usercopy_abort+0x6c/0x80
  Call Trace:
   __check_heap_object
   __check_object_size
   copy_header_to_ring          fs/fuse/dev_uring.c:618
   fuse_uring_prepare_send
   fuse_uring_send_in_task
   ...
   __do_sys_io_uring_enter
   entry_SYSCALL_64_after_hwframe

Bounce both headers through an on-stack copy so the usercopy touches
stack memory, not the slab object.

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]>
Reviewed-by: Bernd Schubert <[email protected]>
Reviewed-by: Joanne Koong <[email protected]>
---
v3: no context change; add Bernd's Reviewed-by

 fs/fuse/dev_uring.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c
index 77c8cec43d9c..0814681eb04b 100644
--- a/fs/fuse/dev_uring.c
+++ b/fs/fuse/dev_uring.c
@@ -744,6 +744,7 @@ static int fuse_uring_copy_to_ring(struct fuse_ring_ent 
*ent,
 {
        struct fuse_ring_queue *queue = ent->queue;
        struct fuse_ring *ring = queue->ring;
+       struct fuse_in_header in_header;
        int err;
 
        err = -EIO;
@@ -765,8 +766,9 @@ static int fuse_uring_copy_to_ring(struct fuse_ring_ent 
*ent,
        }
 
        /* copy fuse_in_header */
-       return copy_header_to_ring(ent, FUSE_URING_HEADER_IN_OUT, &req->in.h,
-                                  sizeof(req->in.h));
+       in_header = req->in.h;
+       return copy_header_to_ring(ent, FUSE_URING_HEADER_IN_OUT, &in_header,
+                                  sizeof(in_header));
 }
 
 static int fuse_uring_prepare_send(struct fuse_ring_ent *ent,
@@ -871,11 +873,13 @@ static void fuse_uring_commit(struct fuse_ring_ent *ent, 
struct fuse_req *req,
                              unsigned int issue_flags)
 {
        struct fuse_ring *ring = ent->queue->ring;
+       struct fuse_out_header out_header;
        ssize_t err = -EFAULT;
 
-       if (copy_header_from_ring(ent, FUSE_URING_HEADER_IN_OUT, &req->out.h,
-                                 sizeof(req->out.h)))
+       if (copy_header_from_ring(ent, FUSE_URING_HEADER_IN_OUT, &out_header,
+                                 sizeof(out_header)))
                goto out;
+       req->out.h = out_header;
 
        err = fuse_uring_out_header_has_err(&req->out.h, req);
        if (err) {
-- 
2.43.0


Reply via email to