[PATCH] fuse: avoid 32-bit prune notification count wrap
From: Samuel Moelius
Date: Mon Jun 08 2026 - 20:45:41 EST
FUSE_NOTIFY_PRUNE validates the nodeid payload length with:
size - sizeof(outarg) != outarg.count * sizeof(u64)
On 32-bit kernels, `size_t` is also 32 bits, so the daemon-controlled
count multiplication can wrap. A prune notification with count
0x20000000 and no nodeid payload passes the check, enters the copy
loop, and asks the device copy path to read nodeids that are not
present in the userspace write buffer. In QEMU this reaches the
fuse_copy_fill() BUG_ON(!err) path.
Validate the already-subtracted payload length with division and modulo
instead. That accepts exactly the same valid messages, but avoids
wrapping arithmetic before the copy loop consumes the count.
Assisted-by: Codex:gpt-5.5-cyber-preview
Signed-off-by: Samuel Moelius <sam.moelius@xxxxxxxxxxxxxxx>
---
fs/fuse/dev.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index c105aaf9ff5d..f2fca4659527 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2066,6 +2066,7 @@ static int fuse_notify_prune(struct fuse_conn *fc, unsigned int size,
struct fuse_copy_state *cs)
{
struct fuse_notify_prune_out outarg;
+ unsigned int payload;
const unsigned int batch = 512;
u64 *nodeids __free(kfree) = kmalloc(sizeof(u64) * batch, GFP_KERNEL);
unsigned int num, i;
@@ -2081,7 +2082,8 @@ static int fuse_notify_prune(struct fuse_conn *fc, unsigned int size,
if (err)
return err;
- if (size - sizeof(outarg) != outarg.count * sizeof(u64))
+ payload = size - sizeof(outarg);
+ if (payload % sizeof(u64) || payload / sizeof(u64) != outarg.count)
return -EINVAL;
for (; outarg.count; outarg.count -= num) {
--
2.43.0