[Devel] [PATCH VZ9 1/2] fs/fuse kio: get refcnt on pages for zero-copy read via krpc.
Alexey Kuznetsov
kuznet at virtuozzo.com
Tue Dec 10 15:29:26 MSK 2024
Acknowledged
On Mon, Dec 9, 2024 at 4:56 PM Liu Kui <kui.liu at 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.liu at 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)
More information about the Devel
mailing list