[Devel] [PATCH RHEL7 COMMIT] fs/fuse kio_pcs: prevention of falling kio reqs to usermode

Konstantin Khorenko khorenko at virtuozzo.com
Tue Oct 9 12:19:02 MSK 2018


The commit is pushed to "branch-rh7-3.10.0-862.14.4.vz7.72.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-862.14.4.vz7.72.4
------>
commit 9e31b0f6c01f532cfedf72acbd67f1f0c50fa2bc
Author: Pavel Butsykin <pbutsykin at virtuozzo.com>
Date:   Tue Oct 9 12:18:59 2018 +0300

    fs/fuse kio_pcs: prevention of falling kio reqs to usermode
    
    As it turned out, some requests may come without io_inode and is a valid request
    for usermod, but not for kio. We can't pass kio requests in usermod which can
    break consistency. Let's add check for the validation of this case.
    
    Signed-off-by: Pavel Butsykin <pbutsykin at virtuozzo.com>
    Reviewed-by: Kirill Tkhai <ktkhai at virtuozzo.com>
    
    =====================
    Patchset description:
    
    Fuse: Fix IOCB_CMD_UNMAP_ITER handling to prevent cache inconsistency
    
    https://pmc.acronis.com/browse/VSTOR-15447
    
    Ploop can asynchronously unmap regions by sending IOCB_CMD_UNMAP_ITER, but this
    command isn't quite correctly interpreted in Fuse. Moreover, in Fast-path mode,
    fallocate(FALLOC_FL_PUNCH_HOLE|FALLOC_FL_ZERO_RANGE) falls to fuse user daemon
    and it can lead to data corruption.
    
    Let's fix it.
    
    Pavel Butsykin (4):
      fs/fuse: fix mutually exclusive flags in fuse_send_unmap()
      fs/fuse: initialize req->io_inode in fuse_send_unmap()
      fs/fuse kio_pcs: split kpcs_req_send()
      fs/fuse kio_pcs: prevention of falling kio reqs to usermode
---
 fs/fuse/kio/pcs/pcs_fuse_kdirect.c | 29 ++++++++++++++++++++---------
 1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
index 3143d24d7544..d3a038e9921f 100644
--- a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
+++ b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
@@ -1028,9 +1028,8 @@ static void _pcs_grow_end(struct fuse_conn *fc, struct fuse_req *req)
 	kpcs_setattr_end(fc, req);
 }
 
-static void pcs_kio_setattr_handle(struct fuse_req *req)
+static void pcs_kio_setattr_handle(struct fuse_inode *fi, struct fuse_req *req)
 {
-	struct fuse_inode *fi = get_fuse_inode(req->io_inode);
 	struct pcs_fuse_req *r = pcs_req_from_fuse(req);
 	struct fuse_setattr_in *inarg = (void*) req->in.args[0].value;
 	struct pcs_dentry_info *di;
@@ -1065,6 +1064,8 @@ static void pcs_kio_setattr_handle(struct fuse_req *req)
 
 static int pcs_kio_classify_req(struct fuse_conn *fc, struct fuse_req *req)
 {
+	struct fuse_inode *fi = get_fuse_inode(req->io_inode);
+
 	switch (req->in.h.opcode) {
 	case FUSE_READ:
 	case FUSE_WRITE:
@@ -1073,7 +1074,10 @@ static int pcs_kio_classify_req(struct fuse_conn *fc, struct fuse_req *req)
 	case FUSE_FALLOCATE:
 		break;
 	case FUSE_SETATTR:
-		pcs_kio_setattr_handle(req);
+		if (unlikely(!fi || !fi->private))
+			goto fail;
+
+		pcs_kio_setattr_handle(fi, req);
 		return 1;
 	case FUSE_IOCTL: {
 		struct fuse_ioctl_in const *inarg = req->in.args[0].value;
@@ -1087,13 +1091,22 @@ static int pcs_kio_classify_req(struct fuse_conn *fc, struct fuse_req *req)
 		return 1;
 	}
 
+	if (unlikely(!fi || !fi->private))
+		goto fail;
+
 	return 0;
+
+fail:
+	WARN_ONCE(1, "Fuse kio: req cannot be processed w/o inode\n");
+	req->out.h.error = -EINVAL;
+	request_end(fc, req);
+	return -EINVAL;
 }
 
 static int kpcs_req_send(struct fuse_conn* fc, struct fuse_req *req, bool bg, bool lk)
 {
 	struct pcs_fuse_cluster *pfc = (struct pcs_fuse_cluster*)fc->kio.ctx;
-	struct fuse_inode *fi = get_fuse_inode(req->io_inode);
+	int ret;
 
 	if (!fc->initialized || fc->conn_error)
 		return 1;
@@ -1107,11 +1120,9 @@ static int kpcs_req_send(struct fuse_conn* fc, struct fuse_req *req, bool bg, bo
 
 	TRACE(" Enter req:%p op:%d end:%p bg:%d lk:%d\n", req, req->in.h.opcode, req->end, bg, lk);
 
-	if (!fi || !fi->private)
-		return 1;
-
-	if (pcs_kio_classify_req(fc, req))
-		return 1;
+	ret = pcs_kio_classify_req(fc, req);
+	if (ret)
+		return ret < 0 ? 0 : 1;
 
 	/* request_end below will do fuse_put_request() */
 	if (!bg)



More information about the Devel mailing list