mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v4 1/3] fuse: whitelist the request headers for usercopy
@ 2026-07-14 23:54 Xiang Mei
  2026-07-14 23:54 ` [PATCH v4 2/3] fuse: bound io-uring payload copies to the registered buffer size Xiang Mei
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Xiang Mei @ 2026-07-14 23:54 UTC (permalink / raw)
  To: Joanne Koong, Bernd Schubert, Baokun Li, Miklos Szeredi,
	Kees Cook, Gustavo A . R . Silva
  Cc: fuse-devel, linux-hardening, linux-kernel, stable,
	Luis Henriques, Pavel Begunkov, bestswngs, Xiang Mei

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: stable@vger.kernel.org
Reported-by: Weiming Shi <bestswngs@gmail.com>
Suggested-by: Baokun Li <libaokun@linux.alibaba.com>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Xiang Mei <xmei5@asu.edu>
---
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


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v4 2/3] fuse: bound io-uring payload copies to the registered buffer size
  2026-07-14 23:54 [PATCH v4 1/3] fuse: whitelist the request headers for usercopy Xiang Mei
@ 2026-07-14 23:54 ` Xiang Mei
  2026-07-14 23:54 ` [PATCH v4 3/3] fuse: deduplicate the oversized-request error selection Xiang Mei
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Xiang Mei @ 2026-07-14 23:54 UTC (permalink / raw)
  To: Joanne Koong, Bernd Schubert, Baokun Li, Miklos Szeredi,
	Kees Cook, Gustavo A . R . Silva
  Cc: fuse-devel, linux-hardening, linux-kernel, stable,
	Luis Henriques, Pavel Begunkov, bestswngs, Xiang Mei

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: stable@vger.kernel.org
Reported-by: Weiming Shi <bestswngs@gmail.com>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Xiang Mei <xmei5@asu.edu>
Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
---
v3: propose the patch fixing another issue found by Bernd by Joanne suggested way
v4: no context change as v3; add Reviewed-by: Joanne Koong ...

 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 77c8cec43d9c..4529505b2bca 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;
+
 	/* copy the payload */
 	err = fuse_copy_args(&cs, num_args, args->in_pages,
 			     (struct fuse_arg *)in_args, 0);
@@ -1159,6 +1165,7 @@ fuse_uring_create_ring_ent(struct io_uring_cmd *cmd,
 	ent->queue = queue;
 	ent->headers = headers->iov_base;
 	ent->payload = payload->iov_base;
+	ent->payload_sz = payload->iov_len;
 
 	atomic_inc(&ring->queue_refs);
 	return ent;
diff --git a/fs/fuse/dev_uring_i.h b/fs/fuse/dev_uring_i.h
index 55f8d04e4b0b..efa7f034496a 100644
--- a/fs/fuse/dev_uring_i.h
+++ b/fs/fuse/dev_uring_i.h
@@ -41,6 +41,7 @@ struct fuse_ring_ent {
 	/* userspace buffer */
 	struct fuse_uring_req_header __user *headers;
 	void __user *payload;
+	size_t payload_sz;
 
 	/* the ring queue that owns the request */
 	struct fuse_ring_queue *queue;
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v4 3/3] fuse: deduplicate the oversized-request error selection
  2026-07-14 23:54 [PATCH v4 1/3] fuse: whitelist the request headers for usercopy Xiang Mei
  2026-07-14 23:54 ` [PATCH v4 2/3] fuse: bound io-uring payload copies to the registered buffer size Xiang Mei
@ 2026-07-14 23:54 ` Xiang Mei
  2026-07-15 18:30   ` Joanne Koong
  2026-07-15  2:17 ` [PATCH v4 1/3] fuse: whitelist the request headers for usercopy Baokun Li
  2026-07-15 18:13 ` Joanne Koong
  3 siblings, 1 reply; 7+ messages in thread
From: Xiang Mei @ 2026-07-14 23:54 UTC (permalink / raw)
  To: Joanne Koong, Bernd Schubert, Baokun Li, Miklos Szeredi,
	Kees Cook, Gustavo A . R . Silva
  Cc: fuse-devel, linux-hardening, linux-kernel, stable,
	Luis Henriques, Pavel Begunkov, bestswngs, Xiang Mei

