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 <kuz...@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 <kuz...@acronis.com> Signed-off-by: Vasily Averin <v...@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; } _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel