@samitouri / QOSamiQemu / commits / f9f70b50e3

migration: Introduce VMStateStructMember

When migrating a buffer or array, the vmstate code needs to know the size of the buffer and the number of elements of the array. Today a vmstate writer can choose from a number of macros that take as last input the name of a struct member from where the size/num will be read. At load time, the code will access those values via an opaque pointer to the migrated data and therefore it needs to also know the size of the struct member at that offset. Currently that information is communicated by means of the VMS_VARRAY_* and VMS_VBUFFER_* flags, where each possible type is represented by a flag. So far, that's all fine, but since the vmstate code makes heavy use of macros, handling several types individually (i.e. by name: int, int32_t, etc) requires several versions of a same macro, one for each type. E.g: VMSTATE_VBUFFER_ALLOC_UINT32 ^ This creates a pattern where the vmstate writer has to match the macro name to the data type and has resulted in the code having a tendency of having one macro version for each type, for each type of vmstate. There is also some cognitive load to deal with, e.g. VMSTATE_VARRAY_INT32 doesn't hold an array of int32, it holds an array of something else and the number of elements for the array is stored in a variable of type int32. We're now dealing with the scenario where the code has been expecting int32_t at some places, but a uint64_t macro variant has been added without the code being updated. To address all these situations, introduce a new struct that will hold the offset of the struct members, but also their size, so the various extra macros can all be removed and the person writing the vmstate doesn't need to care about type-checking. Still, keep a minimum check that those fields are at least integers and fit into 64 bits. What changes: 1) type checking changes from individual types to a single check for all integers; 2) there are new ways to access the offsets; num_offset -> num_indirect.offset size_offset -> size_indirect.offset [new] num_indirect.size [new] size_indirect.size 2) reading the offsets goes from checking the VMS_VARRAY_* flags in an if/elseif block to comparing offset.size against the hardcoded sizes in bytes; 3) the VMS_VARRAY_* and VMS_VBUFFER_* flags become obsolete. Removed in the next patch; 4) memory usage increases +1 byte per vmstate; Reported-by: Seungjung Kim <seungjung0711@gmail.com> Fixes: CVE-2026-6426 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3675 Acked-by: Peter Xu <peterx@redhat.com> [fixed up vmstate_read_from_offset to work on BE host] Signed-off-by: Fabiano Rosas <farosas@suse.de>

Fabiano Rosas committed Aug 18, 2026 at 15:24 UTC f9f70b50e3eb7782a7662653e93fb4c3dec56423
6 files changed +210 -80
include/migration/vmstate.h
+118 -45
@@ -31,6 +31,7 @@
31
32 typedef struct VMStateInfo VMStateInfo;
33 typedef struct VMStateField VMStateField;
34 +typedef struct VMStateStructMember VMStateStructMember;
35
36 /*
37 * VMStateInfo allows customized migration of objects that don't fit in
@@ -67,15 +68,15 @@ enum VMStateFlags {
68 * to the actual field (e.g. struct a { uint8_t *b;
69 * }). Dereference the pointer before using it as basis for
70 * further pointer arithmetic (see e.g. VMS_ARRAY). Does not
70 - * affect the meaning of VMStateField.num_offset or
71 - * VMStateField.size_offset; see VMS_VARRAY* and VMS_VBUFFER for
71 + * affect the meaning of VMStateField.num_indirect or
72 + * VMStateField.size_indirect; see VMS_VARRAY* and VMS_VBUFFER for
73 * those. */
74 VMS_POINTER = 0x002,
75
76 /* The field is an array of fixed size. VMStateField.num contains
77 * the number of entries in the array. The size of each entry is
78 * given by VMStateField.size and / or opaque +
78 - * VMStateField.size_offset; see VMS_VBUFFER and
79 + * VMStateField.size_indirect; see VMS_VBUFFER and
80 * VMS_MULTIPLY. Each array entry will be processed individually
81 * (VMStateField.info.get()/put() if VMS_STRUCT is not set,
82 * recursion into VMStateField.vmsd if VMS_STRUCT is set). May not
@@ -88,8 +89,9 @@ enum VMStateFlags {
89 * array entry. */
90 VMS_STRUCT = 0x008,
91
91 - /* The field is an array of variable size. The int32_t at opaque +
92 - * VMStateField.num_offset contains the number of entries in the
92 + /*
93 + * The field is an array of variable size. The integer at opaque +
94 + * VMStateField.num_indirect contains the number of entries in the
95 * array. See the VMS_ARRAY description regarding array handling
96 * in general. May not be combined with VMS_ARRAY or any other
97 * VMS_VARRAY*. */
@@ -105,7 +107,7 @@ enum VMStateFlags {
107 VMS_ARRAY_OF_POINTER = 0x040,
108
109 /* The field is an array of variable size. The uint16_t at opaque
108 - * + VMStateField.num_offset
110 + * + VMStateField.num_indirect
111 * contains the number of entries in the array. See the VMS_ARRAY
112 * description regarding array handling in general. May not be
113 * combined with VMS_ARRAY or any other VMS_VARRAY*. */
@@ -114,26 +116,27 @@ enum VMStateFlags {
116 /* The size of the individual entries (a single array entry if
117 * VMS_ARRAY or any of VMS_VARRAY* are set, or the field itself if
118 * neither is set) is variable (i.e. not known at compile-time),
117 - * but the same for all entries. Use the int32_t at opaque +
118 - * VMStateField.size_offset (subject to VMS_MULTIPLY) to determine
119 + * but the same for all entries. Use the integer at opaque +
120 + * VMStateField.size_indirect (subject to VMS_MULTIPLY) to determine
121 * the size of each (and every) entry. */
122 VMS_VBUFFER = 0x100,
123
122 - /* Multiply the entry size given by the int32_t at opaque +
123 - * VMStateField.size_offset (see VMS_VBUFFER description) with
124 + /*
125 + * Multiply the entry size given by the integer at opaque +
126 + * VMStateField.size_indirect (see VMS_VBUFFER description) with
127 * VMStateField.size to determine the number of bytes to be
128 * allocated. Only valid in combination with VMS_VBUFFER. */
129 VMS_MULTIPLY = 0x200,
130
131 /* The field is an array of variable size. The uint8_t at opaque +
129 - * VMStateField.num_offset
132 + * VMStateField.num_indirect
133 * contains the number of entries in the array. See the VMS_ARRAY
134 * description regarding array handling in general. May not be
135 * combined with VMS_ARRAY or any other VMS_VARRAY*. */
136 VMS_VARRAY_UINT8 = 0x400,
137
138 /* The field is an array of variable size. The uint32_t at opaque
136 - * + VMStateField.num_offset
139 + * + VMStateField.num_indirect
140 * contains the number of entries in the array. See the VMS_ARRAY
141 * description regarding array handling in general. May not be
142 * combined with VMS_ARRAY or any other VMS_VARRAY*. */
@@ -187,29 +190,88 @@ typedef enum {
190 MIG_PRI_MAX,
191 } MigrationPriority;
192
193 +
194 +/*
195 + * VMStateStructMember: Metadata about a single member of the struct
196 + * being migrated by the vmstate. This is kept separate from
197 + * VMStateField because a single VMStateField can reference other
198 + * members of the struct aside from the main struct member that's
199 + * being migrated.
200 + *
201 + * One situation where extra fields are referenced is the common case
202 + * of a struct containing an array or buffer, the size of which is
203 + * stored in another member of the same struct.
204 + *
205 + * Example 1:
206 + * struct IDEState {
207 + * ...
208 + * uint8_t *io_buffer;
209 + * int32_t io_buffer_total_len;
210 + * ...
211 + * }
212 + * VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
213 + * vmstate_info_uint8, uint8_t)
214 + *
215 + * In the above, io_buffer is the main field being migrated by the
216 + * VMSTATE_VARRAY while io_buffer_total_len is the meta field that
217 + * provides the size of the io_buffer. In this particular case,
218 + * io_buffer_total_len is never migrated.
219 + *
220 + * Example 2:
221 + * struct SpaprMachineState {
222 + * ...
223 + * uint32_t fdt_size;
224 + * void *fdt_blob;
225 + * ...
226 + * }
227 + * VMSTATE_UINT32(fdt_size, SpaprMachineState),
228 + * VMSTATE_VBUFFER_ALLOC_UINT32(fdt_blob, SpaprMachineState, 0, NULL, fdt_size),
229 + *
230 + * Here, fdt_blob is the field being migrated by VMSTATE_VBUFFER_ALLOC
231 + * and fdt_size is the meta field providing the size. In this case,
232 + * the extra field is also independently migrated by the
233 + * VMSTATE_UINT32 above.
234 + */
235 +struct VMStateStructMember {
236 + /* offsetof the field inside the migrated struct */
237 + uint32_t offset;
238 + /* size of field itself */
239 + uint8_t size;
240 +};
241 +
242 struct VMStateField {
243 const char *name;
244 size_t offset;
245
246 /*
195 - * @size or @size_offset specifies the size of the element embeded in
196 - * the field. Only one of them should be present never both. When
197 - * @size_offset is used together with VMS_VBUFFER, it means the size is
198 - * dynamic calculated instead of a constant.
247 + * @size directly specifies the size of the element being
248 + * migrated.
249 + *
250 + * @size_indirect specifies the offset inside a struct where the
251 + * size of the element is stored.
252 + *
253 + * Only one of the above should be present (except for
254 + * VMSTATE_MULTIPLY which uses .size as a multiplier). When
255 + * @size_indirect is used together with VMS_VBUFFER, it means
256 + * the size is dynamic calculated instead of a constant.
257 + *
258 + * When the field is an array of any type, these refer to the size
259 + * of one element of the array.
260 *
200 - * When the field is an array of any type, this stores the size of one
201 - * element of the array.
261 + * NOTE: even if VMS_POINTER or VMS_ARRAY_OF_POINTER may be
262 + * specified, these parameters always reflect the real size of the
263 + * objects that a pointer point to.
264 *
203 - * NOTE: even if VMS_POINTER or VMS_ARRAY_OF_POINTER may be specified,
204 - * this parameter always reflects the real size of the objects that a
205 - * pointer point to.
265 + * @num_indirect specifies the offset inside a struct where the
266 + * number of elements of an array is stored.
267 */
268 size_t size;
208 - size_t size_offset;
269 + VMStateStructMember size_indirect;
270 + VMStateStructMember num_indirect;
271
272 size_t start;
273 int num;
212 - size_t num_offset;
274 +
275 const VMStateInfo *info;
276 enum VMStateFlags flags;
277 const VMStateDescription *vmsd;
@@ -328,6 +390,17 @@ extern const VMStateInfo vmstate_info_g_byte_array;
390 (type_check(t1, typeof_elt_of_field(t2, f)) \
391 + QEMU_BUILD_BUG_ON_ZERO(!QEMU_IS_ARRAY(((t2 *)0)->f)))
392
393 +#define type_check_int64(t) \
394 + (~((t)0) * sizeof(struct { \
395 + QEMU_BUILD_BUG_ON(sizeof(t) > sizeof(uint64_t)); \
396 + }))
397 +
398 +#define vmstate_field_offset(_state, _field) { \
399 + .offset = (offsetof(_state, _field) + \
400 + type_check_int64(typeof_field(_state, _field))), \
401 + .size = sizeof(typeof_field(_state, _field)), \
402 +}
403 +
404 #define vmstate_offset_value(_state, _field, _type) \
405 (offsetof(_state, _field) + \
406 type_check(_type, typeof_field(_state, _field)))
@@ -454,7 +527,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
527 #define VMSTATE_VARRAY_INT32(_field, _state, _field_num, _version, _info, _type) {\
528 .name = (stringify(_field)), \
529 .version_id = (_version), \
457 - .num_offset = vmstate_offset_value(_state, _field_num, int32_t), \
530 + .num_indirect = vmstate_field_offset(_state, _field_num), \
531 .info = &(_info), \
532 .size = sizeof(_type), \
533 .flags = VMS_VARRAY_INT32|VMS_POINTER, \
@@ -464,7 +537,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
537 #define VMSTATE_VARRAY_UINT32(_field, _state, _field_num, _version, _info, _type) {\
538 .name = (stringify(_field)), \
539 .version_id = (_version), \
467 - .num_offset = vmstate_offset_value(_state, _field_num, uint32_t),\
540 + .num_indirect = vmstate_field_offset(_state, _field_num), \
541 .info = &(_info), \
542 .size = sizeof(_type), \
543 .flags = VMS_VARRAY_UINT32|VMS_POINTER, \
@@ -474,7 +547,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
547 #define VMSTATE_VARRAY_INT32_ALLOC(_field, _state, _field_num, _version, _info, _type) {\
548 .name = (stringify(_field)), \
549 .version_id = (_version), \
477 - .num_offset = vmstate_offset_value(_state, _field_num, int32_t), \
550 + .num_indirect = vmstate_field_offset(_state, _field_num), \
551 .info = &(_info), \
552 .size = sizeof(_type), \
553 .flags = VMS_VARRAY_INT32 | VMS_POINTER | VMS_ALLOC, \
@@ -484,7 +557,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
557 #define VMSTATE_VARRAY_UINT32_ALLOC(_field, _state, _field_num, _version, _info, _type) {\
558 .name = (stringify(_field)), \
559 .version_id = (_version), \
487 - .num_offset = vmstate_offset_value(_state, _field_num, uint32_t),\
560 + .num_indirect = vmstate_field_offset(_state, _field_num), \
561 .info = &(_info), \
562 .size = sizeof(_type), \
563 .flags = VMS_VARRAY_UINT32|VMS_POINTER|VMS_ALLOC, \
@@ -494,7 +567,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
567 #define VMSTATE_VARRAY_UINT16_ALLOC(_field, _state, _field_num, _version, _info, _type) {\
568 .name = (stringify(_field)), \
569 .version_id = (_version), \
497 - .num_offset = vmstate_offset_value(_state, _field_num, uint16_t),\
570 + .num_indirect = vmstate_field_offset(_state, _field_num), \
571 .info = &(_info), \
572 .size = sizeof(_type), \
573 .flags = VMS_VARRAY_UINT16 | VMS_POINTER | VMS_ALLOC, \
@@ -504,7 +577,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
577 #define VMSTATE_VARRAY_UINT16_UNSAFE(_field, _state, _field_num, _version, _info, _type) {\
578 .name = (stringify(_field)), \
579 .version_id = (_version), \
507 - .num_offset = vmstate_offset_value(_state, _field_num, uint16_t),\
580 + .num_indirect = vmstate_field_offset(_state, _field_num), \
581 .info = &(_info), \
582 .size = sizeof(_type), \
583 .flags = VMS_VARRAY_UINT16, \
@@ -583,7 +656,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
656 _field, _state, _field_num, _version, _vmsd, _type) { \
657 .name = (stringify(_field)), \
658 .version_id = (_version), \
586 - .num_offset = vmstate_offset_value(_state, _field_num, uint8_t), \
659 + .num_indirect = vmstate_field_offset(_state, _field_num), \
660 .vmsd = &(_vmsd), \
661 .size = sizeof(_type), \
662 .flags = VMS_POINTER | VMS_VARRAY_UINT8 | \
@@ -596,7 +669,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
669 _field, _state, _field_num, _version, _vmsd, _type) { \
670 .name = (stringify(_field)), \
671 .version_id = (_version), \
599 - .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \
672 + .num_indirect = vmstate_field_offset(_state, _field_num), \
673 .vmsd = &(_vmsd), \
674 .size = sizeof(_type), \
675 .flags = VMS_POINTER | VMS_VARRAY_UINT32 | \
@@ -608,7 +681,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
681 #define VMSTATE_VARRAY_OF_POINTER_UINT32(_field, _state, _field_num, _version, _info, _type) { \
682 .name = (stringify(_field)), \
683 .version_id = (_version), \
611 - .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \
684 + .num_indirect = vmstate_field_offset(_state, _field_num), \
685 .info = &(_info), \
686 .flags = VMS_VARRAY_UINT32 | VMS_ARRAY_OF_POINTER | VMS_POINTER, \
687 .offset = vmstate_offset_pointer(_state, _field, _type *), \
@@ -650,7 +723,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
723
724 #define VMSTATE_STRUCT_VARRAY_UINT8(_field, _state, _field_num, _version, _vmsd, _type) { \
725 .name = (stringify(_field)), \
653 - .num_offset = vmstate_offset_value(_state, _field_num, uint8_t), \
726 + .num_indirect = vmstate_field_offset(_state, _field_num), \
727 .version_id = (_version), \
728 .vmsd = &(_vmsd), \
729 .size = sizeof(_type), \
@@ -674,7 +747,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
747 #define VMSTATE_STRUCT_VARRAY_POINTER_INT32(_field, _state, _field_num, _vmsd, _type) { \
748 .name = (stringify(_field)), \
749 .version_id = 0, \
677 - .num_offset = vmstate_offset_value(_state, _field_num, int32_t), \
750 + .num_indirect = vmstate_field_offset(_state, _field_num), \
751 .size = sizeof(_type), \
752 .vmsd = &(_vmsd), \
753 .flags = VMS_POINTER | VMS_VARRAY_INT32 | VMS_STRUCT, \
@@ -684,7 +757,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
757 #define VMSTATE_STRUCT_VARRAY_POINTER_UINT32(_field, _state, _field_num, _vmsd, _type) { \
758 .name = (stringify(_field)), \
759 .version_id = 0, \
687 - .num_offset = vmstate_offset_value(_state, _field_num, uint32_t),\
760 + .num_indirect = vmstate_field_offset(_state, _field_num), \
761 .size = sizeof(_type), \
762 .vmsd = &(_vmsd), \
763 .flags = VMS_POINTER | VMS_VARRAY_INT32 | VMS_STRUCT, \
@@ -694,7 +767,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
767 #define VMSTATE_STRUCT_VARRAY_POINTER_UINT16(_field, _state, _field_num, _vmsd, _type) { \
768 .name = (stringify(_field)), \
769 .version_id = 0, \
697 - .num_offset = vmstate_offset_value(_state, _field_num, uint16_t),\
770 + .num_indirect = vmstate_field_offset(_state, _field_num), \
771 .size = sizeof(_type), \
772 .vmsd = &(_vmsd), \
773 .flags = VMS_POINTER | VMS_VARRAY_UINT16 | VMS_STRUCT, \
@@ -703,7 +776,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
776
777 #define VMSTATE_STRUCT_VARRAY_UINT32(_field, _state, _field_num, _version, _vmsd, _type) { \
778 .name = (stringify(_field)), \
706 - .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \
779 + .num_indirect = vmstate_field_offset(_state, _field_num), \
780 .version_id = (_version), \
781 .vmsd = &(_vmsd), \
782 .size = sizeof(_type), \
@@ -715,7 +788,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
788 .name = (stringify(_field)), \
789 .version_id = (_version), \
790 .vmsd = &(_vmsd), \
718 - .num_offset = vmstate_offset_value(_state, _field_num, int32_t), \
791 + .num_indirect = vmstate_field_offset(_state, _field_num), \
792 .size = sizeof(_type), \
793 .flags = VMS_STRUCT|VMS_VARRAY_INT32|VMS_ALLOC|VMS_POINTER, \
794 .offset = vmstate_offset_pointer(_state, _field, _type), \
@@ -736,7 +809,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
809 .name = (stringify(_field)), \
810 .version_id = (_version), \
811 .field_exists = (_test), \
739 - .size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\
812 + .size_indirect = vmstate_field_offset(_state, _field_size), \
813 .size = (_multiply), \
814 .info = &vmstate_info_buffer, \
815 .flags = VMS_VBUFFER|VMS_POINTER|VMS_MULTIPLY, \
@@ -747,7 +820,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
820 .name = (stringify(_field)), \
821 .version_id = (_version), \
822 .field_exists = (_test), \
750 - .size_offset = vmstate_offset_value(_state, _field_size, int32_t),\
823 + .size_indirect = vmstate_field_offset(_state, _field_size), \
824 .info = &vmstate_info_buffer, \
825 .flags = VMS_VBUFFER|VMS_POINTER, \
826 .offset = offsetof(_state, _field), \
@@ -757,7 +830,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
830 .name = (stringify(_field)), \
831 .version_id = (_version), \
832 .field_exists = (_test), \
760 - .size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\
833 + .size_indirect = vmstate_field_offset(_state, _field_size), \
834 .info = &vmstate_info_buffer, \
835 .flags = VMS_VBUFFER|VMS_POINTER, \
836 .offset = offsetof(_state, _field), \
@@ -767,7 +840,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
840 .name = (stringify(_field)), \
841 .version_id = (_version), \
842 .field_exists = (_test), \
770 - .size_offset = vmstate_offset_value(_state, _field_size, uint64_t),\
843 + .size_indirect = vmstate_field_offset(_state, _field_size), \
844 .info = &vmstate_info_buffer, \
845 .flags = VMS_VBUFFER | VMS_POINTER, \
846 .offset = offsetof(_state, _field), \
@@ -778,7 +851,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
851 .name = (stringify(_field)), \
852 .version_id = (_version), \
853 .field_exists = (_test), \
781 - .size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\
854 + .size_indirect = vmstate_field_offset(_state, _field_size), \
855 .info = &vmstate_info_buffer, \
856 .flags = VMS_VBUFFER|VMS_POINTER|VMS_ALLOC, \
857 .offset = offsetof(_state, _field), \
@@ -838,7 +911,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
911 #define VMSTATE_UNUSED_VARRAY_UINT32(_state, _test, _version, _field_num, _size) {\
912 .name = "unused", \
913 .field_exists = (_test), \
841 - .num_offset = vmstate_offset_value(_state, _field_num, uint32_t),\
914 + .num_indirect = vmstate_field_offset(_state, _field_num), \
915 .version_id = (_version), \
916 .size = (_size), \
917 .info = &vmstate_info_unused_buffer, \
@@ -852,7 +925,7 @@ extern const VMStateInfo vmstate_info_g_byte_array;
925 .name = (stringify(_field)), \
926 .field_exists = (_test), \
927 .version_id = (_version), \
855 - .size_offset = vmstate_offset_value(_state, _field_size, int32_t),\
928 + .size_indirect = vmstate_field_offset(_state, _field_size), \
929 .info = &vmstate_info_bitmap, \
930 .flags = VMS_VBUFFER|VMS_POINTER, \
931 .offset = offsetof(_state, _field), \
migration/savevm.c
+2 -2
@@ -873,14 +873,14 @@ static void vmstate_check(const VMStateDescription *vmsd)
873 * Size must be provided because dest QEMU needs that
874 * info to know what to allocate
875 */
876 - assert(field->size || field->size_offset);
876 + assert(field->size != 0 || field->size_indirect.size != 0);
877 } else {
878 /*
879 * Otherwise size info isn't useful (because it's
880 * always the size of host pointer), detect accidental
881 * setup of sizes in this case.
882 */
883 - assert(field->size == 0 && field->size_offset == 0);
883 + assert(field->size == 0 && field->size_indirect.size == 0);
884 }
885 /*
886 * VMS_ARRAY_OF_POINTER must be used only together with one
migration/vmstate.c
+45 -18
@@ -78,32 +78,57 @@ vmsd_init_ptr_marker_field(VMStateField *fake, const VMStateField *field)
78 };
79 }
80
81 -static int vmstate_n_elems(void *opaque, const VMStateField *field)
81 +static uint64_t vmstate_read_from_offset(const VMStateStructMember *member,
82 + void *opaque)
83 {
83 - int n_elems = 1;
84 + uint8_t *ptr = (uint8_t *)opaque + member->offset;
85 +
86 + switch (member->size) {
87 + case 1: {
88 + uint8_t v;
89 + memcpy(&v, ptr, 1);
90 + return v;
91 + }
92 + case 2: {
93 + uint16_t v;
94 + memcpy(&v, ptr, 2);
95 + return v;
96 + }
97 + case 4: {
98 + uint32_t v;
99 + memcpy(&v, ptr, 4);
100 + return v;
101 + }
102 + case 8: {
103 + uint64_t v;
104 + memcpy(&v, ptr, 8);
105 + return v;
106 + }
107 + }
108 + g_assert_not_reached();
109 +}
110 +
111 +static uint64_t vmstate_n_elems(void *opaque, const VMStateField *field)
112 +{
113 + uint64_t n_elems = 1;
114
115 if (field->flags & VMS_ARRAY) {
116 n_elems = field->num;
87 - } else if (field->flags & VMS_VARRAY_INT32) {
88 - n_elems = *(int32_t *)(opaque + field->num_offset);
89 - } else if (field->flags & VMS_VARRAY_UINT32) {
90 - n_elems = *(uint32_t *)(opaque + field->num_offset);
91 - } else if (field->flags & VMS_VARRAY_UINT16) {
92 - n_elems = *(uint16_t *)(opaque + field->num_offset);
93 - } else if (field->flags & VMS_VARRAY_UINT8) {
94 - n_elems = *(uint8_t *)(opaque + field->num_offset);
117 + } else if (field->flags & (VMS_VARRAY_INT32 | VMS_VARRAY_UINT32
118 + | VMS_VARRAY_UINT16 | VMS_VARRAY_UINT8)) {
119 + n_elems = vmstate_read_from_offset(&field->num_indirect, opaque);
120 }
121
122 trace_vmstate_n_elems(field->name, n_elems);
123 return n_elems;
124 }
125
101 -static int vmstate_size(void *opaque, const VMStateField *field)
126 +static uint64_t vmstate_size(void *opaque, const VMStateField *field)
127 {
103 - int size;
128 + uint64_t size;
129
130 if (field->flags & VMS_VBUFFER) {
106 - size = *(int32_t *)(opaque + field->size_offset);
131 + size = vmstate_read_from_offset(&field->size_indirect, opaque);
132 if (field->flags & VMS_MULTIPLY) {
133 size *= field->size;
134 }
@@ -124,7 +149,7 @@ static void vmstate_handle_alloc(void *ptr, const VMStateField *field,
149 void *opaque)
150 {
151 if (field->flags & VMS_POINTER && field->flags & VMS_ALLOC) {
127 - gsize size = vmstate_size(opaque, field);
152 + uint64_t size = vmstate_size(opaque, field);
153 size *= vmstate_n_elems(opaque, field);
154 if (size) {
155 *(void **)ptr = g_malloc(size);
@@ -335,8 +360,9 @@ bool vmstate_load_vmsd(QEMUFile *f, const VMStateDescription *vmsd,
360
361 if (exists) {
362 void *first_elem = opaque + field->offset;
338 - int i, n_elems = vmstate_n_elems(opaque, field);
339 - int size = vmstate_size(opaque, field);
363 + int i;
364 + uint64_t n_elems = vmstate_n_elems(opaque, field);
365 + uint64_t size = vmstate_size(opaque, field);
366
367 vmstate_handle_alloc(first_elem, field, opaque);
368 if (field->flags & VMS_POINTER) {
@@ -650,8 +676,9 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,
676 while (field->name) {
677 if (vmstate_field_exists(vmsd, field, opaque, version_id)) {
678 void *first_elem = opaque + field->offset;
653 - int i, n_elems = vmstate_n_elems(opaque, field);
654 - int size = vmstate_size(opaque, field);
679 + int i;
680 + uint64_t n_elems = vmstate_n_elems(opaque, field);
681 + uint64_t size = vmstate_size(opaque, field);
682 JSONWriter *vmdesc_loop = vmdesc;
683 bool is_prev_null = false;
684 /*
rust/bindings/migration-sys/lib.rs
+10
@@ -47,6 +47,7 @@ impl Default for VMStateFlags {
47 unsafe impl Zeroable for VMStateFlags {}
48 unsafe impl Zeroable for VMStateField {}
49 unsafe impl Zeroable for VMStateDescription {}
50 +unsafe impl Zeroable for VMStateStructMember {}
51
52 // The following higher-level helpers could be in "migration"
53 // crate when Rust has const trait impl.
@@ -115,3 +116,12 @@ impl VMStateField {
116 self.with_varray_flag_unchecked(flag)
117 }
118 }
119 +
120 +impl VMStateStructMember {
121 + pub const fn new(off: usize, size: usize) -> Self {
122 + Self {
123 + offset: off as u32,
124 + size: size as u8,
125 + }
126 + }
127 +}
rust/migration/src/vmstate.rs
+22 -2
@@ -41,7 +41,7 @@ use common::{
41 };
42
43 use crate::bindings::{self, VMStateFlags};
44 -pub use crate::bindings::{MigrationPriority, VMStateField};
44 +pub use crate::bindings::{MigrationPriority, VMStateField, VMStateStructMember};
45
46 /// This macro is used to call a function with a generic argument bound
47 /// to the type of a field. The function must take a
@@ -119,6 +119,23 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags
119 T::VARRAY_FLAG
120 }
121
122 +pub const OPAQUE: &[u8; 1048576] = &[0; 1048576];
123 +
124 +pub const fn size_of_ptr_type<T>(_: *const T) -> usize {
125 + ::core::mem::size_of::<T>()
126 +}
127 +
128 +#[macro_export]
129 +macro_rules! size_of_field_type {
130 + ($struct_name:ty, $($field_name:ident).+) => {
131 + $crate::vmstate::size_of_ptr_type(unsafe {
132 + ::core::ptr::addr_of!(
133 + (*$crate::vmstate::OPAQUE.as_ptr().cast::<$struct_name>()).$($field_name).+
134 + )
135 + })
136 + };
137 +}
138 +
139 /// Return the `VMStateField` for a field of a struct. The field must be
140 /// visible in the current scope.
141 ///
@@ -146,7 +163,10 @@ macro_rules! vmstate_of {
163 .as_bytes()
164 .as_ptr().cast::<::std::os::raw::c_char>(),
165 offset: ::std::mem::offset_of!($struct_name, $($field_name).+),
149 - $(num_offset: ::std::mem::offset_of!($struct_name, $($num).+),)?
166 + $(num_indirect: $crate::vmstate::VMStateStructMember {
167 + offset: ::std::mem::offset_of!($struct_name, $($num).+) as u32,
168 + size: $crate::size_of_field_type!($struct_name, $($num).+) as u8,
169 + },)?
170 $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
171 // The calls to `call_func_with_field!` are the magic that
172 // computes most of the VMStateField from the type of the field.
rust/tests/tests/vmstate_tests.rs
+13 -13
@@ -65,7 +65,7 @@ fn test_vmstate_uint16() {
65 b"elem\0"
66 );
67 assert_eq!(foo_fields[0].offset, 16);
68 - assert_eq!(foo_fields[0].num_offset, 0);
68 + assert_eq!(foo_fields[0].num_indirect.size, 0);
69 assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int8 });
70 assert_eq!(foo_fields[0].version_id, 0);
71 assert_eq!(foo_fields[0].size, 1);
@@ -86,7 +86,7 @@ fn test_vmstate_unused() {
86 b"unused\0"
87 );
88 assert_eq!(foo_fields[1].offset, 0);
89 - assert_eq!(foo_fields[1].num_offset, 0);
89 + assert_eq!(foo_fields[1].num_indirect.size, 0);
90 assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_unused_buffer });
91 assert_eq!(foo_fields[1].version_id, 0);
92 assert_eq!(foo_fields[1].size, 8);
@@ -108,7 +108,7 @@ fn test_vmstate_varray_uint16_unsafe() {
108 b"arr\0"
109 );
110 assert_eq!(foo_fields[2].offset, 0);
111 - assert_eq!(foo_fields[2].num_offset, 4);
111 + assert_eq!(foo_fields[2].num_indirect.offset, 4);
112 assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 });
113 assert_eq!(foo_fields[2].version_id, 0);
114 assert_eq!(foo_fields[2].size, 1);
@@ -172,7 +172,7 @@ fn test_vmstate_bool_v() {
172 b"val\0"
173 );
174 assert_eq!(foo_fields[0].offset, 136);
175 - assert_eq!(foo_fields[0].num_offset, 0);
175 + assert_eq!(foo_fields[0].num_indirect.size, 0);
176 assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_bool });
177 assert_eq!(foo_fields[0].version_id, 2);
178 assert_eq!(foo_fields[0].size, 1);
@@ -193,7 +193,7 @@ fn test_vmstate_uint64() {
193 b"wrap\0"
194 );
195 assert_eq!(foo_fields[1].offset, 128);
196 - assert_eq!(foo_fields[1].num_offset, 0);
196 + assert_eq!(foo_fields[1].num_indirect.size, 0);
197 assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_uint64 });
198 assert_eq!(foo_fields[1].version_id, 0);
199 assert_eq!(foo_fields[1].size, 8);
@@ -215,7 +215,7 @@ fn test_vmstate_struct_varray_uint8() {
215 b"arr_a\0"
216 );
217 assert_eq!(foo_fields[2].offset, 0);
218 - assert_eq!(foo_fields[2].num_offset, 60);
218 + assert_eq!(foo_fields[2].num_indirect.offset, 60);
219 assert!(foo_fields[2].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field.
220 assert_eq!(foo_fields[2].version_id, 1);
221 assert_eq!(foo_fields[2].size, 20);
@@ -240,7 +240,7 @@ fn test_vmstate_macro_array() {
240 b"arr_i64\0"
241 );
242 assert_eq!(foo_fields[4].offset, 144);
243 - assert_eq!(foo_fields[4].num_offset, 0);
243 + assert_eq!(foo_fields[4].num_indirect.size, 0);
244 assert_eq!(foo_fields[4].info, unsafe { &vmstate_info_int64 });
245 assert_eq!(foo_fields[4].version_id, 0);
246 assert_eq!(foo_fields[4].size, 8);
@@ -264,7 +264,7 @@ fn test_vmstate_struct_varray_uint8_wrapper() {
264 unsafe { CStr::from_ptr(foo_fields[5].name) }.to_bytes_with_nul(),
265 b"arr_a_wrap\0"
266 );
267 - assert_eq!(foo_fields[5].num_offset, 228);
267 + assert_eq!(foo_fields[5].num_indirect.offset, 228);
268 assert!(unsafe { foo_fields[5].field_exists.unwrap()(foo_b_p, 0) });
269
270 // The last VMStateField in VMSTATE_FOOB.
@@ -316,7 +316,7 @@ fn test_vmstate_pointer() {
316 b"ptr\0"
317 );
318 assert_eq!(foo_fields[0].offset, 0);
319 - assert_eq!(foo_fields[0].num_offset, 0);
319 + assert_eq!(foo_fields[0].num_indirect.size, 0);
320 assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int32 });
321 assert_eq!(foo_fields[0].version_id, 2);
322 assert_eq!(foo_fields[0].size, 4);
@@ -341,7 +341,7 @@ fn test_vmstate_struct_pointer() {
341 b"ptr_a\0"
342 );
343 assert_eq!(foo_fields[1].offset, PTR_SIZE);
344 - assert_eq!(foo_fields[1].num_offset, 0);
344 + assert_eq!(foo_fields[1].num_indirect.size, 0);
345 assert_eq!(foo_fields[1].vmsd, VMSTATE_FOOA.as_ref());
346 assert_eq!(foo_fields[1].version_id, 0);
347 assert_eq!(foo_fields[1].size, size_of::<FooA>());
@@ -366,7 +366,7 @@ fn test_vmstate_macro_array_of_pointer() {
366 b"arr_ptr\0"
367 );
368 assert_eq!(foo_fields[2].offset, 2 * PTR_SIZE);
369 - assert_eq!(foo_fields[2].num_offset, 0);
369 + assert_eq!(foo_fields[2].num_indirect.size, 0);
370 assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 });
371 assert_eq!(foo_fields[2].version_id, 0);
372 assert_eq!(foo_fields[2].size, PTR_SIZE);
@@ -391,7 +391,7 @@ fn test_vmstate_macro_array_of_pointer_wrapped() {
391 b"arr_ptr_wrap\0"
392 );
393 assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE);
394 - assert_eq!(foo_fields[3].num_offset, 0);
394 + assert_eq!(foo_fields[3].num_indirect.size, 0);
395 assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_uint8 });
396 assert_eq!(foo_fields[3].version_id, 0);
397 assert_eq!(foo_fields[3].size, PTR_SIZE);
@@ -454,7 +454,7 @@ fn test_vmstate_validate() {
454 b"foo_d_0\0"
455 );
456 assert_eq!(foo_fields[0].offset, 0);
457 - assert_eq!(foo_fields[0].num_offset, 0);
457 + assert_eq!(foo_fields[0].num_indirect.size, 0);
458 assert!(foo_fields[0].info.is_null());
459 assert_eq!(foo_fields[0].version_id, 0);
460 assert_eq!(foo_fields[0].size, 0);