On Wed, Jul 29, 2026 at 10:00:13AM -0400, Peter Xu wrote:
> On Tue, Jul 28, 2026 at 05:57:56PM -0300, Fabiano Rosas wrote:
> > Peter Xu <[email protected]> writes:
> >
> > > 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.
> > > ---
> > > 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), \
> > > -}
> >
> > Why don't you just leave this macro around, put a "do not use" comment
> > above it and remove the type check for this one instance? You're already
> > checking against INT32_MAX during pre_load.
>
> What's the benefit of keeping this global macro if we only allow one user
> to use it?
>
> Commenting to say "don't use" is not guaranteeing anything, at least we
> should also rename the macro to some weird name to make people notice.
> Keeping the macro name like this OTOH suggests abuse. But if we think it
> should only be used in 1 place, fixing the one place might be better?
>
> >
> > We could even lift that check up into vmstate_pre_load and reject
> > there globally, don't even touch the virtio code.
>
> Personally, I don't like the idea leaking VBUFFER impl details into
> vmstate_pre_load() which is so far only a wrapper, especially only for this
> one instance.. which we do not suggest future users to use.
>
> If we go this route, I'd rather merge Michael's version to support u64,
> even if we don't need a u64 size. But I really don't want to introduce yet
> another VMS flag just for this... we'll have no real use if we have noticed
> this problem when the vhost inflight patch was reviewed. It will be a
> uint32_t or int32_t already. I just can't come up with some users need
> size >2G.
It's not that size needs to be >2G. But it might be nice for some code
to keep it as u64 for its own reasons to avoid issues like overruns
during math ops.
>
> The recent AI reports just make such feeling stronger: we just used the
> reason "max 2G, should be fine" when AI reports that unlimited size
> allocation problem, now we will need to wait for another AI report which
> says "it's not 2G anymore, 1<<64-1 that is", if we don't come up with a
> proper limit for VMSD field allocations.
>
> >
> >
> > /rant
> > ... what's even the point of having per-type variants of VMSTATE macros
> > that exist just to type-check the extra offsets (.num_offset,
> > .size_offset as opposed to .offset)?
> >
> > For instance, look at vmstate_n_elems casting opaque data to 32 and
> > sub-32bit size and assigning to an int! What do we gain from that? It's
> > circular reasoning that does nothing aside from bothering the person
> > writing the vmstate.
> >
> > Right? Maybe I'm missing something, it's the end of the day already.
>
> I can also only guess, which is.. I believe Michael was right, the initial
> vmstate code was simply broken where it should have considered the type of
> size fields of all kinds, but forgot, and it just worked because nobody
> needed u64 for a size field.
>
> Said that, I still want to see if we can avoid introducing that at all. To
> me, I still prefer this patch (after fixing pre_save()..). But let me know
> if I didn't convince any of you.. we can discuss.
>
> Thanks,
The issue is that 2G is not a sane limit either.
So what we really want is a macro that supplies
a size limit. That would be an improvement.
> >
> > > -
> > > #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()
> > > }
> > > };
> >
>
> --
> Peter Xu