fuse_dev_do_read() and fuse_uring_args_to_ring() both pick the error for
a request that does not fit the server's buffer, and both special-case
FUSE_SETXATTR.  Move that choice into a helper so the two transports
cannot drift apart.

No functional change.

Signed-off-by: Xiang Mei <xmei5@asu.edu>
---
v4: introduce fuse_req_too_large_error as a helper

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

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index b8e43e374b35..56c38aca7389 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -1584,10 +1584,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
 
 	/* If request is too large, reply with an error and restart the read */
 	if (nbytes < reqsize) {
-		req->out.h.error = -EIO;
-		/* SETXATTR is special, since it may contain too large data */
-		if (args->opcode == FUSE_SETXATTR)
-			req->out.h.error = -E2BIG;
+		req->out.h.error = fuse_req_too_large_error(args);
 		fuse_request_end(req);
 		goto restart;
 	}
diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c
index 4529505b2bca..cebd8f871627 100644
--- a/fs/fuse/dev_uring.c
+++ b/fs/fuse/dev_uring.c
@@ -729,7 +729,7 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req,
 	}
 
 	if (fuse_len_args(num_args, (struct fuse_arg *)in_args) > ent->payload_sz)
-		return args->opcode == FUSE_SETXATTR ? -E2BIG : -EIO;
+		return fuse_req_too_large_error(args);
 
 	/* copy the payload */
 	err = fuse_copy_args(&cs, num_args, args->in_pages,
diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h
index b511aaab6bfc..4958158c0f02 100644
--- a/fs/fuse/fuse_dev_i.h
+++ b/fs/fuse/fuse_dev_i.h
@@ -13,6 +13,8 @@
 #include <linux/workqueue.h>
 #include <linux/fs.h>
 
+#include "args.h"
+
 /* Ordinary requests have even IDs, while interrupts IDs are odd */
 #define FUSE_INT_REQ_BIT (1ULL << 0)
 #define FUSE_REQ_ID_STEP (1ULL << 1)
@@ -367,6 +369,11 @@ static inline struct fuse_dev *__fuse_get_dev(struct file *file)
 	return fud;
 }
 
+static inline int fuse_req_too_large_error(struct fuse_args *args)
+{
+	return args->opcode == FUSE_SETXATTR ? -E2BIG : -EIO;
+}
+
 void fuse_iqueue_init(struct fuse_iqueue *fiq, const struct fuse_iqueue_ops *ops, void *priv);
 
 struct fuse_dev *fuse_get_dev(struct file *file);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 1/3] fuse: whitelist the request headers for usercopy
  2026-07-14 23:54 [PATCH v4 1/3] fuse: whitelist the request headers for usercopy Xiang Mei
  2026-07-14 23:54 ` [PATCH v4 2/3] fuse: bound io-uring payload copies to the registered buffer size Xiang Mei
  2026-07-14 23:54 ` [PATCH v4 3/3] fuse: deduplicate the oversized-request error selection Xiang Mei
@ 2026-07-15  2:17 ` Baokun Li
  2026-07-15 18:13 ` Joanne Koong
  3 siblings, 0 replies; 7+ messages in thread
From: Baokun Li @ 2026-07-15  2:17 UTC (permalink / raw)
  To: Xiang Mei
  Cc: fuse-devel, linux-hardening, linux-kernel, stable,
	Luis Henriques, Pavel Begunkov, bestswngs, Joanne Koong,
	Bernd Schubert, Miklos Szeredi, Gustavo A . R . Silva, Kees Cook

On 2026/7/15 07:54, Xiang Mei wrote:
> 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: stable@vger.kernel.org
> Reported-by: Weiming Shi <bestswngs@gmail.com>
> Suggested-by: Baokun Li <libaokun@linux.alibaba.com>
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Xiang Mei <xmei5@asu.edu>

Looks good, feel free to add:

