* [PATCH] fuse: Move same-superblock check to fuse_copy_file_range @ 2025-08-06 13:52 Chunsheng Luo 2025-08-06 20:55 ` Bernd Schubert 2025-08-07 8:40 ` Luis Henriques 0 siblings, 2 replies; 5+ messages in thread From: Chunsheng Luo @ 2025-08-06 13:52 UTC (permalink / raw) To: miklos; +Cc: linux-fsdevel, linux-kernel, Chunsheng Luo The copy_file_range COPY_FILE_SPLICE capability allows filesystems to handle cross-superblock copy. However, in the current fuse implementation, __fuse_copy_file_range accesses src_file->private_data under the assumption that it points to a fuse_file structure. When the source file belongs to a non-FUSE filesystem, it will leads to kernel panics. To resolve this, move the same-superblock check from __fuse_copy_file_range to fuse_copy_file_range to ensure both files belong to the same fuse superblock before accessing private_data. Signed-off-by: Chunsheng Luo <luochunsheng@ustc.edu> --- fs/fuse/file.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 95275a1e2f54..a29f1b84f11b 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2984,9 +2984,6 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in, if (fc->no_copy_file_range) return -EOPNOTSUPP; - if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb) - return -EXDEV; - inode_lock(inode_in); err = fuse_writeback_range(inode_in, pos_in, pos_in + len - 1); inode_unlock(inode_in); @@ -3066,9 +3063,12 @@ static ssize_t fuse_copy_file_range(struct file *src_file, loff_t src_off, { ssize_t ret; + if (file_inode(src_file)->i_sb != file_inode(dst_file)->i_sb) + return splice_copy_file_range(src_file, src_off, dst_file, + dst_off, len); + ret = __fuse_copy_file_range(src_file, src_off, dst_file, dst_off, len, flags); - if (ret == -EOPNOTSUPP || ret == -EXDEV) ret = splice_copy_file_range(src_file, src_off, dst_file, dst_off, len); -- 2.43.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH] fuse: Move same-superblock check to fuse_copy_file_range 2025-08-06 13:52 [PATCH] fuse: Move same-superblock check to fuse_copy_file_range Chunsheng Luo @ 2025-08-06 20:55 ` Bernd Schubert 2025-08-07 8:40 ` Luis Henriques 1 sibling, 0 replies; 5+ messages in thread From: Bernd Schubert @ 2025-08-06 20:55 UTC (permalink / raw) To: Chunsheng Luo, miklos; +Cc: linux-fsdevel, linux-kernel On 8/6/25 15:52, Chunsheng Luo wrote: > The copy_file_range COPY_FILE_SPLICE capability allows filesystems to > handle cross-superblock copy. However, in the current fuse implementation, > __fuse_copy_file_range accesses src_file->private_data under the assumption > that it points to a fuse_file structure. When the source file belongs to a > non-FUSE filesystem, it will leads to kernel panics. > > To resolve this, move the same-superblock check from __fuse_copy_file_range > to fuse_copy_file_range to ensure both files belong to the same fuse > superblock before accessing private_data. > > Signed-off-by: Chunsheng Luo <luochunsheng@ustc.edu> > --- > fs/fuse/file.c | 8 ++++---- > 1 file changed, 4 insertions(+), 4 deletions(-) > > diff --git a/fs/fuse/file.c b/fs/fuse/file.c > index 95275a1e2f54..a29f1b84f11b 100644 > --- a/fs/fuse/file.c > +++ b/fs/fuse/file.c > @@ -2984,9 +2984,6 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in, > if (fc->no_copy_file_range) > return -EOPNOTSUPP; > > - if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb) > - return -EXDEV; > - > inode_lock(inode_in); > err = fuse_writeback_range(inode_in, pos_in, pos_in + len - 1); > inode_unlock(inode_in); > @@ -3066,9 +3063,12 @@ static ssize_t fuse_copy_file_range(struct file *src_file, loff_t src_off, > { > ssize_t ret; > > + if (file_inode(src_file)->i_sb != file_inode(dst_file)->i_sb) > + return splice_copy_file_range(src_file, src_off, dst_file, > + dst_off, len); > + > ret = __fuse_copy_file_range(src_file, src_off, dst_file, dst_off, > len, flags); > - > if (ret == -EOPNOTSUPP || ret == -EXDEV) > ret = splice_copy_file_range(src_file, src_off, dst_file, > dst_off, len); I guess you can remove the check EXDEV here? ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] fuse: Move same-superblock check to fuse_copy_file_range 2025-08-06 13:52 [PATCH] fuse: Move same-superblock check to fuse_copy_file_range Chunsheng Luo 2025-08-06 20:55 ` Bernd Schubert @ 2025-08-07 8:40 ` Luis Henriques 2025-08-07 12:49 ` Chunsheng Luo 1 sibling, 1 reply; 5+ messages in thread From: Luis Henriques @ 2025-08-07 8:40 UTC (permalink / raw) To: Chunsheng Luo; +Cc: miklos, linux-fsdevel, linux-kernel On Wed, Aug 06 2025, Chunsheng Luo wrote: > The copy_file_range COPY_FILE_SPLICE capability allows filesystems to > handle cross-superblock copy. However, in the current fuse implementation, > __fuse_copy_file_range accesses src_file->private_data under the assumption > that it points to a fuse_file structure. When the source file belongs to a > non-FUSE filesystem, it will leads to kernel panics. I wonder if you have actually seen this kernel panic happening. It seems like the code you're moving into fuse_copy_file_range() shouldn't be needed as the same check is already done in generic_copy_file_checks() (which is called from vfs_copy_file_range()). Either way, I think your change to fuse_copy_file_range() could be simplified with something like: ssize_t ret = -EXDEV; if (file_inode(src_file)->i_sb == file_inode(dst_file)->i_sb) ret = __fuse_copy_file_range(src_file, src_off, dst_file, dst_off, len, flags); if (ret == -EOPNOTSUPP || ret == -EXDEV) ret = splice_copy_file_range(src_file, src_off, dst_file, dst_off, len); But again, my understanding is that this should never happen in practice and that the superblock check could even be removed from __fuse_copy_file_range(). Cheers, -- Luís > > To resolve this, move the same-superblock check from __fuse_copy_file_range > to fuse_copy_file_range to ensure both files belong to the same fuse > superblock before accessing private_data. > > Signed-off-by: Chunsheng Luo <luochunsheng@ustc.edu> > --- > fs/fuse/file.c | 8 ++++---- > 1 file changed, 4 insertions(+), 4 deletions(-) > > diff --git a/fs/fuse/file.c b/fs/fuse/file.c > index 95275a1e2f54..a29f1b84f11b 100644 > --- a/fs/fuse/file.c > +++ b/fs/fuse/file.c > @@ -2984,9 +2984,6 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in, > if (fc->no_copy_file_range) > return -EOPNOTSUPP; > > - if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb) > - return -EXDEV; > - > inode_lock(inode_in); > err = fuse_writeback_range(inode_in, pos_in, pos_in + len - 1); > inode_unlock(inode_in); > @@ -3066,9 +3063,12 @@ static ssize_t fuse_copy_file_range(struct file *src_file, loff_t src_off, > { > ssize_t ret; > > + if (file_inode(src_file)->i_sb != file_inode(dst_file)->i_sb) > + return splice_copy_file_range(src_file, src_off, dst_file, > + dst_off, len); > + > ret = __fuse_copy_file_range(src_file, src_off, dst_file, dst_off, > len, flags); > - > if (ret == -EOPNOTSUPP || ret == -EXDEV) > ret = splice_copy_file_range(src_file, src_off, dst_file, > dst_off, len); > -- > 2.43.0 > ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] fuse: Move same-superblock check to fuse_copy_file_range 2025-08-07 8:40 ` Luis Henriques @ 2025-08-07 12:49 ` Chunsheng Luo 2025-08-08 9:55 ` Amir Goldstein 0 siblings, 1 reply; 5+ messages in thread From: Chunsheng Luo @ 2025-08-07 12:49 UTC (permalink / raw) To: luis; +Cc: linux-fsdevel, linux-kernel, luochunsheng, miklos, bernd [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Type: text/plain; charset=y, Size: 4226 bytes --] On Thu, Aug 07 2025, Luis Henriques wrote: >> The copy_file_range COPY_FILE_SPLICE capability allows filesystems to >> handle cross-superblock copy. However, in the current fuse implementation, >> __fuse_copy_file_range accesses src_file->private_data under the assumption >> that it points to a fuse_file structure. When the source file belongs to a >> non-FUSE filesystem, it will leads to kernel panics. > > I wonder if you have actually seen this kernel panic happening. It seems > like the code you're moving into fuse_copy_file_range() shouldn't be > needed as the same check is already done in generic_copy_file_checks() > (which is called from vfs_copy_file_range()). > > Either way, I think your change to fuse_copy_file_range() could be > simplified with something like: > > ssize_t ret = -EXDEV; > > if (file_inode(src_file)->i_sb == file_inode(dst_file)->i_sb) > ret = __fuse_copy_file_range(src_file, src_off, dst_file, dst_off, > len, flags); > > if (ret == -EOPNOTSUPP || ret == -EXDEV) > ret = splice_copy_file_range(src_file, src_off, dst_file, > dst_off, len); > > But again, my understanding is that this should never happen in practice > and that the superblock check could even be removed from > __fuse_copy_file_range(). > > Cheers, > -- > Luís > Yes, now copy_file_range won't crash. generic_copy_file_checks: /* * 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 (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; } else if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb) { return -EXDEV; } generic_copy_file_checks mentions that now allows some filesystems to handle cross-sb copy. code: } 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; If the same filesystem is satisfied but the sb is not same, it will go to fuse_copy_file_range, so fuse_copy_file_range needs to handle this situation. Sorry, there is an mistake with my patch log description. __fuse_copy_file_range does not exist that the source file is a NON-Fuse filesystem, so It can safely use ->private_data. Therefore, this patch does not need. Thanks Chunsheng Luo >> >> To resolve this, move the same-superblock check from __fuse_copy_file_range >> to fuse_copy_file_range to ensure both files belong to the same fuse >> superblock before accessing private_data. >> >> Signed-off-by: Chunsheng Luo <luochunsheng@ustc.edu> >> --- >> fs/fuse/file.c | 8 ++++---- >> 1 file changed, 4 insertions(+), 4 deletions(-) >> >> diff --git a/fs/fuse/file.c b/fs/fuse/file.c >> index 95275a1e2f54..a29f1b84f11b 100644 >> --- a/fs/fuse/file.c >> +++ b/fs/fuse/file.c >> @@ -2984,9 +2984,6 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in, >> if (fc->no_copy_file_range) >> return -EOPNOTSUPP; >> >> - if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb) >> - return -EXDEV; >> - >> inode_lock(inode_in); >> err = fuse_writeback_range(inode_in, pos_in, pos_in + len - 1); >> inode_unlock(inode_in); >> @@ -3066,9 +3063,12 @@ static ssize_t fuse_copy_file_range(struct file *src_file, loff_t src_off, >> { >> ssize_t ret; >> >> + if (file_inode(src_file)->i_sb != file_inode(dst_file)->i_sb) >> + return splice_copy_file_range(src_file, src_off, dst_file, >> + dst_off, len); >> + >> ret = __fuse_copy_file_range(src_file, src_off, dst_file, dst_off, >> len, flags); >> - >> if (ret == -EOPNOTSUPP || ret == -EXDEV) >> ret = splice_copy_file_range(src_file, src_off, dst_file, >> dst_off, len); >> -- >> 2.43.0 >> ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] fuse: Move same-superblock check to fuse_copy_file_range 2025-08-07 12:49 ` Chunsheng Luo @ 2025-08-08 9:55 ` Amir Goldstein 0 siblings, 0 replies; 5+ messages in thread From: Amir Goldstein @ 2025-08-08 9:55 UTC (permalink / raw) To: Chunsheng Luo; +Cc: luis, linux-fsdevel, linux-kernel, miklos, bernd On Thu, Aug 7, 2025 at 2:49 PM Chunsheng Luo <luochunsheng@ustc.edu> wrote: > > On Thu, Aug 07 2025, Luis Henriques wrote: > > >> The copy_file_range COPY_FILE_SPLICE capability allows filesystems to > >> handle cross-superblock copy. However, in the current fuse implementation, > >> __fuse_copy_file_range accesses src_file->private_data under the assumption > >> that it points to a fuse_file structure. When the source file belongs to a > >> non-FUSE filesystem, it will leads to kernel panics. > > > > I wonder if you have actually seen this kernel panic happening. It seems > > like the code you're moving into fuse_copy_file_range() shouldn't be > > needed as the same check is already done in generic_copy_file_checks() > > (which is called from vfs_copy_file_range()). > > > > Either way, I think your change to fuse_copy_file_range() could be > > simplified with something like: > > > > ssize_t ret = -EXDEV; > > > > if (file_inode(src_file)->i_sb == file_inode(dst_file)->i_sb) > > ret = __fuse_copy_file_range(src_file, src_off, dst_file, dst_off, > > len, flags); > > > > if (ret == -EOPNOTSUPP || ret == -EXDEV) > > ret = splice_copy_file_range(src_file, src_off, dst_file, > > dst_off, len); > > > > But again, my understanding is that this should never happen in practice > > and that the superblock check could even be removed from > > __fuse_copy_file_range(). > > > > Cheers, > > -- > > Luís > > > > Yes, now copy_file_range won't crash. > > generic_copy_file_checks: > /* > * 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 (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; > } else if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb) { > return -EXDEV; > } > > generic_copy_file_checks mentions that now allows some filesystems to handle cross-sb copy. > > code: > } 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; > > If the same filesystem is satisfied but the sb is not same, it will go to fuse_copy_file_range, > so fuse_copy_file_range needs to handle this situation. > > Sorry, there is an mistake with my patch log description. __fuse_copy_file_range does not exist that > the source file is a NON-Fuse filesystem, so It can safely use ->private_data. > > Therefore, this patch does not need. Indeed, this patch makes no sense and does not change any logic at all. Thanks, Amir. ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-08-08 9:55 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-08-06 13:52 [PATCH] fuse: Move same-superblock check to fuse_copy_file_range Chunsheng Luo 2025-08-06 20:55 ` Bernd Schubert 2025-08-07 8:40 ` Luis Henriques 2025-08-07 12:49 ` Chunsheng Luo 2025-08-08 9:55 ` Amir Goldstein
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.