Pages passed to krpc for zero-copy read could be released before the
krpc request completes, for example in case of aborting a file that has
ongoing read request issued by the page cache, pages used by these read
requests could be accessed later by krpc after they are released.
So to avoid such use-after-free issue, krpc need to get extra refcnt on
these pages.

Related to #VSTOR-95997
Signed-off-by: Liu Kui <kui....@virtuozzo.com>
---
 fs/fuse/kio/pcs/pcs_krpc.c | 68 ++++++++++++++++++++++++--------------
 fs/fuse/kio/pcs/pcs_krpc.h |  2 ++
 2 files changed, 45 insertions(+), 25 deletions(-)

diff --git a/fs/fuse/kio/pcs/pcs_krpc.c b/fs/fuse/kio/pcs/pcs_krpc.c
index 9af1c93b20ed..ab2534f7a952 100644
--- a/fs/fuse/kio/pcs/pcs_krpc.c
+++ b/fs/fuse/kio/pcs/pcs_krpc.c
@@ -17,6 +17,8 @@
 #include "pcs_req.h"
 #include "pcs_krpc.h"
 
+static void kreq_release_data_chunks(struct krpc_req *kreq);
+
 extern unsigned int pcs_krpc_version;
 
 struct kmem_cache *krpc_req_cachep;
@@ -69,27 +71,14 @@ static void krpc_req_complete(struct krpc_req *kreq, int 
error)
 {
        struct krpc_completion *comp = &kreq->completion;
        struct pcs_krpc *krpc = kreq->krpc;
-       int i;
 
        BUG_ON(!comp->xid);
 
        comp->result = error;
 
+       kreq_release_data_chunks(kreq);
        pcs_mr_put(kreq->hdr_chunk.mr);
 
-       for (i = 0; i < kreq->nr_data_chunks; i++) {
-               struct krpc_chunk *chunk;
-
-               chunk = &kreq->data_chunks[i];
-               if (chunk->type == KRPC_CHUNK_TYPE_UMEM)
-                       pcs_umem_release(chunk->umem);
-               else if (chunk->type == KRPC_CHUNK_TYPE_MR)
-                       pcs_mr_put(chunk->mr);
-       }
-
-       if (kreq->data_chunks != &kreq->inline_data_chunks[0])
-               kfree(kreq->data_chunks);
-
        spin_lock(&krpc->lock);
        list_del(&kreq->link);
 
@@ -309,6 +298,39 @@ static int pcs_krpc_ioctl_recv_msg(struct pcs_krpc *krpc, 
struct pcs_krpc_ioc_re
        return res;
 }
 
+static void kreq_release_data_chunks(struct krpc_req *kreq)
+{
+       struct krpc_chunk *chunk;
+       int i;
+
+       for (i = 0; i < kreq->nr_data_chunks; i++) {
+               chunk = &kreq->data_chunks[i];
+
+               switch (chunk->type) {
+               case KRPC_CHUNK_TYPE_UMEM:
+                       pcs_umem_release(chunk->umem);
+                       break;
+               case KRPC_CHUNK_TYPE_MR:
+                       pcs_mr_put(chunk->mr);
+                       break;
+               case KRPC_CHUNK_TYPE_ZC:
+                       int j, end;
+
+                       end = chunk->bvec_idx_start + chunk->nr_bvecs;
+                       for (j = chunk->bvec_idx_start; j < end; j++)
+                               put_page(kreq->data_bvecs[j].bv_page);
+
+                       break;
+               default:
+                       WARN_ON("Invalid chunk type");
+                       break;
+               }
+       }
+
+       if (kreq->data_chunks != &kreq->inline_data_chunks[0])
+               kfree(kreq->data_chunks);
+}
+
 
 static void kreq_prepare_data_buff(struct krpc_req *kreq, struct krpc_chunk 
*chunk)
 {
@@ -334,6 +356,8 @@ static void kreq_prepare_data_buff(struct krpc_req *kreq, 
struct krpc_chunk *chu
                        i++;
                }
 
+               chunk->bvec_idx_start = kreq->nr_data_bvecs;
+               chunk->nr_bvecs = 0;
                offset = pos;
                while (len) {
                        /* data bvec array overflow? */
@@ -341,13 +365,15 @@ static void kreq_prepare_data_buff(struct krpc_req *kreq, 
struct krpc_chunk *chu
 
                        bvec = &kreq->data_bvecs[kreq->nr_data_bvecs];
 
-                       bvec->bv_page = ia->ap.pages[i];
                        bvec->bv_offset = ia->ap.descs[i].offset + offset;
                        bvec->bv_len = len < (ia->ap.descs[i].length - offset) 
? len :
                                        (ia->ap.descs[i].length - offset);
+                       bvec->bv_page = ia->ap.pages[i];
+                       get_page(bvec->bv_page);
 
                        len -= bvec->bv_len;
                        kreq->nr_data_bvecs++;
+                       chunk->nr_bvecs++;
                        i++;
                        offset = 0;
                }
@@ -458,6 +484,7 @@ static int pcs_krpc_ioctl_send_msg(struct pcs_krpc *krpc, 
struct pcs_krpc_ioc_se
                case PCS_KRPC_BUF_TYPE_ZC: {
                        struct pcs_krpc_buf_desc_zc *chunk_bdzc = (struct 
pcs_krpc_buf_desc_zc *)chunk_bd;
 
+                       WARN_ON(iocmsg->nr_data_chunks != 1);
                        chunk->type = KRPC_CHUNK_TYPE_ZC;
                        chunk->addr = chunk_bdzc->offset;
                        chunk->req = fuse_dev_find_request(chunk_bdzc->devfd, 
chunk_bdzc->unique);
@@ -509,18 +536,9 @@ static int pcs_krpc_ioctl_send_msg(struct pcs_krpc *krpc, 
struct pcs_krpc_ioc_se
        return 0;
 
 err_free_data_chunk:
-       for (i = 0; i < kreq->nr_data_chunks; i++) {
-               chunk = &kreq->data_chunks[i];
-               if (chunk->type == KRPC_CHUNK_TYPE_UMEM)
-                       pcs_umem_release(chunk->umem);
-               else if (chunk->type == KRPC_CHUNK_TYPE_MR)
-                       pcs_mr_put(chunk->mr);
-       }
+       kreq_release_data_chunks(kreq);
        pcs_mr_put(kreq->hdr_chunk.mr);
 
-       if (kreq->data_chunks != &kreq->inline_data_chunks[0])
-               kfree(kreq->data_chunks);
-
 err_free_kreq:
        krpc_req_free(kreq);
        return res;
diff --git a/fs/fuse/kio/pcs/pcs_krpc.h b/fs/fuse/kio/pcs/pcs_krpc.h
index 23b170c00d7a..556021b1d1f5 100644
--- a/fs/fuse/kio/pcs/pcs_krpc.h
+++ b/fs/fuse/kio/pcs/pcs_krpc.h
@@ -25,6 +25,8 @@ struct krpc_chunk {
                struct pcs_umem *umem;
                struct fuse_req *req;
        };
+       u16      bvec_idx_start;
+       u16      nr_bvecs;
 };
 
 #define PCS_KRPC_HASH_SIZE     1024
-- 
2.39.5 (Apple Git-154)

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to