From: Arun Menon <[email protected]> In GLib, GByteArray is an object managed by the library. Currently, migrating a GByteArray requires treating it as a raw C struct and using VMSTATE_VBUFFER_ALLOC_UINT32. For example, see vmstate_vdba in ui/vdagent.c
QEMU cannot pretend that GByteArray is a C struct and simply use VMS_ALLOC to g_malloc() the buffer. This is because, VMS_ALLOC blindly overwrites the data pointer with a newly allocated buffer, thereby leaking the previous memory. Besides, GLib tracks the array's capacity in a hidden alloc field. Bypassing GLib APIs leave this capacity out of sync with the newly allocated buffer, potentially leading to heap buffer overflows during subsequent g_byte_array_append() calls. This commit introduces VMSTATE_GBYTEARRAY which uses specific library API calls (g_byte_array_set_size()) to safely resize and populate the buffer. Signed-off-by: Arun Menon <[email protected]> Suggested-by: Marc-André Lureau <[email protected]> Reviewed-by: Marc-André Lureau <[email protected]> Reviewed-by: Peter Xu <[email protected]> Link: https://lore.kernel.org/qemu-devel/[email protected] Signed-off-by: Stefan Berger <[email protected]> --- include/migration/vmstate.h | 10 ++++++++++ migration/vmstate-types.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 0a8a2e85a6..1b7f295417 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -308,6 +308,7 @@ extern const VMStateInfo vmstate_info_bitmap; extern const VMStateInfo vmstate_info_qtailq; extern const VMStateInfo vmstate_info_gtree; extern const VMStateInfo vmstate_info_qlist; +extern const VMStateInfo vmstate_info_g_byte_array; #define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0) /* @@ -957,6 +958,15 @@ extern const VMStateInfo vmstate_info_qlist; .start = offsetof(_type, _next), \ } +#define VMSTATE_GBYTEARRAY(_field, _state, _version) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .size = sizeof(GByteArray), \ + .info = &vmstate_info_g_byte_array, \ + .flags = VMS_SINGLE, \ + .offset = vmstate_offset_pointer(_state, _field, GByteArray), \ +} + /* _f : field name _f_n : num of elements field_name _n : num of elements diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index ae465c5c2c..8c01215c25 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -924,3 +924,31 @@ const VMStateInfo vmstate_info_qlist = { .load = load_qlist, .save = save_qlist, }; + +static int get_g_byte_array(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + GByteArray *byte_array = *(GByteArray **)pv; + uint32_t len = qemu_get_be32(f); + + g_byte_array_set_size(byte_array, len); + qemu_get_buffer(f, byte_array->data, len); + return 0; +} + +static int put_g_byte_array(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + GByteArray *byte_array = *(GByteArray **)pv; + + qemu_put_be32(f, byte_array->len); + qemu_put_buffer(f, byte_array->data, byte_array->len); + + return 0; +} + +const VMStateInfo vmstate_info_g_byte_array = { + .name = "GByteArray", + .get = get_g_byte_array, + .put = put_g_byte_array, +}; -- 2.54.0
