[Devel] [PATCH RHEL8 COMMIT] fs/fuse: released handle could be used in fiemap

Konstantin Khorenko khorenko at virtuozzo.com
Fri Apr 23 11:55:02 MSK 2021


The commit is pushed to "branch-rh8-4.18.0-240.1.1.vz8.5.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh8-4.18.0-240.1.1.vz8.5.19
------>
commit e09b1fecb7cb6aee6a822e7a1dfb3ea8c97020d7
Author: Alexey Kuznetsov <kuznet at acronis.com>
Date:   Fri Apr 23 11:55:02 2021 +0300

    fs/fuse: released handle could be used in fiemap
    
    It was missed that handle must be held while passing request to user,
    otherwise vstorage-mount can crash.
    
    https://pmc.acronis.com/browse/VSTOR-42949
    Signed-off-by: Alexey Kuznetsov <kuznet at acronis.com>
    Signed-off-by: Vasily Averin <vvs at virtuozzo.com>
---
 fs/fuse/file.c | 39 +++++++++++++++++++++++++++++----------
 1 file changed, 29 insertions(+), 10 deletions(-)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index b0db23314971..e08e557ecaf2 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -3860,8 +3860,9 @@ static int fuse_request_fiemap(struct inode *inode, u32 cur_max,
 {
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	struct fuse_inode *fi = get_fuse_inode(inode);
+	struct fuse_file *ff = NULL;
 	struct fuse_args_pages ap = {};
-	struct fuse_ioctl_in inarg;
+	struct fuse_ioctl_in inarg = (struct fuse_ioctl_in) { .cmd = FS_IOC_FIEMAP };
 	struct fuse_ioctl_out outarg;
 	struct fiemap ifiemap;
 	struct fiemap ofiemap;
@@ -3871,11 +3872,30 @@ static int fuse_request_fiemap(struct inode *inode, u32 cur_max,
 
 	err = 0;
 	spin_lock(&fi->lock);
-	if (!list_empty(&fi->write_files)) {
-		struct fuse_file *ff = list_entry(fi->write_files.next, struct fuse_file, write_entry);
+	/* Kernel API is weird in this place, we have to find a file associated with this inode.
+	 * This problem is similar to writepage routines, but it is much worse. When doing
+	 * writepage, we have some file open for write and can take any file from write_files
+	 * and it is safe to keep it with get/put. But here we can have the file open for read
+	 * and no files open for write. Even worse, if we select a random file open for write,
+	 * it can be closed by user from another thread and its close will violate close_wait requirement.
+	 * So, we have to search for a read-only file first and fallback to writable only
+	 * if there is no such file.
+	 */
+	if (!list_empty(&fi->rw_files)) {
+		struct fuse_file *t_ff;
+		list_for_each_entry(t_ff, &fi->rw_files, rw_entry) {
+			if (list_empty(&t_ff->write_entry)) {
+				ff = t_ff;
+				break;
+			}
+		}
+		if (!ff)
+			ff = list_entry(fi->rw_files.next, struct fuse_file, rw_entry);
+		fuse_file_get(ff);
 		inarg.fh = ff->fh;
-	} else if (!list_empty(&fi->rw_files)) {
-		struct fuse_file *ff = list_entry(fi->rw_files.next, struct fuse_file, rw_entry);
+	} else if (!list_empty(&fi->write_files)) {
+		ff = list_entry(fi->write_files.next, struct fuse_file, write_entry);
+		fuse_file_get(ff);
 		inarg.fh = ff->fh;
 	} else {
 		err = -EINVAL;
@@ -3884,10 +3904,6 @@ static int fuse_request_fiemap(struct inode *inode, u32 cur_max,
 	if (err)
 		return err;
 
-	inarg.cmd = FS_IOC_FIEMAP;
-	inarg.arg = 0;
-	inarg.flags = 0;
-
 	ifiemap.fm_start = *start_p;
 	ifiemap.fm_length = *len_p;
 	ifiemap.fm_flags = dest->fi_flags;
@@ -3899,8 +3915,10 @@ static int fuse_request_fiemap(struct inode *inode, u32 cur_max,
 		npages = (cur_max*sizeof(struct fiemap_extent) + PAGE_SIZE - 1) / PAGE_SIZE;
 
 	ap.pages = fuse_pages_alloc(fc->max_pages, GFP_KERNEL, &ap.descs);
-	if (!ap.pages)
+	if (!ap.pages) {
+		fuse_file_put(ff, false, false);
 		return -ENOMEM;
+	}
 
 	ap.args.io_inode = inode;
 	ap.args.opcode = FUSE_IOCTL;
@@ -3982,6 +4000,7 @@ static int fuse_request_fiemap(struct inode *inode, u32 cur_max,
 		__free_page(ap.pages[allocated]);
 		ap.pages[allocated] = NULL;
 	}
+	fuse_file_put(ff, false, false);
 	return err;
 }
 


More information about the Devel mailing list