[BUG] fuse: copy_file_range exposes stale EOF data when extending a file
From: shaos
Date: Mon Oct 19 2026 - 20:35:10 EST
Hello,
On a cached non-writeback FUSE mount, __fuse_copy_file_range() truncates cache pages covered by the copy, then publishes the larger file size. If the destination had a partial old EOF page and the copy starts beyond that page, bytes dirtied past the old EOF remain in cache and become readable after extension.
HEAD commit: fe2ec83746e501645709761605c2464a44fd2929
git tree: upstream Linux checkout, local branch master
source version: Linux 7.3.0-rc4-70-gfe2ec83746e50
compiler: gcc 11.4.0, GNU ld 2.38 (from the kernel build banner)
console output:
https://gist.githubusercontent.com/shaos26/b7d8226d784c14ce316263de1779bd94/raw/59a9f0c87769dae27cff05ace7e808daa10ad2f5/console-output.txt
kernel config:
https://gist.githubusercontent.com/shaos26/b7d8226d784c14ce316263de1779bd94/raw/441739e20934d6b36aabbbbfe5153d11b70dfd34/kernel-config.txt
reproducer:
https://gist.githubusercontent.com/shaos26/b7d8226d784c14ce316263de1779bd94/raw/6ffd0112d0654714a15618f2877d9ddd1a9723f1/poc.c
Build the standalone PoC and run it on a host or guest where FUSE mounts are allowed:
cc -static -O2 -Wall -pthread -o fuse_eof_copy poc.c
./fuse_eof_copy --mode=copy
The program creates its own cached non-writeback FUSE server and client mount, dirties the old EOF page tail, performs copy_file_range() to extend the destination, then reads the newly in-range bytes and checks server writeback. The retained QEMU run used the same PoC in the copy-mode initramfs.
Three fresh QEMU boots read back 3072 bytes of 0xee from the newly in-range old EOF tail and recorded a server-side stale write. A same-path no-pollution control and a write-based extension control returned zeroed bytes.
Impact and prerequisites:
This is a data-integrity and stale page-cache exposure bug for cached non-writeback FUSE files. The demonstrated read is within the same process's file; no cross-process leak, memory corruption, or privilege escalation is claimed.
Validation: Three fresh positive boots exposed the stale tail. The same-path no-pollution and write-extension controls returned zeroed bytes.
Thanks,
shaos