Reviewed-by: Baokun Li <libaokun@linux.alibaba.com>

> ---
> 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 */



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 1/3] fuse: whitelist the request headers for usercopy
  2026-07-14 23:54 [PATCH v4 1/3] fuse: whitelist the request headers for usercopy Xiang Mei
                   ` (2 preceding siblings ...)
  2026-07-15  2:17 ` [PATCH v4 1/3] fuse: whitelist the request headers for usercopy Baokun Li
@ 2026-07-15 18:13 ` Joanne Koong
  2026-07-15 21:59   ` Bernd Schubert
  3 siblings, 1 reply; 7+ messages in thread
From: Joanne Koong @ 2026-07-15 18:13 UTC (permalink / raw)
  To: Xiang Mei
  Cc: Bernd Schubert, Baokun Li, Miklos Szeredi, Kees Cook,
	Gustavo A . R . Silva, fuse-devel, linux-hardening, linux-kernel,
	stable, Luis Henriques, Pavel Begunkov, bestswngs

On Tue, Jul 14, 2026 at 4:54 PM Xiang Mei <xmei5@asu.edu> wrote:
>
> 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: stable@vger.kernel.org
> Reported-by: Weiming Shi <bestswngs@gmail.com>
> Suggested-by: Baokun Li <libaokun@linux.alibaba.com>
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
> ---
> 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
>

I think this is more a matter of preference as they're both
functionally correct but imo the previous approach seemed cleaner,
given that only the io-uring path needs this. The extra hop goes
through a tmp stack variable whose memory is already in the L1 cache
and the memcpys are small (~40 bytes), so I think the cost is
essentially negligible. Not sure if Bernd or Miklos or Amir have a
preference here.

Thanks,
Joanne

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 3/3] fuse: deduplicate the oversized-request error selection
  2026-07-14 23:54 ` [PATCH v4 3/3] fuse: deduplicate the oversized-request error selection Xiang Mei
@ 2026-07-15 18:30   ` Joanne Koong
  0 siblings, 0 replies; 7+ messages in thread
From: Joanne Koong @ 2026-07-15 18:30 UTC (permalink / raw)
  To: Xiang Mei
  Cc: Bernd Schubert, Baokun Li, Miklos Szeredi, Kees Cook,
	Gustavo A . R . Silva, fuse-devel, linux-hardening, linux-kernel,
	stable, Luis Henriques, Pavel Begunkov, bestswngs

On Tue, Jul 14, 2026 at 4:54 PM Xiang Mei <xmei5@asu.edu> wrote:
>
> fuse_dev_do_read() and fuse_uring_args_to_ring() both pick the error for
> a request that does not fit the server's buffer, and both special-case
> FUSE_SETXATTR.  Move that choice into a helper so the two transports
> cannot drift apart.
>
> No functional change.
>
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
> ---
> v4: introduce fuse_req_too_large_error as a helper
>
>  fs/fuse/dev.c        | 5 +----
>  fs/fuse/dev_uring.c  | 2 +-
>  fs/fuse/fuse_dev_i.h | 7 +++++++
>  3 files changed, 9 insertions(+), 5 deletions(-)
>
> diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
> index b8e43e374b35..56c38aca7389 100644
> --- a/fs/fuse/dev.c
> +++ b/fs/fuse/dev.c
> @@ -1584,10 +1584,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
>
>         /* If request is too large, reply with an error and restart the read */
>         if (nbytes < reqsize) {
> -               req->out.h.error = -EIO;
> -               /* SETXATTR is special, since it may contain too large data */
> -               if (args->opcode == FUSE_SETXATTR)
> -                       req->out.h.error = -E2BIG;
> +               req->out.h.error = fuse_req_too_large_error(args);
>                 fuse_request_end(req);
>                 goto restart;
>         }
> diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c
> index 4529505b2bca..cebd8f871627 100644
> --- a/fs/fuse/dev_uring.c
> +++ b/fs/fuse/dev_uring.c
> @@ -729,7 +729,7 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req,
>         }
>
>         if (fuse_len_args(num_args, (struct fuse_arg *)in_args) > ent->payload_sz)
> -               return args->opcode == FUSE_SETXATTR ? -E2BIG : -EIO;
> +               return fuse_req_too_large_error(args);
>
>         /* copy the payload */
>         err = fuse_copy_args(&cs, num_args, args->in_pages,
> diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h
> index b511aaab6bfc..4958158c0f02 100644
> --- a/fs/fuse/fuse_dev_i.h
> +++ b/fs/fuse/fuse_dev_i.h
> @@ -13,6 +13,8 @@
>  #include <linux/workqueue.h>
>  #include <linux/fs.h>
>
> +#include "args.h"
> +
>  /* Ordinary requests have even IDs, while interrupts IDs are odd */
>  #define FUSE_INT_REQ_BIT (1ULL << 0)
>  #define FUSE_REQ_ID_STEP (1ULL << 1)
> @@ -367,6 +369,11 @@ static inline struct fuse_dev *__fuse_get_dev(struct file *file)
>         return fud;
>  }
>
> +static inline int fuse_req_too_large_error(struct fuse_args *args)
> +{
> +       return args->opcode == FUSE_SETXATTR ? -E2BIG : -EIO;
> +}

