]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ovl: check access to copy_file_range source with src mounter creds
authorAmir Goldstein <amir73il@gmail.com>
Sun, 12 Jul 2026 12:24:21 +0000 (14:24 +0200)
committerChristian Brauner <brauner@kernel.org>
Wed, 22 Jul 2026 14:42:28 +0000 (16:42 +0200)
Commit 5dae222a5ff0c ("vfs: allow copy_file_range to copy across devices")
allowed filesystems that implement the copy_file_range() f_op to decide
if they want to access cross-sb copy from/to the same fs type.

The same commit added checks to verify same sb copy for filesystems that
implement ->copy_file_range() and do not support cross-sb copy at the
time, namely, to ceph, fuse and nfs.

The two remaining fs which implement ->copy_file_range(), cifs and
overlayfs started to support cross-sb copy from this time.

While overlayfs does support cross-sb copy when the two underlying files
are on the same base fs, the copy operation on the two real files from
two different overalyfs filesystems is performed with the mounter
creds of the destination overlayfs and the read permission access hook
for the source file was called with the wrong creds.

This could cause either deny of access to copy which would otherwise be
allowed (e.g. with splice) or allow read access to file which would
otherwise be denied.

Fix the latter case by explicitly verifying read access to source file
with the source overlayfs mounter creds.

The former case remains a quirk of cross-sb overlayfs copy, but
userspace could fall back to regular copy so no harm done.

Fixes: 5dae222a5ff0c ("vfs: allow copy_file_range to copy across devices")
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Link: https://patch.msgid.link/20260712122421.203113-1-amir73il@gmail.com
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
fs/overlayfs/file.c

index 27cc07738f33bf9a29ebc4092f9b89f71e76bee9..f3d97eb146e85badeb8c0270142d0b38459b9969 100644 (file)
@@ -528,6 +528,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
                            struct file *file_out, loff_t pos_out,
                            loff_t len, unsigned int flags, enum ovl_copyop op)
 {
+       struct inode *inode_in = file_inode(file_in);
        struct inode *inode_out = file_inode(file_out);
        struct file *realfile_in, *realfile_out;
        loff_t ret;
@@ -551,7 +552,20 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
        if (IS_ERR(realfile_in))
                goto out_unlock;
 
-       with_ovl_creds(file_inode(file_out)->i_sb) {
+       /*
+        * For cross-sb copy, vfs_copy_file_range() will verify read access with
+        * the mounter creds of the dest fs mounter, so we need to explicitly
+        * verify read access with the source mounter creds.
+        */
+       if (unlikely(inode_in->i_sb != inode_out->i_sb)) {
+               with_ovl_creds(inode_in->i_sb) {
+                       ret = rw_verify_area(READ, realfile_in, &pos_in, len);
+                       if (unlikely(ret))
+                               goto out_unlock;
+               }
+       }
+
+       with_ovl_creds(inode_out->i_sb) {
                switch (op) {
                case OVL_COPY:
                        ret = vfs_copy_file_range(realfile_in, pos_in,