diff mbox series

[2/2] fs: move file_start_write() into direct_splice_actor()

Message ID 20231129200709.3154370-3-amir73il@gmail.com (mailing list archive)
State New
Headers show
Series Avert possible deadlock with splice() and fanotify | expand

Commit Message

Amir Goldstein Nov. 29, 2023, 8:07 p.m. UTC
The two callers of do_splice_direct() hold file_start_write() on the
output file.

This may cause file permission hooks to be called indirectly on an
overlayfs lower layer, which is on the same filesystem of the output
file and could lead to deadlock with fanotify permission events.

To fix this potential deadlock, move file_start_write() from the callers
into the direct_splice_actor(), so file_start_write() will not be held
while reading the input file.

Suggested-by: Josef Bacik <josef@toxicpanda.com>
Link: https://lore.kernel.org/r/20231128214258.GA2398475@perftesting/
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
 fs/overlayfs/copy_up.c | 2 --
 fs/read_write.c        | 2 --
 fs/splice.c            | 8 +++++++-
 3 files changed, 7 insertions(+), 5 deletions(-)

Comments

Jan Kara Nov. 30, 2023, 1:32 p.m. UTC | #1
On Wed 29-11-23 22:07:09, Amir Goldstein wrote:
> The two callers of do_splice_direct() hold file_start_write() on the
> output file.
> 
> This may cause file permission hooks to be called indirectly on an
> overlayfs lower layer, which is on the same filesystem of the output
> file and could lead to deadlock with fanotify permission events.
> 
> To fix this potential deadlock, move file_start_write() from the callers
> into the direct_splice_actor(), so file_start_write() will not be held
> while reading the input file.
> 
> Suggested-by: Josef Bacik <josef@toxicpanda.com>
> Link: https://lore.kernel.org/r/20231128214258.GA2398475@perftesting/
> Signed-off-by: Amir Goldstein <amir73il@gmail.com>

Yeah, very nice solution! It all looks good to me so feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza
diff mbox series

Patch

diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 7a44c8212331..294b330aba9f 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -333,11 +333,9 @@  static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry,
 		if (error)
 			break;
 
-		ovl_start_write(dentry);
 		bytes = do_splice_direct(old_file, &old_pos,
 					 new_file, &new_pos,
 					 this_len, SPLICE_F_MOVE);
-		ovl_end_write(dentry);
 		if (bytes <= 0) {
 			error = bytes;
 			break;
diff --git a/fs/read_write.c b/fs/read_write.c
index 555514cdad53..b7110ee77c1c 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -1286,10 +1286,8 @@  static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
 		retval = rw_verify_area(WRITE, out.file, &out_pos, count);
 		if (retval < 0)
 			goto fput_out;
-		file_start_write(out.file);
 		retval = do_splice_direct(in.file, &pos, out.file, &out_pos,
 					  count, fl);
-		file_end_write(out.file);
 	} else {
 		if (out.file->f_flags & O_NONBLOCK)
 			fl |= SPLICE_F_NONBLOCK;
diff --git a/fs/splice.c b/fs/splice.c
index 3bb4936f8b70..261adfdfed9d 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -1157,8 +1157,12 @@  static int direct_splice_actor(struct pipe_inode_info *pipe,
 			       struct splice_desc *sd)
 {
 	struct file *file = sd->u.file;
+	long ret;
 
-	return do_splice_from(pipe, file, sd->opos, sd->total_len, sd->flags);
+	file_start_write(file);
+	ret = do_splice_from(pipe, file, sd->opos, sd->total_len, sd->flags);
+	file_end_write(file);
+	return ret;
 }
 
 static int copy_file_range_splice_actor(struct pipe_inode_info *pipe,
@@ -1240,6 +1244,8 @@  EXPORT_SYMBOL(do_splice_direct);
  *
  * Description:
  *    For use by generic_copy_file_range() and ->copy_file_range() methods.
+ *    Like do_splice_direct(), but vfs_copy_file_range() already called
+ *    start_file_write() on @out file.
  *
  * Callers already called rw_verify_area() on the entire range.
  */