Could you add a comment to the top of this function? Maybe something
like "A request whose payload size exceeds the transport buffer size
is rejected with -EIO. The exception is FUSE_SETXATTR whose value may
legitimately be oversized and is rejected with -E2BIG, matching the
vfs setxattr path".

Reviewed-by: Joanne Koong <joannelkoong@gmail.com>

Thanks,
Joanne

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 1/3] fuse: whitelist the request headers for usercopy
  2026-07-15 18:13 ` Joanne Koong
@ 2026-07-15 21:59   ` Bernd Schubert
  0 siblings, 0 replies; 7+ messages in thread
From: Bernd Schubert @ 2026-07-15 21:59 UTC (permalink / raw)
  To: Joanne Koong, Xiang Mei
  Cc: Baokun Li, Miklos Szeredi, Kees Cook, Gustavo A . R . Silva,
	fuse-devel, linux-hardening, linux-kernel, stable,
	Luis Henriques, Pavel Begunkov, bestswngs



On 7/15/26 20:13, Joanne Koong wrote:
> On Tue, Jul 14, 2026 at 4:54 PM Xiang Mei <xmei5@asu.edu> wrote:
>>
>> 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: stable@vger.kernel.org
>> Reported-by: Weiming Shi <bestswngs@gmail.com>
>> Suggested-by: Baokun Li <libaokun@linux.alibaba.com>
>> Assisted-by: Claude:claude-opus-4-8
>> Signed-off-by: Xiang Mei <xmei5@asu.edu>
>> ---
>> 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
>>
> 
> I think this is more a matter of preference as they're both
> functionally correct but imo the previous approach seemed cleaner,
> given that only the io-uring path needs this. The extra hop goes
> through a tmp stack variable whose memory is already in the L1 cache
> and the memcpys are small (~40 bytes), so I think the cost is
> essentially negligible. Not sure if Bernd or Miklos or Amir have a
> preference here.

Sorry for late replies, currently on vacation and reviews only when
everyone else is asleep.

I don't have a strong opinion, I wondered the same if the stack copy was
avoidable, but then came to the same conclusion as Joanne - negligible
overhead.


Thanks,
Bernd

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-07-15 21:59 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-14 23:54 [PATCH v4 1/3] fuse: whitelist the request headers for usercopy Xiang Mei
2026-07-14 23:54 ` [PATCH v4 2/3] fuse: bound io-uring payload copies to the registered buffer size Xiang Mei
2026-07-14 23:54 ` [PATCH v4 3/3] fuse: deduplicate the oversized-request error selection Xiang Mei
2026-07-15 18:30   ` Joanne Koong
2026-07-15  2:17 ` [PATCH v4 1/3] fuse: whitelist the request headers for usercopy Baokun Li
2026-07-15 18:13 ` Joanne Koong
2026-07-15 21:59   ` Bernd Schubert

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome