On Tue, Jul 28, 2026 at 11:39:42AM -0400, Peter Xu wrote:
> It was overlooked that VMSTATE_VBUFFER_UINT64() won't really work with an
> uint64_t, as vmstate core only treats the size as 32bits, and maximum
> INT32_MAX (see vmstate_size()).
>
> Considering that we do not need real 64bits for the size, stick with the 2G
> limit, converting the size field into 32bits.
>
> Since we can't touch the wire protocol on migration from an old QEMU, we
> can't directly modify the type of size to uint32_t. Instead, we need to
> introduce a temporary variable for this extremely rare issue __size_32bits
> to be used only for VMSTATE_VBUFFER_UINT32(). Document it and name it
> weird enough so people won't get confused on having two size variables.
>
> Remove VMSTATE_VBUFFER_UINT64() altogether, because it was never going to
> be used right. It means QEMU will only support 2G max for VMS_VBUFFER.
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3675
> Reported-by: 김승중 <[email protected]>
> Cc: Alexandr Moshkov <[email protected]>
> Cc: Michael S. Tsirkin <[email protected]>
> Cc: Fabiano Rosas <[email protected]>
> Fixes: 3a80ff0721 ("vhost: add vmstate for inflight region with inner buffer")
> Signed-off-by: Peter Xu <[email protected]>
> ---
>
> PS1: I only did smoke test as I'm not fluent with vhost inflight feature.
> Please kindly try it out if possible. In general, migrations from older
> QEMU should work even after applied. One can also treat this as partly-RFC
> from that.
>
> PS2: Michael, we have just discussed what we should define as CVE for
> migration, and this one shouldn't fall into CVE category, please refer to:
>
> https://lore.kernel.org/r/[email protected]
>
> So I didn't yet attach CVE tag. Please correct if I'm wrong, thanks.
I agree.
> ---
> include/hw/virtio/vhost.h | 5 +++++
> include/migration/vmstate.h | 10 ----------
> hw/virtio/vhost.c | 19 ++++++++++++++-----
> 3 files changed, 19 insertions(+), 15 deletions(-)
>
> diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
> index 684bafcaad..1d1cc24c04 100644
> --- a/include/hw/virtio/vhost.h
> +++ b/include/hw/virtio/vhost.h
> @@ -17,6 +17,11 @@ struct vhost_inflight {
> int fd;
> void *addr;
> uint64_t size;
> + /*
> + * This is a temporary variable only used during migration loading to
> + * satisfy VMSTATE_VBUFFER_UINT32() typing. Please use @size otherwise.
> + */
> + uint32_t __size_32bits;
> uint64_t offset;
> uint16_t queue_size;
> };
> diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> index 1b7f295417..a349b2d84a 100644
> --- a/include/migration/vmstate.h
> +++ b/include/migration/vmstate.h
> @@ -782,16 +782,6 @@ extern const VMStateInfo vmstate_info_g_byte_array;
> .offset = offsetof(_state, _field), \
> }
>
> -#define VMSTATE_VBUFFER_UINT64(_field, _state, _version, _test, _field_size)
> { \
> - .name = (stringify(_field)), \
> - .version_id = (_version), \
> - .field_exists = (_test), \
> - .size_offset = vmstate_offset_value(_state, _field_size, uint64_t),\
> - .info = &vmstate_info_buffer, \
> - .flags = VMS_VBUFFER | VMS_POINTER, \
> - .offset = offsetof(_state, _field), \
> -}
> -
> #define VMSTATE_VBUFFER_ALLOC_UINT32(_field, _state, _version, \
> _test, _field_size) { \
> .name = (stringify(_field)), \
> diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
> index af41841b52..e7c570d0f5 100644
> --- a/hw/virtio/vhost.c
> +++ b/hw/virtio/vhost.c
> @@ -2022,15 +2022,24 @@ void vhost_get_features_ex(struct vhost_dev *hdev,
> static bool vhost_inflight_buffer_pre_load(void *opaque, Error **errp)
> {
> struct vhost_inflight *inflight = opaque;
> -
> int fd = -1;
> - void *addr = qemu_memfd_alloc("vhost-inflight", inflight->size,
> - F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL,
> - &fd, errp);
> + void *addr;
> +
> + if (inflight->size > INT32_MAX) {
> + error_setg(errp, "inflight size '%"PRIu64"' exceeds "
> + "migration limit '%"PRIu32"'", inflight->size, INT32_MAX);
> + return false;
> + }
> +
> + addr = qemu_memfd_alloc("vhost-inflight", inflight->size,
> + F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL,
> + &fd, errp);
> if (!addr) {
> return false;
> }
>
> + /* Only used in VMSTATE_VBUFFER_UINT32() */
> + inflight->__size_32bits = inflight->size;
> inflight->offset = 0;
> inflight->addr = addr;
> inflight->fd = fd;
> @@ -2042,7 +2051,7 @@ const VMStateDescription
> vmstate_vhost_inflight_region_buffer = {
> .name = "vhost-inflight-region/buffer",
> .pre_load_errp = vhost_inflight_buffer_pre_load,
> .fields = (const VMStateField[]) {
> - VMSTATE_VBUFFER_UINT64(addr, struct vhost_inflight, 0, NULL, size),
> + VMSTATE_VBUFFER_UINT32(addr, struct vhost_inflight, 0, NULL,
> __size_32bits),
> VMSTATE_END_OF_LIST()
> }
> };
> --
> 2.54.0