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