This patch is migration only change and hasn't been reviewed.  Give us a
few days to review it?
Can't do it now because it is on cellphone and I just bathed my son.

Please hold off merging.

Thanks.

On Sun, Jul 26, 2026, 5:29 p.m. Michael S. Tsirkin <[email protected]> wrote:

> vhost user currently saves the inflight buffer to the migration stream
> using VMSTATE_VBUFFER_UINT64. The size is controlled by the vhost-user
> backend.
>
> But the implementation of that is broken if size is >2G: it stores the
> buffer size in a uint64_t field, but vmstate_size() always reads the
> size field as int32_t regardless of the macro used. This, in turn,
> causes negative or truncated lengths on load, leading to undersized
> allocations and down the road out-of-bounds buffer access.
>
> There's no practical reason to support such large sizes, so it's enough
> to validate: read the field as uint64_t when VMS_VBUFFER_UINT64 is set,
> reject negative or oversized values, and propagate errors to
> vmstate_load_vmsd().
>
> Note: the large value is coming from the backend, not guest, so this
> shouldn't be considered a security issue. The CVE was assigned before
> the qemu security policy was updated to exclude this class of bugs.
>
> Fixes: CVE-2026-6426
> Fixes: f6fdd8b2bd ("vmstate: introduce VMSTATE_VBUFFER_UINT64")
> Cc: Alexandr Moshkov <[email protected]>
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3675
> Signed-off-by: Michael S. Tsirkin <[email protected]>
> Message-ID: <
> 801b1501ee10241f7ac49a10570d548352b36347.1784890517.git....@redhat.com>
> ---
>  include/migration/vmstate.h |  5 ++++-
>  migration/vmstate.c         | 31 ++++++++++++++++++++++++++++---
>  2 files changed, 32 insertions(+), 4 deletions(-)
>
> diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> index 1b7f295417..e7095cd977 100644
> --- a/include/migration/vmstate.h
> +++ b/include/migration/vmstate.h
> @@ -168,6 +168,9 @@ enum VMStateFlags {
>       */
>      VMS_ARRAY_OF_POINTER_AUTO_ALLOC = 0x10000,
>
> +    /* Use a uint64_t size field for VMS_VBUFFER instead of int32_t. */
> +    VMS_VBUFFER_UINT64              = 0x40000,
> +
>      /* Marker for end of list */
>      VMS_END                         = 0x20000,
>  };
> @@ -788,7 +791,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
>      .field_exists = (_test),                                         \
>      .size_offset  = vmstate_offset_value(_state, _field_size, uint64_t),\
>      .info         = &vmstate_info_buffer,                            \
> -    .flags        = VMS_VBUFFER | VMS_POINTER,                       \
> +    .flags        = VMS_VBUFFER | VMS_VBUFFER_UINT64 | VMS_POINTER,  \
>      .offset       = offsetof(_state, _field),                        \
>  }
>
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index 50ebe37845..d7f03a9f5b 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -103,10 +103,24 @@ static int vmstate_size(void *opaque, const
> VMStateField *field)
>      int size;
>
>      if (field->flags & VMS_VBUFFER) {
> -        size = *(int32_t *)(opaque + field->size_offset);
> -        if (field->flags & VMS_MULTIPLY) {
> -            size *= field->size;
> +        uint64_t usize64;
> +
> +        if (field->flags & VMS_VBUFFER_UINT64) {
> +            usize64 = *(uint64_t *)(opaque + field->size_offset);
> +        } else {
> +            int32_t ssize32 = *(int32_t *)(opaque + field->size_offset);
> +            if (ssize32 < 0) {
> +                return -1;
> +            }
> +            usize64 = ssize32;
>          }
> +        if (field->flags & VMS_MULTIPLY) {
> +            usize64 *= field->size;
> +        }
> +        if (usize64 > INT_MAX) {
> +            return -1;
> +        }
> +        size = usize64;
>      } else if (field->flags & VMS_ARRAY_OF_POINTER) {
>          /*
>           * For an array of pointer, the each element is always size of a
> @@ -337,6 +351,11 @@ bool vmstate_load_vmsd(QEMUFile *f, const
> VMStateDescription *vmsd,
>              void *first_elem = opaque + field->offset;
>              int i, n_elems = vmstate_n_elems(opaque, field);
>              int size = vmstate_size(opaque, field);
> +            if (size < 0) {
> +                error_setg(errp, "VMState field '%s': invalid size",
> +                           field->name);
> +                return false;
> +            }
>
>              vmstate_handle_alloc(first_elem, field, opaque);
>              if (field->flags & VMS_POINTER) {
> @@ -661,6 +680,12 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const
> VMStateDescription *vmsd,
>              bool use_dynamic_array =
>                  field->flags & VMS_ARRAY_OF_POINTER_AUTO_ALLOC;
>
> +            if (size < 0) {
> +                error_setg(errp, "VMState field '%s': invalid size",
> +                           field->name);
> +                ok = false;
> +                goto out;
> +            }
>              trace_vmstate_save_state_loop(vmsd->name, field->name,
> n_elems);
>              if (field->flags & VMS_POINTER) {
>                  first_elem = *(void **)first_elem;
> --
> MST
>
>

Reply via email to