From 35fc1f71899fd42323bd8f33da18f0211e0d2727 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 Apr 2014 19:50:26 +0300 Subject: vmstate: reduce code duplication move size offset and number of elements math out to functions, to reduce code duplication. Signed-off-by: Michael S. Tsirkin Cc: "Dr. David Alan Gilbert" Signed-off-by: Juan Quintela --- vmstate.c | 100 ++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 52 insertions(+), 48 deletions(-) (limited to 'vmstate.c') diff --git a/vmstate.c b/vmstate.c index b689f2f9b3..dd6f834331 100644 --- a/vmstate.c +++ b/vmstate.c @@ -10,6 +10,50 @@ static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, void *opaque); +static int vmstate_n_elems(void *opaque, VMStateField *field) +{ + int n_elems = 1; + + if (field->flags & VMS_ARRAY) { + n_elems = field->num; + } else if (field->flags & VMS_VARRAY_INT32) { + n_elems = *(int32_t *)(opaque+field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT32) { + n_elems = *(uint32_t *)(opaque+field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT16) { + n_elems = *(uint16_t *)(opaque+field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT8) { + n_elems = *(uint8_t *)(opaque+field->num_offset); + } + + return n_elems; +} + +static int vmstate_size(void *opaque, VMStateField *field) +{ + int size = field->size; + + if (field->flags & VMS_VBUFFER) { + size = *(int32_t *)(opaque+field->size_offset); + if (field->flags & VMS_MULTIPLY) { + size *= field->size; + } + } + + return size; +} + +static void *vmstate_base_addr(void *opaque, VMStateField *field) +{ + void *base_addr = opaque + field->offset; + + if (field->flags & VMS_POINTER) { + base_addr = *(void **)base_addr + field->start; + } + + return base_addr; +} + int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id) { @@ -36,30 +80,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, field->field_exists(opaque, version_id)) || (!field->field_exists && field->version_id <= version_id)) { - void *base_addr = opaque + field->offset; - int i, n_elems = 1; - int size = field->size; - - if (field->flags & VMS_VBUFFER) { - size = *(int32_t *)(opaque+field->size_offset); - if (field->flags & VMS_MULTIPLY) { - size *= field->size; - } - } - if (field->flags & VMS_ARRAY) { - n_elems = field->num; - } else if (field->flags & VMS_VARRAY_INT32) { - n_elems = *(int32_t *)(opaque+field->num_offset); - } else if (field->flags & VMS_VARRAY_UINT32) { - n_elems = *(uint32_t *)(opaque+field->num_offset); - } else if (field->flags & VMS_VARRAY_UINT16) { - n_elems = *(uint16_t *)(opaque+field->num_offset); - } else if (field->flags & VMS_VARRAY_UINT8) { - n_elems = *(uint8_t *)(opaque+field->num_offset); - } - if (field->flags & VMS_POINTER) { - base_addr = *(void **)base_addr + field->start; - } + void *base_addr = vmstate_base_addr(opaque, field); + int i, n_elems = vmstate_n_elems(opaque, field); + int size = vmstate_size(opaque, field); + for (i = 0; i < n_elems; i++) { void *addr = base_addr + size * i; @@ -102,30 +126,10 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, while (field->name) { if (!field->field_exists || field->field_exists(opaque, vmsd->version_id)) { - void *base_addr = opaque + field->offset; - int i, n_elems = 1; - int size = field->size; - - if (field->flags & VMS_VBUFFER) { - size = *(int32_t *)(opaque+field->size_offset); - if (field->flags & VMS_MULTIPLY) { - size *= field->size; - } - } - if (field->flags & VMS_ARRAY) { - n_elems = field->num; - } else if (field->flags & VMS_VARRAY_INT32) { - n_elems = *(int32_t *)(opaque+field->num_offset); - } else if (field->flags & VMS_VARRAY_UINT32) { - n_elems = *(uint32_t *)(opaque+field->num_offset); - } else if (field->flags & VMS_VARRAY_UINT16) { - n_elems = *(uint16_t *)(opaque+field->num_offset); - } else if (field->flags & VMS_VARRAY_UINT8) { - n_elems = *(uint8_t *)(opaque+field->num_offset); - } - if (field->flags & VMS_POINTER) { - base_addr = *(void **)base_addr + field->start; - } + void *base_addr = vmstate_base_addr(opaque, field); + int i, n_elems = vmstate_n_elems(opaque, field); + int size = vmstate_size(opaque, field); + for (i = 0; i < n_elems; i++) { void *addr = base_addr + size * i; -- cgit 1.4.1 From 5bf81c8d63db0216a4d29dc87f9ce530bb791dd1 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 Apr 2014 19:50:31 +0300 Subject: vmstate: add VMS_MUST_EXIST Can be used to verify a required field exists or validate state in some other way. Signed-off-by: Michael S. Tsirkin Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Juan Quintela --- include/migration/vmstate.h | 1 + vmstate.c | 10 ++++++++++ 2 files changed, 11 insertions(+) (limited to 'vmstate.c') diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index e7e170561d..de970abedd 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -100,6 +100,7 @@ enum VMStateFlags { VMS_MULTIPLY = 0x200, /* multiply "size" field by field_size */ VMS_VARRAY_UINT8 = 0x400, /* Array with size in uint8_t field*/ VMS_VARRAY_UINT32 = 0x800, /* Array with size in uint32_t field*/ + VMS_MUST_EXIST = 0x1000, /* Field must exist in input */ }; typedef struct { diff --git a/vmstate.c b/vmstate.c index dd6f834331..f019228188 100644 --- a/vmstate.c +++ b/vmstate.c @@ -102,6 +102,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, return ret; } } + } else if (field->flags & VMS_MUST_EXIST) { + fprintf(stderr, "Input validation failed: %s/%s\n", + vmsd->name, field->name); + return -1; } field++; } @@ -142,6 +146,12 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, field->info->put(f, addr, size); } } + } else { + if (field->flags & VMS_MUST_EXIST) { + fprintf(stderr, "Output state validation failed: %s/%s\n", + vmsd->name, field->name); + assert(!(field->flags & VMS_MUST_EXIST)); + } } field++; } -- cgit 1.4.1 From d2ef4b61fe6d33d2a5dcf100a9b9440de341ad62 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 Apr 2014 19:51:42 +0300 Subject: vmstate: fix buffer overflow in target-arm/machine.c CVE-2013-4531 cpreg_vmstate_indexes is a VARRAY_INT32. A negative value for cpreg_vmstate_array_len will cause a buffer overflow. VMSTATE_INT32_LE was supposed to protect against this but doesn't because it doesn't validate that input is non-negative. Fix this macro to valide the value appropriately. The only other user of VMSTATE_INT32_LE doesn't ever use negative numbers so it doesn't care. Reported-by: Anthony Liguori Signed-off-by: Michael S. Tsirkin Signed-off-by: Juan Quintela --- vmstate.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'vmstate.c') diff --git a/vmstate.c b/vmstate.c index f019228188..dbb76665d9 100644 --- a/vmstate.c +++ b/vmstate.c @@ -337,8 +337,9 @@ const VMStateInfo vmstate_info_int32_equal = { .put = put_int32, }; -/* 32 bit int. Check that the received value is less than or equal to - the one in the field */ +/* 32 bit int. Check that the received value is non-negative + * and less than or equal to the one in the field. + */ static int get_int32_le(QEMUFile *f, void *pv, size_t size) { @@ -346,7 +347,7 @@ static int get_int32_le(QEMUFile *f, void *pv, size_t size) int32_t loaded; qemu_get_sbe32s(f, &loaded); - if (loaded <= *cur) { + if (loaded >= 0 && loaded <= *cur) { *cur = loaded; return 0; } -- cgit 1.4.1 From 767adce2d9cd397de3418caa16be35ea18d56f22 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 3 Apr 2014 19:52:28 +0300 Subject: savevm: Ignore minimum_version_id_old if there is no load_state_old At the moment we require vmstate definitions to set minimum_version_id_old to the same value as minimum_version_id if they do not provide a load_state_old handler. Since the load_state_old functionality is required only for a handful of devices that need to retain migration compatibility with a pre-vmstate implementation, this means the bulk of devices have pointless boilerplate. Relax the definition so that minimum_version_id_old is ignored if there is no load_state_old handler. Note that under the old scheme we would segfault if the vmstate specified a minimum_version_id_old that was less than minimum_version_id but did not provide a load_state_old function, and the incoming state specified a version number between minimum_version_id_old and minimum_version_id. Under the new scheme this will just result in our failing the migration. Signed-off-by: Peter Maydell Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Signed-off-by: Juan Quintela --- docs/migration.txt | 12 +++++------- vmstate.c | 9 +++++---- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'vmstate.c') diff --git a/docs/migration.txt b/docs/migration.txt index 0e0a1d44da..fe1f2bb738 100644 --- a/docs/migration.txt +++ b/docs/migration.txt @@ -139,7 +139,6 @@ static const VMStateDescription vmstate_kbd = { .name = "pckbd", .version_id = 3, .minimum_version_id = 3, - .minimum_version_id_old = 3, .fields = (VMStateField []) { VMSTATE_UINT8(write_cmd, KBDState), VMSTATE_UINT8(status, KBDState), @@ -168,12 +167,13 @@ You can see that there are several version fields: - minimum_version_id: the minimum version_id that VMState is able to understand for that device. - minimum_version_id_old: For devices that were not able to port to vmstate, we can - assign a function that knows how to read this old state. + assign a function that knows how to read this old state. This field is + ignored if there is no load_state_old handler. So, VMState is able to read versions from minimum_version_id to -version_id. And the function load_state_old() is able to load state -from minimum_version_id_old to minimum_version_id. This function is -deprecated and will be removed when no more users are left. +version_id. And the function load_state_old() (if present) is able to +load state from minimum_version_id_old to minimum_version_id. This +function is deprecated and will be removed when no more users are left. === Massaging functions === @@ -255,7 +255,6 @@ const VMStateDescription vmstate_ide_drive_pio_state = { .name = "ide_drive/pio_state", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .pre_save = ide_drive_pio_pre_save, .post_load = ide_drive_pio_post_load, .fields = (VMStateField []) { @@ -275,7 +274,6 @@ const VMStateDescription vmstate_ide_drive = { .name = "ide_drive", .version_id = 3, .minimum_version_id = 0, - .minimum_version_id_old = 0, .post_load = ide_drive_post_load, .fields = (VMStateField []) { .... several fields .... diff --git a/vmstate.c b/vmstate.c index dbb76665d9..b5882fae67 100644 --- a/vmstate.c +++ b/vmstate.c @@ -63,11 +63,12 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, if (version_id > vmsd->version_id) { return -EINVAL; } - if (version_id < vmsd->minimum_version_id_old) { - return -EINVAL; - } if (version_id < vmsd->minimum_version_id) { - return vmsd->load_state_old(f, opaque, version_id); + if (vmsd->load_state_old && + version_id >= vmsd->minimum_version_id_old) { + return vmsd->load_state_old(f, opaque, version_id); + } + return -EINVAL; } if (vmsd->pre_load) { int ret = vmsd->pre_load(opaque); -- cgit 1.4.1