[PATCH v4 1/3] fuse: whitelist the request headers for usercopy

Xiang Mei <[email protected]>
Newsgroups org.kernel.vger.linux-hardening,dev.linux.lists.fuse-devel,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <[email protected]>
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!
  Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
  RIP: 0010:usercopy_abort (mm/usercopy.c:90)
  Call Trace:
   __check_heap_object (mm/slub.c:8268)
   __check_object_size (mm/usercopy.c:197 mm/usercopy.c:258 mm/usercopy.c:223)
   copy_header_to_ring (fs/fuse/dev_uring.c:618)
   fuse_uring_prepare_send (fs/fuse/dev_uring.c:776 fs/fuse/dev_uring.c:785)
   fuse_uring_send_in_task (fs/fuse/dev_uring.c:1306)
   tctx_task_work_run (io_uring/tw.c:96)
   task_work_run (kernel/task_work.c:233)
   io_run_task_work (io_uring/tw.h:84)
   io_cqring_wait (io_uring/wait.c:278)
   __do_sys_io_uring_enter (io_uring/io_uring.c:2685)
   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)

in.h and out.h are adjacent in struct fuse_req, so a single usercopy
region starting at in.h covers both and nothing else.  Create the cache
with that region whitelisted.

Fixes: c090c8abae4b ("fuse: Add io-uring sqe commit and fetch support")
Cc: [email protected]
Reported-by: Weiming Shi <[email protected]>
Suggested-by: Baokun Li <[email protected]>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Xiang Mei <[email protected]>
---
v3: no context change; add Bernd's Reviewed-by
v4: drop previous tags; use kmem_cache_args to reserve usercopy area

 fs/fuse/dev.c        | 9 +++++++--
 fs/fuse/fuse_dev_i.h | 5 +++++
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 5763a7cd3b37..b8e43e374b35 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2404,10 +2404,15 @@ static struct miscdevice fuse_miscdevice = {
 
 int __init fuse_dev_init(void)
 {
+	struct kmem_cache_args args = {
+		.useroffset = offsetof(struct fuse_req, in.h),
+		.usersize = sizeof_field(struct fuse_req, in.h) +
+			    sizeof_field(struct fuse_req, out.h),
+	};
 	int err = -ENOMEM;
+
 	fuse_req_cachep = kmem_cache_create("fuse_request",
-					    sizeof(struct fuse_req),
-					    0, 0, NULL);
+					    sizeof(struct fuse_req), &args, 0);
 	if (!fuse_req_cachep)
 		goto out;
 
diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h
index 668c8391d61c..b511aaab6bfc 100644
--- a/fs/fuse/fuse_dev_i.h
+++ b/fs/fuse/fuse_dev_i.h
@@ -81,6 +81,11 @@ struct fuse_req {
 	/** @flags: Request flags, updated with test/set/clear_bit() */
 	unsigned long flags;
 
+	/*
+	 * @in and @out are the usercopy region of this cache (see
+	 * fuse_dev_init()); keep them adjacent.
+	 */
+
 	/** @in: The request input header */
 	struct {
 		/** @in.h: The request input header */
-- 
2.43.0
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.