You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
copy_file_range and splice accept a negative offset #467
copy_file_range and splice read *off_in and *off_out from guest memory into an int64_t that starts at -1, and the copy code treats any negative value as "use the descriptor's position" (io_copy_chunked in src/syscall/io.c, and the same >= 0 tests in the splice path). A guest that passes a pointer to a negative offset therefore gets a copy from or to the file position instead of an error.
Linux rejects these calls. Measured with a static aarch64 probe that writes 8 bytes to a regular file and calls each syscall with a pointer to -1 and a count of 4:
Call
Linux (qemu)
elfuse
copy_file_range, *off_in = -1
-1, EOVERFLOW
4
copy_file_range, *off_out = -1
-1, EOVERFLOW
4
splice file to pipe, *off_in = -1
-1, EINVAL
4
splice pipe to file, *off_out = -1
-1, EINVAL
4
On Linux, copy_file_range gives EOVERFLOW because generic_copy_file_checks adds the size_t count to the loff_t position, the negative position promotes to a huge unsigned value, and the sum wraps. splice gives EINVAL from the rw_verify_area check on a negative position.
Goal: a negative offset passed by pointer is refused with the Linux errno before anything is copied, and the "no pointer" case keeps using the descriptor position. That means the sentinel would have to be "pointer was NULL" rather than "value is negative".
Found while answering review on #463; not changed there because it is outside RLIMIT_FSIZE.
The copy_file_range half is fixed on #463 in 7d0851f. copy_file_range now tests pos + len < pos as an unsigned sum at both ends, as generic_copy_file_checks does, so a negative *off_in or *off_out whose sum wraps returns EOVERFLOW, matching the qemu measurement above (-1 with a count of 4). A negative offset whose sum does not wrap, such as -4096 with a count of 10, gets EINVAL from rw_verify_area on qemu, and elfuse now returns EINVAL there too. An earlier version of this comment said every negative offset gives EOVERFLOW; that holds only when the sum wraps. The splice half (EINVAL for a negative offset) is still open.
Activity
Refuse a copy_file_range range that wraps
xalestar commentedon Oct 24, 2026
The copy_file_range half is fixed on #463 in 7d0851f. copy_file_range now tests
pos + len < posas an unsigned sum at both ends, asgeneric_copy_file_checksdoes, so a negative*off_inor*off_outwhose sum wraps returns EOVERFLOW, matching the qemu measurement above (-1 with a count of 4). A negative offset whose sum does not wrap, such as -4096 with a count of 10, gets EINVAL fromrw_verify_areaon qemu, and elfuse now returns EINVAL there too. An earlier version of this comment said every negative offset gives EOVERFLOW; that holds only when the sum wraps. The splice half (EINVAL for a negative offset) is still open.Refuse a copy_file_range range that wraps