The original vmstate migrated the bulk_in_pending and pending_answers
arrays but not the pending_answers queue indices (pending_answers_start,
pending_answers_end). This made migration silently lose in-flight
smartcard responses.
Add a vmstate subsection "usb-ccid/pending-answers" that migrates the
missing pending_answers indices. A compat property
x-migrate-pending-answers (default on, off for <= 11.0) gates the
subsection so older machine types keep the old behavior.
Populate pending_answers_num in pre_save so post_load can reconstruct
start/end when loading from an older source that lacks the subsection.
When the subsection is absent, post_load derives pending_answers
start/end from the legacy count. Validate all wire-sourced indices
and bulk-in entry bounds on load.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
hw/core/machine.c | 1 +
hw/usb/dev-smartcard-reader.c | 70 ++++++++++++++++++++++++++++++++++++++++---
2 files changed, 67 insertions(+), 4 deletions(-)
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 01e50ebe05e0..28842f53ae4f 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -52,6 +52,7 @@ GlobalProperty hw_compat_11_0[] = {
{ TYPE_ARM_SMMUV3, "oas", "44" },
{ "migration", "switchover-ack-legacy", "on" },
{ "usb-ccid", "x-accurate-message-length", "off" },
+ { "usb-ccid", "x-migrate-pending-answers", "off" },
};
const size_t hw_compat_11_0_len = G_N_ELEMENTS(hw_compat_11_0);
diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
index e3c86cb90e6c..5ca50c111217 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -312,6 +312,8 @@ struct USBCCIDState {
uint8_t notify_slot_change;
uint8_t debug;
bool accurate_message_length;
+ bool migrate_pending_answers;
+ bool pending_answers_loaded;
};
static uint32_t ccid_bulk_in_pending_num(USBCCIDState *s)
@@ -1409,16 +1411,35 @@ static void ccid_realize(USBDevice *dev, Error **errp)
s->debug = parse_debug_env("QEMU_CCID_DEBUG", D_VERBOSE, s->debug);
}
+static int ccid_pre_load(void *opaque)
+{
+ USBCCIDState *s = opaque;
+
+ s->pending_answers_loaded = false;
+ return 0;
+}
+
static int ccid_post_load(void *opaque, int version_id)
{
USBCCIDState *s = opaque;
int i;
- if (s->bulk_in_pending_end - s->bulk_in_pending_start > BULK_IN_PENDING_NUM) {
+ if (!s->pending_answers_loaded) {
+ /*
+ * Version 1 did not migrate the pending_answers[] queue indices.
+ * The historical interpretation of pending_answers[] as starting at
+ * element zero.
+ */
+ if (s->pending_answers_num > PENDING_ANSWERS_NUM) {
+ return -EINVAL;
+ }
+ s->pending_answers_start = 0;
+ s->pending_answers_end = s->pending_answers_num;
+ } else if (s->pending_answers_end - s->pending_answers_start > PENDING_ANSWERS_NUM) {
return -EINVAL;
}
- if (s->pending_answers_num > PENDING_ANSWERS_NUM) {
+ if (s->bulk_in_pending_end - s->bulk_in_pending_start > BULK_IN_PENDING_NUM) {
return -EINVAL;
}
@@ -1447,12 +1468,14 @@ static bool ccid_pre_save(void *opaque, Error **errp)
{
USBCCIDState *s = opaque;
- if (ccid_pending_answers_num(s) || ccid_bulk_in_pending_num(s)) {
+ if (!s->migrate_pending_answers &&
+ (ccid_pending_answers_num(s) || ccid_bulk_in_pending_num(s))) {
error_setg(errp, "usb-ccid has pending queue state which cannot be "
"migrated safely");
return false;
}
+ s->pending_answers_num = ccid_pending_answers_num(s);
s->state_vmstate = s->dev.state;
return true;
@@ -1493,10 +1516,43 @@ static const VMStateDescription usb_device_vmstate = {
}
};
+static bool ccid_pending_answers_needed(void *opaque)
+{
+ USBCCIDState *s = opaque;
+
+ if (!s->migrate_pending_answers) {
+ return false;
+ }
+
+ return ccid_pending_answers_num(s) > 0;
+}
+
+static int ccid_pending_answers_post_load(void *opaque, int version_id)
+{
+ USBCCIDState *s = opaque;
+
+ s->pending_answers_loaded = true;
+ return 0;
+}
+
+static const VMStateDescription ccid_pending_answers_vmstate = {
+ .name = "usb-ccid/pending-answers",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = ccid_pending_answers_needed,
+ .post_load = ccid_pending_answers_post_load,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(pending_answers_start, USBCCIDState),
+ VMSTATE_UINT32(pending_answers_end, USBCCIDState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription ccid_vmstate = {
.name = "usb-ccid",
.version_id = 1,
.minimum_version_id = 1,
+ .pre_load = ccid_pre_load,
.post_load = ccid_post_load,
.pre_save_errp = ccid_pre_save,
.fields = (const VMStateField[]) {
@@ -1523,13 +1579,19 @@ static const VMStateDescription ccid_vmstate = {
VMSTATE_UNUSED(1), /* was migration_state */
VMSTATE_UINT32(state_vmstate, USBCCIDState),
VMSTATE_END_OF_LIST()
- }
+ },
+ .subsections = (const VMStateDescription * const []) {
+ &ccid_pending_answers_vmstate,
+ NULL
+ },
};
static const Property ccid_properties[] = {
DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0),
DEFINE_PROP_BOOL("x-accurate-message-length", USBCCIDState,
accurate_message_length, true),
+ DEFINE_PROP_BOOL("x-migrate-pending-answers", USBCCIDState,
+ migrate_pending_answers, true),
};
static void ccid_class_initfn(ObjectClass *klass, const void *data)
--
2.55.0