[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