]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
copy_file_range: limit size if in compat mode
authorMiklos Szeredi <mszeredi@redhat.com>
Wed, 13 Aug 2025 15:11:05 +0000 (17:11 +0200)
committerChristian Brauner <brauner@kernel.org>
Fri, 15 Aug 2025 14:11:47 +0000 (16:11 +0200)
If the process runs in 32-bit compat mode, copy_file_range results can be
in the in-band error range.  In this case limit copy length to MAX_RW_COUNT
to prevent a signed overflow.

Reported-by: Florian Weimer <fweimer@redhat.com>
Closes: https://lore.kernel.org/all/lhuh5ynl8z5.fsf@oldenburg.str.redhat.com/
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Link: https://lore.kernel.org/20250813151107.99856-1-mszeredi@redhat.com
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/read_write.c

index c5b6265d984baec17ffb1e358a156c23ce2e1d5a..833bae068770a4e410e4895132586313a9687fa2 100644 (file)
@@ -1576,6 +1576,13 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
        if (len == 0)
                return 0;
 
+       /*
+        * Make sure return value doesn't overflow in 32bit compat mode.  Also
+        * limit the size for all cases except when calling ->copy_file_range().
+        */
+       if (splice || !file_out->f_op->copy_file_range || in_compat_syscall())
+               len = min_t(size_t, MAX_RW_COUNT, len);
+
        file_start_write(file_out);
 
        /*
@@ -1589,9 +1596,7 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
                                                      len, flags);
        } else if (!splice && file_in->f_op->remap_file_range && samesb) {
                ret = file_in->f_op->remap_file_range(file_in, pos_in,
-                               file_out, pos_out,
-                               min_t(loff_t, MAX_RW_COUNT, len),
-                               REMAP_FILE_CAN_SHORTEN);
+                               file_out, pos_out, len, REMAP_FILE_CAN_SHORTEN);
                /* fallback to splice */
                if (ret <= 0)
                        splice = true;
@@ -1624,8 +1629,7 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
         * to splicing from input file, while file_start_write() is held on
         * the output file on a different sb.
         */
-       ret = do_splice_direct(file_in, &pos_in, file_out, &pos_out,
-                              min_t(size_t, len, MAX_RW_COUNT), 0);
+       ret = do_splice_direct(file_in, &pos_in, file_out, &pos_out, len, 0);
 done:
        if (ret > 0) {
                fsnotify_access(file_in);