[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