]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.10-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 15 Dec 2022 06:51:58 +0000 (07:51 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 15 Dec 2022 06:51:58 +0000 (07:51 +0100)
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
queue-5.10/vfs-fix-copy_file_range-averts-filesystem-freeze-protection.patch [new file with mode: 0644]
queue-5.10/vfs-fix-copy_file_range-regression-in-cross-fs-copies.patch [new file with mode: 0644]

index 58eb11f654d1da62b2780eaeb958482b773b6fa4..baa84e87b4cbadaeedbb0b6ce9ae9cdf8fe01c0b 100644 (file)
@@ -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 (file)
index 0000000..275aef1
--- /dev/null
@@ -0,0 +1,140 @@
+From stable-owner@vger.kernel.org Tue Dec 13 14:14:03 2022
+From: Amir Goldstein <amir73il@gmail.com>
+Date: Tue, 13 Dec 2022 15:13:41 +0200
+Subject: vfs: fix copy_file_range() averts filesystem freeze protection
+To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: Sasha Levin <sashal@kernel.org>, Luis Henriques <lhenriques@suse.de>, "Darrick J . Wong" <djwong@kernel.org>, linux-fsdevel@vger.kernel.org, stable@vger.kernel.org, Namjae Jeon <linkinjeon@kernel.org>, Al Viro <viro@zeniv.linux.org.uk>
+Message-ID: <20221213131341.951049-3-amir73il@gmail.com>
+
+From: Amir Goldstein <amir73il@gmail.com>
+
+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 <linkinjeon@kernel.org>
+Tested-by: Luis Henriques <lhenriques@suse.de>
+Signed-off-by: Amir Goldstein <amir73il@gmail.com>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Amir Goldstein <amir73il@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..8bcaac7
--- /dev/null
@@ -0,0 +1,217 @@
+From stable-owner@vger.kernel.org Tue Dec 13 14:14:02 2022
+From: Amir Goldstein <amir73il@gmail.com>
+Date: Tue, 13 Dec 2022 15:13:40 +0200
+Subject: vfs: fix copy_file_range() regression in cross-fs copies
+To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: Sasha Levin <sashal@kernel.org>, Luis Henriques <lhenriques@suse.de>, "Darrick J . Wong" <djwong@kernel.org>, linux-fsdevel@vger.kernel.org, stable@vger.kernel.org, Nicolas Boichat <drinkcat@chromium.org>, kernel test robot <oliver.sang@intel.com>, He Zhe <zhe.he@windriver.com>, Namjae Jeon <linkinjeon@kernel.org>, Linus Torvalds <torvalds@linux-foundation.org>
+Message-ID: <20221213131341.951049-2-amir73il@gmail.com>
+
+From: Amir Goldstein <amir73il@gmail.com>
+
+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 <drinkcat@chromium.org>
+Reported-by: kernel test robot <oliver.sang@intel.com>
+Signed-off-by: Luis Henriques <lhenriques@suse.de>
+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 <zhe.he@windriver.com>
+Tested-by: Namjae Jeon <linkinjeon@kernel.org>
+Tested-by: Luis Henriques <lhenriques@suse.de>
+Signed-off-by: Amir Goldstein <amir73il@gmail.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=216800
+Signed-off-by: Amir Goldstein <amir73il@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);