Acknowledged On Mon, Dec 9, 2024 at 4:56 PM Liu Kui <kui....@virtuozzo.com> wrote: > > 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