Re: [PATCH RFC 11/15] hw/virtio/vhost-user: create isolation region

Connor Kite <[email protected]> Sun, 2 Aug 2026 18:28:47 -0700
Newsgroups dev.linux.lists.virtio-fs,org.nongnu.qemu-devel
Message-ID <CA+spn3oGVk0YNKNcFpiCWqg_EPNjeM9W2UwGwepRND27GP8ydg@mail.gmail.com>
On Fri, Jul 24, 2026 at 5:53=E2=80=AFAM Akihiko Odaki
<[email protected]> wrote:
...
>
> u->iso_memory is zeroed before they are read here.
>
...
>
> This should be new_entry as per: docs/devel/style.rst
>
...
>
> The check of u->iso_memory.base_addr is redundant;
> cleanup_isolation_regions() performs that check.
>
> cleanup_isolation_regions() also calls vhost_iova_tree_delete(), so the
> duplicate call of the function leads to use-after-free and double-free.
>

Thanks!  Will fix these.

>
> I wonder if it is fine to dismiss return values of
> vhost_iova_tree_map_alloc() and vhost_iova_tree_map_alloc_gpa().
>

It probably makes sense to add a check here and return with the value on er=
ror

On Fri, Jul 24, 2026 at 5:53=E2=80=AFAM Akihiko Odaki
<[email protected]> wrote:
>
> On 2026/07/24 7:30, Connor Kite wrote:
> > If memory isolation mode is active for the vhost-user device adds
> > features to:
> > - Gather the size required for bounce buffers and vrings in shared
> >    isolation region
> > - Allocate the required space in an anonymous file
> > - Create a vhost-iova-tree with space to map entire isolation region
> > - Map guest memory regions and shared vrings into the tree
> > - Release these resources upon backend cleanup
> >
> > Signed-off-by: Connor Kite <[email protected]>
> > ---
> >   hw/virtio/vhost-user.c | 129 ++++++++++++++++++++++++++++++++++++++++=
+++++++++
> >   1 file changed, 129 insertions(+)
> >
> > diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
> > index f296b63fb9..710cf966f8 100644
> > --- a/hw/virtio/vhost-user.c
> > +++ b/hw/virtio/vhost-user.c
> > @@ -18,6 +18,7 @@
> >   #include "hw/virtio/vhost-backend.h"
> >   #include "hw/virtio/virtio.h"
> >   #include "hw/virtio/virtio-net.h"
> > +#include "hw/virtio/vhost-iova-tree.h"
> >   #include "chardev/char-fe.h"
> >   #include "io/channel-socket.h"
> >   #include "system/kvm.h"
> > @@ -25,6 +26,7 @@
> >   #include "qemu/main-loop.h"
> >   #include "qemu/uuid.h"
> >   #include "qemu/sockets.h"
> > +#include "qemu/memfd.h"
> >   #include "system/runstate.h"
> >   #include "system/cryptodev.h"
> >   #include "migration/postcopy-ram.h"
> > @@ -320,6 +322,13 @@ static VhostUserMsg m __attribute__ ((unused));
> >   /* The version of the protocol we support */
> >   #define VHOST_USER_VERSION    (0x1)
> >
> > +typedef struct IsolationRegion {
> > +    uint64_t base_addr;
> > +    uint64_t vring_base_addr;
> > +    uint64_t size;
> > +    int iso_fd;
> > +} IsolationRegion;
> > +
> >   struct vhost_user {
> >       struct vhost_dev *dev;
> >       /* Shared between vhost devs of the same virtio device */
> > @@ -353,6 +362,10 @@ struct vhost_user {
> >        * by the backend (see @features).
> >        */
> >       uint64_t protocol_features;
> > +
> > +    /* Isolated memory data*/
> > +    struct IsolationRegion iso_memory;
> > +    VhostIOVATree *iso_iova_tree;
> >   };
> >
> >   struct scrub_regions {
> > @@ -1109,6 +1122,121 @@ static int vhost_user_set_mem_table_postcopy(st=
ruct vhost_dev *dev,
> >       return 0;
> >   }
> >
> > +/* TODO: Is there any notifier cleanup required here?*/
> > +static void cleanup_isolation_regions(struct vhost_dev *dev)
> > +{
> > +    struct vhost_user *u =3D dev->opaque;
> > +    if (u->iso_memory.base_addr) {
> > +        vhost_iova_tree_delete(u->iso_iova_tree);
> > +        u->iso_iova_tree =3D NULL;
> > +        memset(&u->iso_memory, 0, sizeof(IsolationRegion));
> > +        qemu_memfd_free((gpointer) u->iso_memory.base_addr, u->iso_mem=
ory.size,
> > +                         u->iso_memory.iso_fd);
>
> u->iso_memory is zeroed before they are read here.
>
> > +        u->iso_memory.base_addr =3D 0;
> > +    }
> > +}
> > +
> > +__attribute__((unused))
> > +static int init_isolation_regions(struct vhost_dev *dev,
> > +                                  VhostUserMsg *msg,
> > +                                  int *fds, size_t *fd_num)
> > +{
> > +    Error *err =3D NULL;
> > +    struct vhost_user *u =3D dev->opaque;
> > +    uint32_t nregions =3D dev->mem->nregions;
> > +    uint64_t buffer_reg_size =3D 0;
> > +    DMAMap newEntry =3D {
>
> This should be new_entry as per: docs/devel/style.rst
>
> > +        .perm =3D IOMMU_RW
> > +    };
> > +    g_autoptr(GArray) buffer_regions =3D
> > +        g_array_new(FALSE, TRUE, sizeof(DMAMap));
> > +
> > +    msg->hdr.request =3D VHOST_USER_SET_MEM_TABLE;
> > +
> > +    /* In case of reset, clear old regions*/
> > +    if (u->iso_memory.base_addr !=3D 0) {
> > +        cleanup_isolation_regions(dev);
> > +        vhost_iova_tree_delete(u->iso_iova_tree);
> > +    }
>
> The check of u->iso_memory.base_addr is redundant;
> cleanup_isolation_regions() performs that check.
>
> cleanup_isolation_regions() also calls vhost_iova_tree_delete(), so the
> duplicate call of the function leads to use-after-free and double-free.
>
> > +
> > +    /* Gather information for bounce buffers to be mapped */
> > +    for (int i =3D 0; i < nregions; i++) {
> > +        struct vhost_memory_region *dev_region =3D &dev->mem->regions[=
i];
> > +        hwaddr size =3D ROUND_UP(dev_region->memory_size,
> > +                      qemu_real_host_page_size());
> > +        newEntry.translated_addr =3D dev_region->guest_phys_addr;
> > +        newEntry.size =3D size - 1;
> > +        buffer_reg_size +=3D size;
> > +        g_array_append_val(buffer_regions, newEntry);
> > +    }
> > +
> > +    int num;
> > +    size_t desc_size;
> > +    size_t avail_size;
> > +    size_t driver_area_size;
> > +    size_t device_area_size;
> > +    size_t total_vring_size =3D 0;
> > +    size_t total_mmap_size;
> > +
> > +    /* Get space required for all vrings */
> > +    for (int j =3D 0; j < dev->nvqs; j++) {
> > +        num =3D virtio_queue_get_num(dev->vdev, dev->vq_index + j);
> > +        desc_size =3D sizeof(vring_desc_t) * num;
> > +        avail_size =3D offsetof(vring_avail_t, ring[num]) +
> > +                        sizeof(uint16_t);
> > +        driver_area_size =3D ROUND_UP(desc_size + avail_size,
> > +                            qemu_real_host_page_size());
> > +        device_area_size =3D ROUND_UP(offsetof(vring_used_t, ring[num]=
) +
> > +                                    sizeof(uint16_t),
> > +                                    qemu_real_host_page_size());
> > +        total_vring_size +=3D driver_area_size + device_area_size;
> > +    }
> > +
> > +    total_mmap_size =3D buffer_reg_size + total_vring_size;
> > +
> > +    /* Allocate and map an anonymous file to hold the isolation region=
 */
> > +    u->iso_memory.base_addr =3D (uint64_t) qemu_memfd_alloc("iso_r",
> > +                              total_mmap_size,
> > +                              F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEA=
L,
> > +                              &u->iso_memory.iso_fd, &err);
> > +    u->iso_memory.size =3D total_mmap_size;
> > +
> > +    if (err) {
> > +        error_report_err(err);
> > +        cleanup_isolation_regions(dev);
> > +        return -1;
> > +    }
> > +
> > +    uint64_t last_addr =3D int128_get64(int128_add(u->iso_memory.base_=
addr,
> > +                                                 total_mmap_size - 1))=
;
> > +
> > +    /*
> > +     * Instantiates iova tree sized to map bounce buffers and vrings t=
o the
> > +     * isolation region in host va.
> > +     */
> > +    u->iso_iova_tree =3D vhost_iova_tree_new(u->iso_memory.base_addr, =
last_addr);
> > +
> > +    assert(&u->iso_memory.iso_fd >=3D 0);
> > +    DMAMap *map;
> > +    DMAMap vring_map =3D {
> > +        .perm =3D IOMMU_RW,
> > +        .size =3D total_vring_size - 1,
> > +        /*vrings are allocated on tree first, so will be assigned base=
 addr*/
> > +        .translated_addr =3D u->iso_memory.base_addr
> > +    };
> > +
> > +    vhost_iova_tree_map_alloc(u->iso_iova_tree, &vring_map,
> > +                              vring_map.translated_addr);
> > +    u->iso_memory.vring_base_addr =3D vring_map.iova;
> > +    for (int i =3D 0; i < buffer_regions->len; i++) {
> > +        map =3D &g_array_index(buffer_regions, DMAMap, i);
> > +        vhost_iova_tree_map_alloc_gpa(u->iso_iova_tree, map,
> > +                                      map->translated_addr);
>
> I wonder if it is fine to dismiss return values of
> vhost_iova_tree_map_alloc() and vhost_iova_tree_map_alloc_gpa().
>
> Regards,
> Akihiko Odaki
>
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> >   static int vhost_user_set_mem_table(struct vhost_dev *dev,
> >                                       struct vhost_memory *mem)
> >   {
> > @@ -2681,6 +2809,7 @@ static int vhost_user_backend_cleanup(struct vhos=
t_dev *dev)
> >       g_free(u->region_rb_offset);
> >       u->region_rb_offset =3D NULL;
> >       u->region_rb_len =3D 0;
> > +    cleanup_isolation_regions(dev);
> >       g_free(u);
> >       dev->opaque =3D 0;
> >
> >
>