From b9dd9c68d38dc5b987543cba28c1f40ef94dbc69 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 15 Dec 2022 07:51:58 +0100 Subject: [PATCH] 5.10-stable patches added patches: vfs-fix-copy_file_range-averts-filesystem-freeze-protection.patch vfs-fix-copy_file_range-regression-in-cross-fs-copies.patch --- queue-5.10/series | 2 + ...-averts-filesystem-freeze-protection.patch | 140 +++++++++++ ..._range-regression-in-cross-fs-copies.patch | 217 ++++++++++++++++++ 3 files changed, 359 insertions(+) create mode 100644 queue-5.10/vfs-fix-copy_file_range-averts-filesystem-freeze-protection.patch create mode 100644 queue-5.10/vfs-fix-copy_file_range-regression-in-cross-fs-copies.patch diff --git a/queue-5.10/series b/queue-5.10/series index 58eb11f654d..baa84e87b4c 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -1 +1,3 @@ x86-smpboot-move-rcu_cpu_starting-earlier.patch +vfs-fix-copy_file_range-regression-in-cross-fs-copies.patch +vfs-fix-copy_file_range-averts-filesystem-freeze-protection.patch diff --git a/queue-5.10/vfs-fix-copy_file_range-averts-filesystem-freeze-protection.patch b/queue-5.10/vfs-fix-copy_file_range-averts-filesystem-freeze-protection.patch new file mode 100644 index 00000000000..275aef12a91 --- /dev/null +++ b/queue-5.10/vfs-fix-copy_file_range-averts-filesystem-freeze-protection.patch @@ -0,0 +1,140 @@ +From stable-owner@vger.kernel.org Tue Dec 13 14:14:03 2022 +From: Amir Goldstein +Date: Tue, 13 Dec 2022 15:13:41 +0200 +Subject: vfs: fix copy_file_range() averts filesystem freeze protection +To: Greg Kroah-Hartman +Cc: Sasha Levin , Luis Henriques , "Darrick J . Wong" , linux-fsdevel@vger.kernel.org, stable@vger.kernel.org, Namjae Jeon , Al Viro +Message-ID: <20221213131341.951049-3-amir73il@gmail.com> + +From: Amir Goldstein + +commit 10bc8e4af65946b727728d7479c028742321b60a upstream. + +[backport comments for pre v5.15: +- ksmbd mentions are irrelevant - ksmbd hunks were dropped +- sb_write_started() is missing - assert was dropped +] + +Commit 868f9f2f8e00 ("vfs: fix copy_file_range() regression in cross-fs +copies") removed fallback to generic_copy_file_range() for cross-fs +cases inside vfs_copy_file_range(). + +To preserve behavior of nfsd and ksmbd server-side-copy, the fallback to +generic_copy_file_range() was added in nfsd and ksmbd code, but that +call is missing sb_start_write(), fsnotify hooks and more. + +Ideally, nfsd and ksmbd would pass a flag to vfs_copy_file_range() that +will take care of the fallback, but that code would be subtle and we got +vfs_copy_file_range() logic wrong too many times already. + +Instead, add a flag to explicitly request vfs_copy_file_range() to +perform only generic_copy_file_range() and let nfsd and ksmbd use this +flag only in the fallback path. + +This choise keeps the logic changes to minimum in the non-nfsd/ksmbd code +paths to reduce the risk of further regressions. + +Fixes: 868f9f2f8e00 ("vfs: fix copy_file_range() regression in cross-fs copies") +Tested-by: Namjae Jeon +Tested-by: Luis Henriques +Signed-off-by: Amir Goldstein +Signed-off-by: Al Viro +Signed-off-by: Amir Goldstein +Signed-off-by: Greg Kroah-Hartman +--- + fs/nfsd/vfs.c | 4 ++-- + fs/read_write.c | 17 +++++++++++++---- + include/linux/fs.h | 8 ++++++++ + 3 files changed, 23 insertions(+), 6 deletions(-) + +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -584,8 +584,8 @@ ssize_t nfsd_copy_file_range(struct file + ret = vfs_copy_file_range(src, src_pos, dst, dst_pos, count, 0); + + if (ret == -EOPNOTSUPP || ret == -EXDEV) +- ret = generic_copy_file_range(src, src_pos, dst, dst_pos, +- count, 0); ++ ret = vfs_copy_file_range(src, src_pos, dst, dst_pos, count, ++ COPY_FILE_SPLICE); + return ret; + } + +--- a/fs/read_write.c ++++ b/fs/read_write.c +@@ -1419,7 +1419,9 @@ static int generic_copy_file_checks(stru + * and several different sets of file_operations, but they all end up + * using the same ->copy_file_range() function pointer. + */ +- if (file_out->f_op->copy_file_range) { ++ if (flags & COPY_FILE_SPLICE) { ++ /* cross sb splice is allowed */ ++ } else if (file_out->f_op->copy_file_range) { + if (file_in->f_op->copy_file_range != + file_out->f_op->copy_file_range) + return -EXDEV; +@@ -1469,8 +1471,9 @@ ssize_t vfs_copy_file_range(struct file + size_t len, unsigned int flags) + { + ssize_t ret; ++ bool splice = flags & COPY_FILE_SPLICE; + +- if (flags != 0) ++ if (flags & ~COPY_FILE_SPLICE) + return -EINVAL; + + ret = generic_copy_file_checks(file_in, pos_in, file_out, pos_out, &len, +@@ -1496,14 +1499,14 @@ ssize_t vfs_copy_file_range(struct file + * same sb using clone, but for filesystems where both clone and copy + * are supported (e.g. nfs,cifs), we only call the copy method. + */ +- if (file_out->f_op->copy_file_range) { ++ if (!splice && file_out->f_op->copy_file_range) { + ret = file_out->f_op->copy_file_range(file_in, pos_in, + file_out, pos_out, + len, flags); + goto done; + } + +- if (file_in->f_op->remap_file_range && ++ if (!splice && file_in->f_op->remap_file_range && + file_inode(file_in)->i_sb == file_inode(file_out)->i_sb) { + ret = file_in->f_op->remap_file_range(file_in, pos_in, + file_out, pos_out, +@@ -1523,6 +1526,8 @@ ssize_t vfs_copy_file_range(struct file + * consistent story about which filesystems support copy_file_range() + * and which filesystems do not, that will allow userspace tools to + * make consistent desicions w.r.t using copy_file_range(). ++ * ++ * We also get here if caller (e.g. nfsd) requested COPY_FILE_SPLICE. + */ + ret = generic_copy_file_range(file_in, pos_in, file_out, pos_out, len, + flags); +@@ -1577,6 +1582,10 @@ SYSCALL_DEFINE6(copy_file_range, int, fd + pos_out = f_out.file->f_pos; + } + ++ ret = -EINVAL; ++ if (flags != 0) ++ goto out; ++ + ret = vfs_copy_file_range(f_in.file, pos_in, f_out.file, pos_out, len, + flags); + if (ret > 0) { +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1817,6 +1817,14 @@ struct dir_context { + */ + #define REMAP_FILE_ADVISORY (REMAP_FILE_CAN_SHORTEN) + ++/* ++ * These flags control the behavior of vfs_copy_file_range(). ++ * They are not available to the user via syscall. ++ * ++ * COPY_FILE_SPLICE: call splice direct instead of fs clone/copy ops ++ */ ++#define COPY_FILE_SPLICE (1 << 0) ++ + struct iov_iter; + + struct file_operations { diff --git a/queue-5.10/vfs-fix-copy_file_range-regression-in-cross-fs-copies.patch b/queue-5.10/vfs-fix-copy_file_range-regression-in-cross-fs-copies.patch new file mode 100644 index 00000000000..8bcaac73927 --- /dev/null +++ b/queue-5.10/vfs-fix-copy_file_range-regression-in-cross-fs-copies.patch @@ -0,0 +1,217 @@ +From stable-owner@vger.kernel.org Tue Dec 13 14:14:02 2022 +From: Amir Goldstein +Date: Tue, 13 Dec 2022 15:13:40 +0200 +Subject: vfs: fix copy_file_range() regression in cross-fs copies +To: Greg Kroah-Hartman +Cc: Sasha Levin , Luis Henriques , "Darrick J . Wong" , linux-fsdevel@vger.kernel.org, stable@vger.kernel.org, Nicolas Boichat , kernel test robot , He Zhe , Namjae Jeon , Linus Torvalds +Message-ID: <20221213131341.951049-2-amir73il@gmail.com> + +From: Amir Goldstein + +commit 868f9f2f8e004bfe0d3935b1976f625b2924893b upstream. + +[backport comments for pre v5.15: +- This commit has a bug fixed by commit 10bc8e4af659 ("vfs: fix + copy_file_range() averts filesystem freeze protection") +- ksmbd mentions are irrelevant - ksmbd hunks were dropped +] + +A regression has been reported by Nicolas Boichat, found while using the +copy_file_range syscall to copy a tracefs file. + +Before commit 5dae222a5ff0 ("vfs: allow copy_file_range to copy across +devices") the kernel would return -EXDEV to userspace when trying to +copy a file across different filesystems. After this commit, the +syscall doesn't fail anymore and instead returns zero (zero bytes +copied), as this file's content is generated on-the-fly and thus reports +a size of zero. + +Another regression has been reported by He Zhe - the assertion of +WARN_ON_ONCE(ret == -EOPNOTSUPP) can be triggered from userspace when +copying from a sysfs file whose read operation may return -EOPNOTSUPP. + +Since we do not have test coverage for copy_file_range() between any two +types of filesystems, the best way to avoid these sort of issues in the +future is for the kernel to be more picky about filesystems that are +allowed to do copy_file_range(). + +This patch restores some cross-filesystem copy restrictions that existed +prior to commit 5dae222a5ff0 ("vfs: allow copy_file_range to copy across +devices"), namely, cross-sb copy is not allowed for filesystems that do +not implement ->copy_file_range(). + +Filesystems that do implement ->copy_file_range() have full control of +the result - if this method returns an error, the error is returned to +the user. Before this change this was only true for fs that did not +implement the ->remap_file_range() operation (i.e. nfsv3). + +Filesystems that do not implement ->copy_file_range() still fall-back to +the generic_copy_file_range() implementation when the copy is within the +same sb. This helps the kernel can maintain a more consistent story +about which filesystems support copy_file_range(). + +nfsd and ksmbd servers are modified to fall-back to the +generic_copy_file_range() implementation in case vfs_copy_file_range() +fails with -EOPNOTSUPP or -EXDEV, which preserves behavior of +server-side-copy. + +fall-back to generic_copy_file_range() is not implemented for the smb +operation FSCTL_DUPLICATE_EXTENTS_TO_FILE, which is arguably a correct +change of behavior. + +Fixes: 5dae222a5ff0 ("vfs: allow copy_file_range to copy across devices") +Link: https://lore.kernel.org/linux-fsdevel/20210212044405.4120619-1-drinkcat@chromium.org/ +Link: https://lore.kernel.org/linux-fsdevel/CANMq1KDZuxir2LM5jOTm0xx+BnvW=ZmpsG47CyHFJwnw7zSX6Q@mail.gmail.com/ +Link: https://lore.kernel.org/linux-fsdevel/20210126135012.1.If45b7cdc3ff707bc1efa17f5366057d60603c45f@changeid/ +Link: https://lore.kernel.org/linux-fsdevel/20210630161320.29006-1-lhenriques@suse.de/ +Reported-by: Nicolas Boichat +Reported-by: kernel test robot +Signed-off-by: Luis Henriques +Fixes: 64bf5ff58dff ("vfs: no fallback for ->copy_file_range") +Link: https://lore.kernel.org/linux-fsdevel/20f17f64-88cb-4e80-07c1-85cb96c83619@windriver.com/ +Reported-by: He Zhe +Tested-by: Namjae Jeon +Tested-by: Luis Henriques +Signed-off-by: Amir Goldstein +Signed-off-by: Linus Torvalds +Link: https://bugzilla.kernel.org/show_bug.cgi?id=216800 +Signed-off-by: Amir Goldstein +Signed-off-by: Greg Kroah-Hartman +--- + fs/nfsd/vfs.c | 8 +++++ + fs/read_write.c | 77 ++++++++++++++++++++++++++++++++------------------------ + 2 files changed, 51 insertions(+), 34 deletions(-) + +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -570,6 +570,7 @@ out_err: + ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst, + u64 dst_pos, u64 count) + { ++ ssize_t ret; + + /* + * Limit copy to 4MB to prevent indefinitely blocking an nfsd +@@ -580,7 +581,12 @@ ssize_t nfsd_copy_file_range(struct file + * limit like this and pipeline multiple COPY requests. + */ + count = min_t(u64, count, 1 << 22); +- return vfs_copy_file_range(src, src_pos, dst, dst_pos, count, 0); ++ ret = vfs_copy_file_range(src, src_pos, dst, dst_pos, count, 0); ++ ++ if (ret == -EOPNOTSUPP || ret == -EXDEV) ++ ret = generic_copy_file_range(src, src_pos, dst, dst_pos, ++ count, 0); ++ return ret; + } + + __be32 nfsd4_vfs_fallocate(struct svc_rqst *rqstp, struct svc_fh *fhp, +--- a/fs/read_write.c ++++ b/fs/read_write.c +@@ -1388,28 +1388,6 @@ ssize_t generic_copy_file_range(struct f + } + EXPORT_SYMBOL(generic_copy_file_range); + +-static ssize_t do_copy_file_range(struct file *file_in, loff_t pos_in, +- struct file *file_out, loff_t pos_out, +- size_t len, unsigned int flags) +-{ +- /* +- * Although we now allow filesystems to handle cross sb copy, passing +- * a file of the wrong filesystem type to filesystem driver can result +- * in an attempt to dereference the wrong type of ->private_data, so +- * avoid doing that until we really have a good reason. NFS defines +- * several different file_system_type structures, but they all end up +- * using the same ->copy_file_range() function pointer. +- */ +- if (file_out->f_op->copy_file_range && +- file_out->f_op->copy_file_range == file_in->f_op->copy_file_range) +- return file_out->f_op->copy_file_range(file_in, pos_in, +- file_out, pos_out, +- len, flags); +- +- return generic_copy_file_range(file_in, pos_in, file_out, pos_out, len, +- flags); +-} +- + /* + * Performs necessary checks before doing a file copy + * +@@ -1431,6 +1409,24 @@ static int generic_copy_file_checks(stru + if (ret) + return ret; + ++ /* ++ * We allow some filesystems to handle cross sb copy, but passing ++ * a file of the wrong filesystem type to filesystem driver can result ++ * in an attempt to dereference the wrong type of ->private_data, so ++ * avoid doing that until we really have a good reason. ++ * ++ * nfs and cifs define several different file_system_type structures ++ * and several different sets of file_operations, but they all end up ++ * using the same ->copy_file_range() function pointer. ++ */ ++ if (file_out->f_op->copy_file_range) { ++ if (file_in->f_op->copy_file_range != ++ file_out->f_op->copy_file_range) ++ return -EXDEV; ++ } else if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb) { ++ return -EXDEV; ++ } ++ + /* Don't touch certain kinds of inodes */ + if (IS_IMMUTABLE(inode_out)) + return -EPERM; +@@ -1496,26 +1492,41 @@ ssize_t vfs_copy_file_range(struct file + file_start_write(file_out); + + /* +- * Try cloning first, this is supported by more file systems, and +- * more efficient if both clone and copy are supported (e.g. NFS). ++ * Cloning is supported by more file systems, so we implement copy on ++ * same sb using clone, but for filesystems where both clone and copy ++ * are supported (e.g. nfs,cifs), we only call the copy method. + */ ++ if (file_out->f_op->copy_file_range) { ++ ret = file_out->f_op->copy_file_range(file_in, pos_in, ++ file_out, pos_out, ++ len, flags); ++ goto done; ++ } ++ + if (file_in->f_op->remap_file_range && + file_inode(file_in)->i_sb == file_inode(file_out)->i_sb) { +- loff_t cloned; +- +- cloned = file_in->f_op->remap_file_range(file_in, pos_in, ++ 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); +- if (cloned > 0) { +- ret = cloned; ++ if (ret > 0) + goto done; +- } + } + +- ret = do_copy_file_range(file_in, pos_in, file_out, pos_out, len, +- flags); +- WARN_ON_ONCE(ret == -EOPNOTSUPP); ++ /* ++ * We can get here for same sb copy of filesystems that do not implement ++ * ->copy_file_range() in case filesystem does not support clone or in ++ * case filesystem supports clone but rejected the clone request (e.g. ++ * because it was not block aligned). ++ * ++ * In both cases, fall back to kernel copy so we are able to maintain a ++ * consistent story about which filesystems support copy_file_range() ++ * and which filesystems do not, that will allow userspace tools to ++ * make consistent desicions w.r.t using copy_file_range(). ++ */ ++ ret = generic_copy_file_range(file_in, pos_in, file_out, pos_out, len, ++ flags); ++ + done: + if (ret > 0) { + fsnotify_access(file_in); -- 2.47.3