* [PULL 01/10] tests/qtest/migration: Fix leak of migration tests data
2026-03-17 18:23 [PULL 00/10] Migration/Qtest patches for 2026-03-17 Fabiano Rosas
@ 2026-03-17 18:23 ` Fabiano Rosas
2026-03-17 18:23 ` [PULL 02/10] io: Fix TLS bye task leak Fabiano Rosas
` (9 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Fabiano Rosas @ 2026-03-17 18:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Xu, Prasad Pandit
When the migration-test is invoked with the '-p' flag (to run a single
test), the glib code won't call the destroy function for the
not-executed tests, causing the MigrationTest wrapper data to leak.
This doesn't affect make check, but affects debugging use-cases where
having a leak pop up in ASAN output is extra annoying.
Fix by adding the tests data to a list and freeing them all at the end
of migration-test execution. Any tests actually dispatched by glib
will have the destroy function called as usual.
Note that migration_test_add_suffix() is altered to call
migration_test_add() so that there's only one place adding the data to
the list.
Performance is not an issue at the moment, we have < 100 tests.
Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Prasad Pandit <pjp@fedoraproject.org>
Link: https://lore.kernel.org/qemu-devel/20260311213418.16951-2-farosas@suse.de
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/migration/framework.c | 2 ++
tests/qtest/migration/migration-util.c | 19 ++++++++++++++-----
tests/qtest/migration/migration-util.h | 2 +-
3 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c
index 0bfc241914..b9371372de 100644
--- a/tests/qtest/migration/framework.c
+++ b/tests/qtest/migration/framework.c
@@ -1162,5 +1162,7 @@ int migration_env_clean(MigrationTestEnv *env)
}
g_free(tmpfs);
+ migration_tests_free();
+
return ret;
}
diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c
index c2462306a1..2648ad7f61 100644
--- a/tests/qtest/migration/migration-util.c
+++ b/tests/qtest/migration/migration-util.c
@@ -38,6 +38,7 @@
#include "linux/kvm.h"
#endif
+GQueue *tests;
static char *SocketAddress_to_str(SocketAddress *addr)
{
@@ -243,6 +244,8 @@ static void migration_test_destroy(gpointer data)
{
MigrationTest *test = (MigrationTest *)data;
+ g_queue_remove(tests, test);
+
g_free(test->data);
g_free(test->name);
g_free(test);
@@ -268,21 +271,27 @@ void migration_test_add(const char *path,
qtest_add_data_func_full(path, test, migration_test_wrapper,
migration_test_destroy);
+ if (!tests) {
+ tests = g_queue_new();
+ }
+ g_queue_push_tail(tests, test);
}
void migration_test_add_suffix(const char *path, const char *suffix,
void (*fn)(char *name, MigrateCommon *args))
{
- MigrationTest *test = g_new0(MigrationTest, 1);
+ g_autofree char *name = NULL;
g_assert(g_str_has_suffix(path, "/"));
g_assert(!g_str_has_prefix(suffix, "/"));
- test->func = fn;
- test->name = g_strconcat(path, suffix, NULL);
+ name = g_strconcat(path, suffix, NULL);
+ migration_test_add(name, fn);
+}
- qtest_add_data_func_full(test->name, test, migration_test_wrapper,
- migration_test_destroy);
+void migration_tests_free(void)
+{
+ g_queue_free_full(tests, migration_test_destroy);
}
#ifdef O_DIRECT
diff --git a/tests/qtest/migration/migration-util.h b/tests/qtest/migration/migration-util.h
index e73d69bab0..694773e594 100644
--- a/tests/qtest/migration/migration-util.h
+++ b/tests/qtest/migration/migration-util.h
@@ -59,5 +59,5 @@ void migration_test_add_suffix(const char *path, const char *suffix,
void (*fn)(char *name, MigrateCommon *args));
char *migrate_get_connect_uri(QTestState *who);
void migrate_set_ports(QTestState *to, QList *channel_list);
-
+void migration_tests_free(void);
#endif /* MIGRATION_UTIL_H */
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PULL 02/10] io: Fix TLS bye task leak
2026-03-17 18:23 [PULL 00/10] Migration/Qtest patches for 2026-03-17 Fabiano Rosas
2026-03-17 18:23 ` [PULL 01/10] tests/qtest/migration: Fix leak of migration tests data Fabiano Rosas
@ 2026-03-17 18:23 ` Fabiano Rosas
2026-03-18 20:36 ` Michael Tokarev
2026-03-17 18:23 ` [PULL 03/10] tests/qtest/migration: Fix leak in CPR exec test Fabiano Rosas
` (8 subsequent siblings)
10 siblings, 1 reply; 16+ messages in thread
From: Fabiano Rosas @ 2026-03-17 18:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Xu, Daniel P. Berrangé
Recent fixes to TLS tasks memory handling have left the TLS bye task
uncovered. Fix by freeing the task in the same way the handshake task
is freed.
Direct leak of 704 byte(s) in 4 object(s) allocated from:
#1 0x7f5909b1d6a0 in g_malloc0 ../glib/gmem.c:163
#2 0x557650496d61 in qio_task_new ../io/task.c:58:12
#3 0x557650475d7f in qio_channel_tls_bye ../io/channel-tls.c:352:12
#4 0x55764f7a1bb4 in migration_tls_channel_end ../migration/tls.c:159:5
#5 0x55764f709750 in migration_ioc_shutdown_gracefully ../migration/multifd.c:462:9
#6 0x55764f6fcf53 in multifd_send_terminate_threads ../migration/multifd.c:493:13
#7 0x55764f6fcafb in multifd_send_shutdown ../migration/multifd.c:580:5
#8 0x55764f6e1b14 in migration_cleanup ../migration/migration.c:1323:9
#9 0x55764f6f5bac in migration_cleanup_bh ../migration/migration.c:1350:5
Fixes: d39d0f3acd ("io: fix cleanup for TLS I/O source data on cancellation")
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Acked-by: Daniel P. Berrangé <berrange@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260311213418.16951-3-farosas@suse.de
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
io/channel-tls.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/io/channel-tls.c b/io/channel-tls.c
index 940fc3c6d1..31ec4d236d 100644
--- a/io/channel-tls.c
+++ b/io/channel-tls.c
@@ -352,7 +352,9 @@ void qio_channel_tls_bye(QIOChannelTLS *ioc, Error **errp)
task = qio_task_new(OBJECT(ioc), propagate_error, errp, NULL);
trace_qio_channel_tls_bye_start(ioc);
- qio_channel_tls_bye_task(ioc, task, NULL);
+ if (qio_channel_tls_bye_task(ioc, task, NULL)) {
+ qio_task_free(task);
+ }
}
static void qio_channel_tls_init(Object *obj G_GNUC_UNUSED)
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PULL 02/10] io: Fix TLS bye task leak
2026-03-17 18:23 ` [PULL 02/10] io: Fix TLS bye task leak Fabiano Rosas
@ 2026-03-18 20:36 ` Michael Tokarev
2026-03-19 8:57 ` Daniel P. Berrangé
0 siblings, 1 reply; 16+ messages in thread
From: Michael Tokarev @ 2026-03-18 20:36 UTC (permalink / raw)
To: Fabiano Rosas, qemu-devel; +Cc: Peter Xu, Daniel P. Berrangé, qemu-stable
On 17.03.2026 21:23, Fabiano Rosas wrote:
> Recent fixes to TLS tasks memory handling have left the TLS bye task
> uncovered. Fix by freeing the task in the same way the handshake task
> is freed.
>
> Direct leak of 704 byte(s) in 4 object(s) allocated from:
> #1 0x7f5909b1d6a0 in g_malloc0 ../glib/gmem.c:163
> #2 0x557650496d61 in qio_task_new ../io/task.c:58:12
> #3 0x557650475d7f in qio_channel_tls_bye ../io/channel-tls.c:352:12
> #4 0x55764f7a1bb4 in migration_tls_channel_end ../migration/tls.c:159:5
> #5 0x55764f709750 in migration_ioc_shutdown_gracefully ../migration/multifd.c:462:9
> #6 0x55764f6fcf53 in multifd_send_terminate_threads ../migration/multifd.c:493:13
> #7 0x55764f6fcafb in multifd_send_shutdown ../migration/multifd.c:580:5
> #8 0x55764f6e1b14 in migration_cleanup ../migration/migration.c:1323:9
> #9 0x55764f6f5bac in migration_cleanup_bh ../migration/migration.c:1350:5
>
> Fixes: d39d0f3acd ("io: fix cleanup for TLS I/O source data on cancellation")
> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
> Acked-by: Daniel P. Berrangé <berrange@redhat.com>
> Link: https://lore.kernel.org/qemu-devel/20260311213418.16951-3-farosas@suse.de
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
This seems like a qemu-stable material, -- since the above mentioned
commit is in currently active stable branches.
Please let me know if I shouldn't pick it up for the currently active
stable branches (10.0 & 10.2).
Thanks,
/mjt
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PULL 02/10] io: Fix TLS bye task leak
2026-03-18 20:36 ` Michael Tokarev
@ 2026-03-19 8:57 ` Daniel P. Berrangé
0 siblings, 0 replies; 16+ messages in thread
From: Daniel P. Berrangé @ 2026-03-19 8:57 UTC (permalink / raw)
To: Michael Tokarev; +Cc: Fabiano Rosas, qemu-devel, Peter Xu, qemu-stable
On Wed, Mar 18, 2026 at 11:36:50PM +0300, Michael Tokarev wrote:
> On 17.03.2026 21:23, Fabiano Rosas wrote:
> > Recent fixes to TLS tasks memory handling have left the TLS bye task
> > uncovered. Fix by freeing the task in the same way the handshake task
> > is freed.
> >
> > Direct leak of 704 byte(s) in 4 object(s) allocated from:
> > #1 0x7f5909b1d6a0 in g_malloc0 ../glib/gmem.c:163
> > #2 0x557650496d61 in qio_task_new ../io/task.c:58:12
> > #3 0x557650475d7f in qio_channel_tls_bye ../io/channel-tls.c:352:12
> > #4 0x55764f7a1bb4 in migration_tls_channel_end ../migration/tls.c:159:5
> > #5 0x55764f709750 in migration_ioc_shutdown_gracefully ../migration/multifd.c:462:9
> > #6 0x55764f6fcf53 in multifd_send_terminate_threads ../migration/multifd.c:493:13
> > #7 0x55764f6fcafb in multifd_send_shutdown ../migration/multifd.c:580:5
> > #8 0x55764f6e1b14 in migration_cleanup ../migration/migration.c:1323:9
> > #9 0x55764f6f5bac in migration_cleanup_bh ../migration/migration.c:1350:5
> >
> > Fixes: d39d0f3acd ("io: fix cleanup for TLS I/O source data on cancellation")
> > Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
> > Acked-by: Daniel P. Berrangé <berrange@redhat.com>
> > Link: https://lore.kernel.org/qemu-devel/20260311213418.16951-3-farosas@suse.de
> > Signed-off-by: Fabiano Rosas <farosas@suse.de>
>
> This seems like a qemu-stable material, -- since the above mentioned
> commit is in currently active stable branches.
>
> Please let me know if I shouldn't pick it up for the currently active
> stable branches (10.0 & 10.2).
Yes, please take to stable.
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PULL 03/10] tests/qtest/migration: Fix leak in CPR exec test
2026-03-17 18:23 [PULL 00/10] Migration/Qtest patches for 2026-03-17 Fabiano Rosas
2026-03-17 18:23 ` [PULL 01/10] tests/qtest/migration: Fix leak of migration tests data Fabiano Rosas
2026-03-17 18:23 ` [PULL 02/10] io: Fix TLS bye task leak Fabiano Rosas
@ 2026-03-17 18:23 ` Fabiano Rosas
2026-03-17 18:23 ` [PULL 04/10] migration/multifd: Fix leaks of TLS error objects Fabiano Rosas
` (7 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Fabiano Rosas @ 2026-03-17 18:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Xu, Prasad Pandit
The string was being dup'ed only to get around the const of the
qdict_get_str() return value.
Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Prasad Pandit <pjp@fedoraproject.org>
Link: https://lore.kernel.org/qemu-devel/20260311213418.16951-4-farosas@suse.de
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/migration/cpr-tests.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c
index 0d97b5b89f..63ca5f3996 100644
--- a/tests/qtest/migration/cpr-tests.c
+++ b/tests/qtest/migration/cpr-tests.c
@@ -154,15 +154,16 @@ static void set_cpr_exec_args(QTestState *who, MigrateCommon *args)
static void wait_for_migration_event(QTestState *who, const char *waitfor)
{
QDict *rsp, *data;
- char *status;
bool done = false;
while (!done) {
+ const char *status;
+
rsp = qtest_qmp_eventwait_ref(who, "MIGRATION");
g_assert(qdict_haskey(rsp, "data"));
data = qdict_get_qdict(rsp, "data");
g_assert(qdict_haskey(data, "status"));
- status = g_strdup(qdict_get_str(data, "status"));
+ status = qdict_get_str(data, "status");
g_assert(strcmp(status, "failed"));
done = !strcmp(status, waitfor);
qobject_unref(rsp);
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PULL 04/10] migration/multifd: Fix leaks of TLS error objects
2026-03-17 18:23 [PULL 00/10] Migration/Qtest patches for 2026-03-17 Fabiano Rosas
` (2 preceding siblings ...)
2026-03-17 18:23 ` [PULL 03/10] tests/qtest/migration: Fix leak in CPR exec test Fabiano Rosas
@ 2026-03-17 18:23 ` Fabiano Rosas
2026-03-17 18:23 ` [PULL 05/10] tests/qtest/migration: Force exit-on-error=false Fabiano Rosas
` (6 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Fabiano Rosas @ 2026-03-17 18:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Xu, Prasad Pandit
The code currently ignores errors from multifd threads that happen
after a first error has already been propagated. Make sure the
subsequent errors are freed appopriately.
This fixes a leak of the TLS session->werr when the certificate
validation fails after multifd threads are already running. The first
writes on the threads will fail deep into the gnutls stack.
No need to check if(err) because the callers are all under a similar
check.
Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Prasad Pandit <pjp@fedoraproject.org>
Link: https://lore.kernel.org/qemu-devel/20260311213418.16951-5-farosas@suse.de
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
migration/multifd.c | 27 ++++++++++++---------------
1 file changed, 12 insertions(+), 15 deletions(-)
diff --git a/migration/multifd.c b/migration/multifd.c
index 8b9ed84805..035cb70f7b 100644
--- a/migration/multifd.c
+++ b/migration/multifd.c
@@ -412,28 +412,25 @@ bool multifd_send(MultiFDSendData **send_data)
/* Multifd send side hit an error; remember it and prepare to quit */
static void multifd_send_error_propagate(Error *err)
{
+ MigrationState *s = migrate_get_current();
+
/*
- * We don't want to exit each threads twice. Depending on where
- * we get the error, or if there are two independent errors in two
- * threads at the same time, we can end calling this function
- * twice.
+ * There may be independent errors in each thread. Propagate the
+ * first and free the subsequent ones.
*/
if (qatomic_xchg(&multifd_send_state->exiting, 1)) {
+ error_free(err);
return;
}
- if (err) {
- MigrationState *s = migrate_get_current();
+ migrate_error_propagate(s, err);
- migrate_error_propagate(s, err);
-
- if (s->state == MIGRATION_STATUS_SETUP ||
- s->state == MIGRATION_STATUS_PRE_SWITCHOVER ||
- s->state == MIGRATION_STATUS_DEVICE ||
- s->state == MIGRATION_STATUS_ACTIVE) {
- migrate_set_state(&s->state, s->state,
- MIGRATION_STATUS_FAILING);
- }
+ if (s->state == MIGRATION_STATUS_SETUP ||
+ s->state == MIGRATION_STATUS_PRE_SWITCHOVER ||
+ s->state == MIGRATION_STATUS_DEVICE ||
+ s->state == MIGRATION_STATUS_ACTIVE) {
+ migrate_set_state(&s->state, s->state,
+ MIGRATION_STATUS_FAILING);
}
}
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PULL 05/10] tests/qtest/migration: Force exit-on-error=false
2026-03-17 18:23 [PULL 00/10] Migration/Qtest patches for 2026-03-17 Fabiano Rosas
` (3 preceding siblings ...)
2026-03-17 18:23 ` [PULL 04/10] migration/multifd: Fix leaks of TLS error objects Fabiano Rosas
@ 2026-03-17 18:23 ` Fabiano Rosas
2026-03-26 9:02 ` Thomas Huth
2026-03-17 18:23 ` [PULL 06/10] migration: assert that the same migration handler is not being added twice Fabiano Rosas
` (5 subsequent siblings)
10 siblings, 1 reply; 16+ messages in thread
From: Fabiano Rosas @ 2026-03-17 18:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Xu, Prasad Pandit
Some tests can cause QEMU to exit(1) too early while the incoming
coroutine has not yielded for a first time yet. This trips ASAN
because resources related to dispatching the incoming process will
still be allocated in the io/channel.c layer without a
straight-forward way for the migration code to clean them up.
As an example of one such issue, the UUID validation happens early
enough that the temporary socket from qio_net_listener_channel_func()
still has an elevated refcount. If it fails, the listener dispatch
code never gets to free the resource:
Direct leak of 400 byte(s) in 1 object(s) allocated from:
#0 0x55e668890a07 in malloc asan_malloc_linux.cpp:68:3
#1 0x7f3c7e2b6648 in g_malloc ../glib/gmem.c:130
#2 0x55e66a8ef05f in object_new_with_type ../qom/object.c:767:15
#3 0x55e66a8ef178 in object_new ../qom/object.c:789:12
#4 0x55e66a93bcc6 in qio_channel_socket_new ../io/channel-socket.c:70:31
#5 0x55e66a93f34f in qio_channel_socket_accept ../io/channel-socket.c:401:12
#6 0x55e66a96752a in qio_net_listener_channel_func ../io/net-listener.c:64:12
#7 0x55e66a94bdac in qio_channel_fd_source_dispatch ../io/channel-watch.c:84:12
#8 0x7f3c7e2adf4b in g_main_dispatch ../glib/gmain.c:3476
#9 0x7f3c7e2adf4b in g_main_context_dispatch_unlocked ../glib/gmain.c:4284
#10 0x7f3c7e2b00c8 in g_main_context_dispatch ../glib/gmain.c:4272
The exit(1) also requires some tests to setup qtest to expect a return
code of 1 from the QEMU process. Although we can check migration
status changes to be fairly certain where the failure happened, there
is always the possibility of QEMU exiting for another reason and the
test passing. This happens frequently with sanitizers enabled, but
also risks masking issues in the regular build.
Stop allowing the incoming migration to exit and instead require the
tests to wait for the FAILED state and end QEMU gracefully with
qtest_quit.
In practice this means setting exit-on-error=false for every incoming
migration, changing MIG_TEST_FAIL_DEST_QUIT_ERR to MIG_TEST_FAIL and
waiting for a change of state where necessary.
With this, the MIG_TEST_FAIL_DEST_QUIT_ERR error result is now unused,
remove it.
The affected tests are:
validate_uuid_error
multifd_tcp_cancel
dirty_limit
precopy_unix_tls_x509_default_host
precopy_tcp_tls_no_hostname
tcp_tls_x509_mismatch_host
dbus_vmstate_missing_src
dbus_vmstate_missing_dst
Also add a comment to QEMU source explaining that the incoming
coroutine might block for a while until it yields as this is the
actual root cause of the issue.
Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Prasad Pandit <pjp@fedoraproject.org>
Link: https://lore.kernel.org/qemu-devel/20260311213418.16951-6-farosas@suse.de
[assert that key doesn't already exists]
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
migration/migration.c | 5 +++++
tests/qtest/dbus-vmstate-test.c | 5 +++--
tests/qtest/migration/framework.c | 5 +----
tests/qtest/migration/framework.h | 2 --
tests/qtest/migration/migration-qmp.c | 7 +++++++
tests/qtest/migration/misc-tests.c | 4 ++--
tests/qtest/migration/precopy-tests.c | 12 +++++-------
tests/qtest/migration/tls-tests.c | 14 ++++++++------
8 files changed, 31 insertions(+), 23 deletions(-)
diff --git a/migration/migration.c b/migration/migration.c
index f949708629..c77832f851 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -898,6 +898,11 @@ void migration_start_incoming(void)
Coroutine *co = qemu_coroutine_create(process_incoming_migration_co, NULL);
qemu_coroutine_enter(co);
+ /*
+ * This doesn't return right away. The coroutine will run
+ * unimpeded until its first yield, which may happen as late as
+ * the force yield at ram_load_precopy().
+ */
}
int migrate_send_rp_switchover_ack(MigrationIncomingState *mis)
diff --git a/tests/qtest/dbus-vmstate-test.c b/tests/qtest/dbus-vmstate-test.c
index 6c990864e3..0a82cc9f93 100644
--- a/tests/qtest/dbus-vmstate-test.c
+++ b/tests/qtest/dbus-vmstate-test.c
@@ -219,8 +219,8 @@ test_dbus_vmstate(Test *test)
dstaddr = g_strsplit(g_test_dbus_get_bus_address(dstbus), ",", 2);
dst_qemu_args =
- g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s -incoming %s",
- dstaddr[0], uri);
+ g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s -incoming defer",
+ dstaddr[0]);
src_qemu = qtest_init(src_qemu_args);
dst_qemu = qtest_init(dst_qemu_args);
@@ -229,6 +229,7 @@ test_dbus_vmstate(Test *test)
thread = g_thread_new("dbus-vmstate-thread", dbus_vmstate_thread, loop);
+ migrate_incoming_qmp(dst_qemu, uri, NULL, "{}");
migrate_qmp(src_qemu, uri, "{}");
test->src_qemu = src_qemu;
if (test->migrate_fail) {
diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c
index b9371372de..9f71d51f1e 100644
--- a/tests/qtest/migration/framework.c
+++ b/tests/qtest/migration/framework.c
@@ -576,6 +576,7 @@ static int migrate_postcopy_prepare(QTestState **from_ptr,
migrate_prepare_for_dirty_mem(from);
qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming',"
" 'arguments': { "
+ " 'exit-on-error': false,"
" 'channels': [ { 'channel-type': 'main',"
" 'addr': { 'transport': 'socket',"
" 'type': 'inet',"
@@ -906,10 +907,6 @@ int test_precopy_common(MigrateCommon *args)
if (args->result != MIG_TEST_SUCCEED) {
bool allow_active = args->result == MIG_TEST_FAIL;
wait_for_migration_fail(from, allow_active);
-
- if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) {
- qtest_set_expected_status(to, EXIT_FAILURE);
- }
} else {
if (args->live) {
/*
diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h
index 80eef75893..79604c60f5 100644
--- a/tests/qtest/migration/framework.h
+++ b/tests/qtest/migration/framework.h
@@ -208,8 +208,6 @@ typedef struct {
MIG_TEST_SUCCEED = 0,
/* This test should fail, dest qemu should keep alive */
MIG_TEST_FAIL,
- /* This test should fail, dest qemu should fail with abnormal status */
- MIG_TEST_FAIL_DEST_QUIT_ERR,
/* The QMP command for this migration should fail with an error */
MIG_TEST_QMP_ERROR,
} result;
diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c
index 8279504db1..437b5eaeff 100644
--- a/tests/qtest/migration/migration-qmp.c
+++ b/tests/qtest/migration/migration-qmp.c
@@ -173,6 +173,13 @@ void migrate_incoming_qmp(QTestState *to, const char *uri, QObject *channels,
/* This function relies on the event to work, make sure it's enabled */
migrate_set_capability(to, "events", true);
+ /*
+ * Set the incoming migration to never exit QEMU abruptly during
+ * the tests. It causes issues when running sanitizers and
+ * expecting a failure exit code can mask other issues.
+ */
+ g_assert(!qdict_haskey(args, "exit-on-error"));
+ qdict_put_bool(args, "exit-on-error", false);
rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
args);
diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c
index 810e9e6549..196f1ca842 100644
--- a/tests/qtest/migration/misc-tests.c
+++ b/tests/qtest/migration/misc-tests.c
@@ -131,7 +131,7 @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail)
g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
QTestState *from, *to;
- if (migrate_start(&from, &to, uri, args)) {
+ if (migrate_start(&from, &to, "defer", args)) {
return;
}
@@ -146,10 +146,10 @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail)
/* Wait for the first serial output from the source */
wait_for_serial("src_serial");
+ migrate_incoming_qmp(to, uri, NULL, "{}");
migrate_qmp(from, to, uri, NULL, "{}");
if (should_fail) {
- qtest_set_expected_status(to, EXIT_FAILURE);
wait_for_migration_fail(from, true);
} else {
wait_for_migration_complete(from);
diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c
index f17dc5176d..c6c8ae3004 100644
--- a/tests/qtest/migration/precopy-tests.c
+++ b/tests/qtest/migration/precopy-tests.c
@@ -545,8 +545,7 @@ static void test_multifd_tcp_cancel(MigrateCommon *args, bool postcopy_ram)
migrate_cancel(from);
/* Make sure QEMU process "to" exited */
- qtest_set_expected_status(to, EXIT_FAILURE);
- qtest_wait_qemu(to);
+ migration_event_wait(to, "failed");
qtest_quit(to);
/*
@@ -634,7 +633,7 @@ static void test_cancel_src_after_cancelled(QTestState *from, QTestState *to,
const char *uri, const char *phase,
MigrateStart *args)
{
- migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
+ migrate_incoming_qmp(to, uri, NULL, "{}");
wait_for_serial("src_serial");
migrate_ensure_converge(from);
@@ -659,7 +658,7 @@ static void test_cancel_src_after_complete(QTestState *from, QTestState *to,
const char *uri, const char *phase,
MigrateStart *args)
{
- migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
+ migrate_incoming_qmp(to, uri, NULL, "{}");
wait_for_serial("src_serial");
migrate_ensure_converge(from);
@@ -690,7 +689,7 @@ static void test_cancel_src_after_none(QTestState *from, QTestState *to,
wait_for_serial("src_serial");
migrate_cancel(from);
- migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
+ migrate_incoming_qmp(to, uri, NULL, "{}");
migrate_ensure_converge(from);
migrate_qmp(from, to, uri, NULL, "{}");
@@ -709,7 +708,7 @@ static void test_cancel_src_pre_switchover(QTestState *from, QTestState *to,
migrate_set_capability(from, "multifd", true);
migrate_set_capability(to, "multifd", true);
- migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
+ migrate_incoming_qmp(to, uri, NULL, "{}");
wait_for_serial("src_serial");
migrate_ensure_converge(from);
@@ -1101,7 +1100,6 @@ static void test_dirty_limit(char *name, MigrateCommon *args)
/* destination always fails after cancel */
migration_event_wait(to, "failed");
- qtest_set_expected_status(to, EXIT_FAILURE);
qtest_quit(to);
/* Check if dirty limit throttle switched off, set timeout 1ms */
diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c
index 4ce7f6c676..87898af260 100644
--- a/tests/qtest/migration/tls-tests.c
+++ b/tests/qtest/migration/tls-tests.c
@@ -441,10 +441,10 @@ static void test_precopy_unix_tls_x509_default_host(char *name,
g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
args->connect_uri = uri;
- args->listen_uri = uri;
+ args->listen_uri = "defer";
args->start_hook = migrate_hook_start_tls_x509_default_host;
args->end_hook = migrate_hook_end_tls_x509;
- args->result = MIG_TEST_FAIL_DEST_QUIT_ERR;
+ args->result = MIG_TEST_FAIL;
args->start.hide_stderr = true;
@@ -522,10 +522,11 @@ migrate_hook_start_tls_x509_no_host(QTestState *from, QTestState *to)
static void test_precopy_tcp_tls_no_hostname(char *name, MigrateCommon *args)
{
- args->listen_uri = "tcp:127.0.0.1:0";
+ args->listen_uri = "defer";
+ args->connect_uri = "tcp:127.0.0.1:0";
args->start_hook = migrate_hook_start_tls_x509_no_host;
args->end_hook = migrate_hook_end_tls_x509;
- args->result = MIG_TEST_FAIL_DEST_QUIT_ERR;
+ args->result = MIG_TEST_FAIL;
args->start.hide_stderr = true;
@@ -556,10 +557,11 @@ static void test_precopy_tcp_tls_x509_override_host(char *name,
static void test_precopy_tcp_tls_x509_mismatch_host(char *name,
MigrateCommon *args)
{
- args->listen_uri = "tcp:127.0.0.1:0";
+ args->listen_uri = "defer";
+ args->connect_uri = "tcp:127.0.0.1:0";
args->start_hook = migrate_hook_start_tls_x509_mismatch_host;
args->end_hook = migrate_hook_end_tls_x509;
- args->result = MIG_TEST_FAIL_DEST_QUIT_ERR;
+ args->result = MIG_TEST_FAIL;
args->start.hide_stderr = true;
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PULL 05/10] tests/qtest/migration: Force exit-on-error=false
2026-03-17 18:23 ` [PULL 05/10] tests/qtest/migration: Force exit-on-error=false Fabiano Rosas
@ 2026-03-26 9:02 ` Thomas Huth
2026-03-26 13:28 ` Fabiano Rosas
0 siblings, 1 reply; 16+ messages in thread
From: Thomas Huth @ 2026-03-26 9:02 UTC (permalink / raw)
To: Fabiano Rosas, qemu-devel; +Cc: Peter Xu, Prasad Pandit
On 17/03/2026 19.23, Fabiano Rosas wrote:
> Some tests can cause QEMU to exit(1) too early while the incoming
> coroutine has not yielded for a first time yet. This trips ASAN
> because resources related to dispatching the incoming process will
> still be allocated in the io/channel.c layer without a
> straight-forward way for the migration code to clean them up.
>
> As an example of one such issue, the UUID validation happens early
> enough that the temporary socket from qio_net_listener_channel_func()
> still has an elevated refcount. If it fails, the listener dispatch
> code never gets to free the resource:
>
> Direct leak of 400 byte(s) in 1 object(s) allocated from:
> #0 0x55e668890a07 in malloc asan_malloc_linux.cpp:68:3
> #1 0x7f3c7e2b6648 in g_malloc ../glib/gmem.c:130
> #2 0x55e66a8ef05f in object_new_with_type ../qom/object.c:767:15
> #3 0x55e66a8ef178 in object_new ../qom/object.c:789:12
> #4 0x55e66a93bcc6 in qio_channel_socket_new ../io/channel-socket.c:70:31
> #5 0x55e66a93f34f in qio_channel_socket_accept ../io/channel-socket.c:401:12
> #6 0x55e66a96752a in qio_net_listener_channel_func ../io/net-listener.c:64:12
> #7 0x55e66a94bdac in qio_channel_fd_source_dispatch ../io/channel-watch.c:84:12
> #8 0x7f3c7e2adf4b in g_main_dispatch ../glib/gmain.c:3476
> #9 0x7f3c7e2adf4b in g_main_context_dispatch_unlocked ../glib/gmain.c:4284
> #10 0x7f3c7e2b00c8 in g_main_context_dispatch ../glib/gmain.c:4272
>
> The exit(1) also requires some tests to setup qtest to expect a return
> code of 1 from the QEMU process. Although we can check migration
> status changes to be fairly certain where the failure happened, there
> is always the possibility of QEMU exiting for another reason and the
> test passing. This happens frequently with sanitizers enabled, but
> also risks masking issues in the regular build.
>
> Stop allowing the incoming migration to exit and instead require the
> tests to wait for the FAILED state and end QEMU gracefully with
> qtest_quit.
>
> In practice this means setting exit-on-error=false for every incoming
> migration, changing MIG_TEST_FAIL_DEST_QUIT_ERR to MIG_TEST_FAIL and
> waiting for a change of state where necessary.
>
> With this, the MIG_TEST_FAIL_DEST_QUIT_ERR error result is now unused,
> remove it.
>
> The affected tests are:
> validate_uuid_error
> multifd_tcp_cancel
> dirty_limit
> precopy_unix_tls_x509_default_host
> precopy_tcp_tls_no_hostname
> tcp_tls_x509_mismatch_host
> dbus_vmstate_missing_src
> dbus_vmstate_missing_dst
>
> Also add a comment to QEMU source explaining that the incoming
> coroutine might block for a while until it yields as this is the
> actual root cause of the issue.
>
> Reviewed-by: Peter Xu <peterx@redhat.com>
> Reviewed-by: Prasad Pandit <pjp@fedoraproject.org>
> Link: https://lore.kernel.org/qemu-devel/20260311213418.16951-6-farosas@suse.de
> [assert that key doesn't already exists]
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
> migration/migration.c | 5 +++++
> tests/qtest/dbus-vmstate-test.c | 5 +++--
> tests/qtest/migration/framework.c | 5 +----
> tests/qtest/migration/framework.h | 2 --
> tests/qtest/migration/migration-qmp.c | 7 +++++++
> tests/qtest/migration/misc-tests.c | 4 ++--
> tests/qtest/migration/precopy-tests.c | 12 +++++-------
> tests/qtest/migration/tls-tests.c | 14 ++++++++------
> 8 files changed, 31 insertions(+), 23 deletions(-)
Hi Fabiano,
this patch now triggers a failure in the qtests when I'm running these in
"SPEED=thorough" mode:
MESON_TEST_ITERATION=1 MALLOC_PERTURB_=120
ASAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1 G_TEST_SLOW=1
PYTHON=/home/thuth/tmp/qemu-build/pyvenv/bin/python3 RUST_BACKTRACE=1
QTEST_QEMU_IMG=./qemu-img
G_TEST_DBUS_DAEMON=/home/thuth/devel/qemu/tests/dbus-vmstate-daemon.sh
QTEST_QEMU_STORAGE_DAEMON_BINARY=./storage-daemon/qemu-storage-daemon
UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1
MSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1
QTEST_QEMU_BINARY=./qemu-system-x86_64
/home/thuth/tmp/qemu-build/tests/qtest/migration-test --tap -k --full
TAP version 14
# random seed: R02Sb882c8142734dce2265e65214fd2b060
# starting QEMU: exec ./qemu-system-x86_64 -qtest
unix:/tmp/qtest-106610.sock -qtest-log /dev/null -chardev
socket,path=/tmp/qtest-106610.qmp,id=char0 -mon chardev=char0,mode=control
-display none -audio none -run-with exit-with-parent=on -machine none -accel
qtest
# Skipping test: userfaultfd not available
1..80
# Start of x86_64 tests
# Running /x86_64/dirty_limit
# Using machine type: pc-q35-11.0
# starting QEMU: exec ./qemu-system-x86_64 -qtest
unix:/tmp/qtest-106610.sock -qtest-log /dev/null -chardev
socket,path=/tmp/qtest-106610.qmp,id=char0 -mon chardev=char0,mode=control
-display none -audio none -run-with exit-with-parent=on -accel
kvm,dirty-ring-size=4096 -accel tcg -machine pc-q35-11.0, -name
source,debug-threads=on -machine memory-backend=mig.mem -object
memory-backend-ram,id=mig.mem,size=150M,share=off -serial
file:/tmp/migration-test-8B95M3/src_serial -drive
if=none,id=d0,file=/tmp/migration-test-8B95M3/bootsect,format=raw -device
ide-hd,drive=d0,secs=1,cyls=1,heads=1 2>/dev/null -accel qtest
# starting QEMU: exec ./qemu-system-x86_64 -qtest
unix:/tmp/qtest-106610.sock -qtest-log /dev/null -chardev
socket,path=/tmp/qtest-106610.qmp,id=char0 -mon chardev=char0,mode=control
-display none -audio none -run-with exit-with-parent=on -accel
kvm,dirty-ring-size=4096 -accel tcg -machine pc-q35-11.0, -name
target,debug-threads=on -machine memory-backend=mig.mem -object
memory-backend-ram,id=mig.mem,size=150M,share=off -serial
file:/tmp/migration-test-8B95M3/dest_serial -incoming
unix:/tmp/migration-test-8B95M3/migsocket -drive
if=none,id=d0,file=/tmp/migration-test-8B95M3/bootsect,format=raw -device
ide-hd,drive=d0,secs=1,cyls=1,heads=1 2>/dev/null -accel qtest
../../devel/qemu/tests/qtest/libqtest.c:201: kill_qemu() tried to terminate
QEMU process but encountered exit status 1 (expected 0)
Aborted (core dumped)
Could you please try whether you could reproduce that crash?
Thomas
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PULL 05/10] tests/qtest/migration: Force exit-on-error=false
2026-03-26 9:02 ` Thomas Huth
@ 2026-03-26 13:28 ` Fabiano Rosas
0 siblings, 0 replies; 16+ messages in thread
From: Fabiano Rosas @ 2026-03-26 13:28 UTC (permalink / raw)
To: Thomas Huth, qemu-devel; +Cc: Peter Xu, Prasad Pandit
Thomas Huth <thuth@redhat.com> writes:
> On 17/03/2026 19.23, Fabiano Rosas wrote:
>> Some tests can cause QEMU to exit(1) too early while the incoming
>> coroutine has not yielded for a first time yet. This trips ASAN
>> because resources related to dispatching the incoming process will
>> still be allocated in the io/channel.c layer without a
>> straight-forward way for the migration code to clean them up.
>>
>> As an example of one such issue, the UUID validation happens early
>> enough that the temporary socket from qio_net_listener_channel_func()
>> still has an elevated refcount. If it fails, the listener dispatch
>> code never gets to free the resource:
>>
>> Direct leak of 400 byte(s) in 1 object(s) allocated from:
>> #0 0x55e668890a07 in malloc asan_malloc_linux.cpp:68:3
>> #1 0x7f3c7e2b6648 in g_malloc ../glib/gmem.c:130
>> #2 0x55e66a8ef05f in object_new_with_type ../qom/object.c:767:15
>> #3 0x55e66a8ef178 in object_new ../qom/object.c:789:12
>> #4 0x55e66a93bcc6 in qio_channel_socket_new ../io/channel-socket.c:70:31
>> #5 0x55e66a93f34f in qio_channel_socket_accept ../io/channel-socket.c:401:12
>> #6 0x55e66a96752a in qio_net_listener_channel_func ../io/net-listener.c:64:12
>> #7 0x55e66a94bdac in qio_channel_fd_source_dispatch ../io/channel-watch.c:84:12
>> #8 0x7f3c7e2adf4b in g_main_dispatch ../glib/gmain.c:3476
>> #9 0x7f3c7e2adf4b in g_main_context_dispatch_unlocked ../glib/gmain.c:4284
>> #10 0x7f3c7e2b00c8 in g_main_context_dispatch ../glib/gmain.c:4272
>>
>> The exit(1) also requires some tests to setup qtest to expect a return
>> code of 1 from the QEMU process. Although we can check migration
>> status changes to be fairly certain where the failure happened, there
>> is always the possibility of QEMU exiting for another reason and the
>> test passing. This happens frequently with sanitizers enabled, but
>> also risks masking issues in the regular build.
>>
>> Stop allowing the incoming migration to exit and instead require the
>> tests to wait for the FAILED state and end QEMU gracefully with
>> qtest_quit.
>>
>> In practice this means setting exit-on-error=false for every incoming
>> migration, changing MIG_TEST_FAIL_DEST_QUIT_ERR to MIG_TEST_FAIL and
>> waiting for a change of state where necessary.
>>
>> With this, the MIG_TEST_FAIL_DEST_QUIT_ERR error result is now unused,
>> remove it.
>>
>> The affected tests are:
>> validate_uuid_error
>> multifd_tcp_cancel
>> dirty_limit
>> precopy_unix_tls_x509_default_host
>> precopy_tcp_tls_no_hostname
>> tcp_tls_x509_mismatch_host
>> dbus_vmstate_missing_src
>> dbus_vmstate_missing_dst
>>
>> Also add a comment to QEMU source explaining that the incoming
>> coroutine might block for a while until it yields as this is the
>> actual root cause of the issue.
>>
>> Reviewed-by: Peter Xu <peterx@redhat.com>
>> Reviewed-by: Prasad Pandit <pjp@fedoraproject.org>
>> Link: https://lore.kernel.org/qemu-devel/20260311213418.16951-6-farosas@suse.de
>> [assert that key doesn't already exists]
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>> migration/migration.c | 5 +++++
>> tests/qtest/dbus-vmstate-test.c | 5 +++--
>> tests/qtest/migration/framework.c | 5 +----
>> tests/qtest/migration/framework.h | 2 --
>> tests/qtest/migration/migration-qmp.c | 7 +++++++
>> tests/qtest/migration/misc-tests.c | 4 ++--
>> tests/qtest/migration/precopy-tests.c | 12 +++++-------
>> tests/qtest/migration/tls-tests.c | 14 ++++++++------
>> 8 files changed, 31 insertions(+), 23 deletions(-)
>
> Hi Fabiano,
>
> this patch now triggers a failure in the qtests when I'm running these in
> "SPEED=thorough" mode:
>
> MESON_TEST_ITERATION=1 MALLOC_PERTURB_=120
> ASAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1 G_TEST_SLOW=1
> PYTHON=/home/thuth/tmp/qemu-build/pyvenv/bin/python3 RUST_BACKTRACE=1
> QTEST_QEMU_IMG=./qemu-img
> G_TEST_DBUS_DAEMON=/home/thuth/devel/qemu/tests/dbus-vmstate-daemon.sh
> QTEST_QEMU_STORAGE_DAEMON_BINARY=./storage-daemon/qemu-storage-daemon
> UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1
> MSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1
> QTEST_QEMU_BINARY=./qemu-system-x86_64
> /home/thuth/tmp/qemu-build/tests/qtest/migration-test --tap -k --full
>
> TAP version 14
> # random seed: R02Sb882c8142734dce2265e65214fd2b060
> # starting QEMU: exec ./qemu-system-x86_64 -qtest
> unix:/tmp/qtest-106610.sock -qtest-log /dev/null -chardev
> socket,path=/tmp/qtest-106610.qmp,id=char0 -mon chardev=char0,mode=control
> -display none -audio none -run-with exit-with-parent=on -machine none -accel
> qtest
> # Skipping test: userfaultfd not available
> 1..80
> # Start of x86_64 tests
> # Running /x86_64/dirty_limit
> # Using machine type: pc-q35-11.0
> # starting QEMU: exec ./qemu-system-x86_64 -qtest
> unix:/tmp/qtest-106610.sock -qtest-log /dev/null -chardev
> socket,path=/tmp/qtest-106610.qmp,id=char0 -mon chardev=char0,mode=control
> -display none -audio none -run-with exit-with-parent=on -accel
> kvm,dirty-ring-size=4096 -accel tcg -machine pc-q35-11.0, -name
> source,debug-threads=on -machine memory-backend=mig.mem -object
> memory-backend-ram,id=mig.mem,size=150M,share=off -serial
> file:/tmp/migration-test-8B95M3/src_serial -drive
> if=none,id=d0,file=/tmp/migration-test-8B95M3/bootsect,format=raw -device
> ide-hd,drive=d0,secs=1,cyls=1,heads=1 2>/dev/null -accel qtest
> # starting QEMU: exec ./qemu-system-x86_64 -qtest
> unix:/tmp/qtest-106610.sock -qtest-log /dev/null -chardev
> socket,path=/tmp/qtest-106610.qmp,id=char0 -mon chardev=char0,mode=control
> -display none -audio none -run-with exit-with-parent=on -accel
> kvm,dirty-ring-size=4096 -accel tcg -machine pc-q35-11.0, -name
> target,debug-threads=on -machine memory-backend=mig.mem -object
> memory-backend-ram,id=mig.mem,size=150M,share=off -serial
> file:/tmp/migration-test-8B95M3/dest_serial -incoming
> unix:/tmp/migration-test-8B95M3/migsocket -drive
> if=none,id=d0,file=/tmp/migration-test-8B95M3/bootsect,format=raw -device
> ide-hd,drive=d0,secs=1,cyls=1,heads=1 2>/dev/null -accel qtest
> ../../devel/qemu/tests/qtest/libqtest.c:201: kill_qemu() tried to terminate
> QEMU process but encountered exit status 1 (expected 0)
> Aborted (core dumped)
>
> Could you please try whether you could reproduce that crash?
>
> Thomas
Argh, too many dirty this, dirty that. I'll send a patch. Thanks!
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PULL 06/10] migration: assert that the same migration handler is not being added twice
2026-03-17 18:23 [PULL 00/10] Migration/Qtest patches for 2026-03-17 Fabiano Rosas
` (4 preceding siblings ...)
2026-03-17 18:23 ` [PULL 05/10] tests/qtest/migration: Force exit-on-error=false Fabiano Rosas
@ 2026-03-17 18:23 ` Fabiano Rosas
2026-03-17 18:23 ` [PULL 07/10] migration/options: Fix leaks in StrOrNull qdev accessors Fabiano Rosas
` (4 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Fabiano Rosas @ 2026-03-17 18:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Xu, Ani Sinha, Markus Armbruster, Prasad Pandit
From: Ani Sinha <anisinha@redhat.com>
Currently the code that adds a migration blocker does not check if the same
blocker already exists. Assert that the migration handler being added has
not been added already.
CC: Markus Armbruster <armbru@redhat.com>
CC: Peter Xu <peterx@redhat.com>
CC: Prasad Pandit <pjp@fedoraproject.org>
Reviewed-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Ani Sinha <anisinha@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260311070114.164434-1-anisinha@redhat.com
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
migration/migration.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/migration/migration.c b/migration/migration.c
index c77832f851..7feeba804f 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -1699,6 +1699,8 @@ static int add_blockers(Error **reasonp, unsigned modes, Error **errp)
{
for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) {
if (modes & BIT(mode)) {
+ assert(g_slist_index(migration_blockers[mode],
+ *reasonp) == -1);
migration_blockers[mode] = g_slist_prepend(migration_blockers[mode],
*reasonp);
}
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PULL 07/10] migration/options: Fix leaks in StrOrNull qdev accessors
2026-03-17 18:23 [PULL 00/10] Migration/Qtest patches for 2026-03-17 Fabiano Rosas
` (5 preceding siblings ...)
2026-03-17 18:23 ` [PULL 06/10] migration: assert that the same migration handler is not being added twice Fabiano Rosas
@ 2026-03-17 18:23 ` Fabiano Rosas
2026-03-17 18:23 ` [PULL 08/10] migration: fix implicit integer division in migration_update_counters Fabiano Rosas
` (3 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Fabiano Rosas @ 2026-03-17 18:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Xu, Markus Armbruster, Peter Maydell, Prasad Pandit
Fix a couple of possible leaks detected by Coverity. Both are
currently harmless. This code is only used for the very specific
purpose of maintaining compatibility of a few migration options which
can be set via QEMU command line (-global migration.tls-*). The
command line interface is not supported and only used during
development and testing.
1) The setter function set_StrOrNull() is invoked whenever the -global
migration.tls-* command line options are set. The way it could leak is
that the temporary "StrOrNull *str_or_null" object is allocated before
calling the visitor, which could fail and cause an early return of the
function, leaving *ptr unset and str_or_null leaking.
2) The getter function get_StrOrNull() is unreachable code. It's only
there to provide a complete implementation of the property. Still, the
way it could leak is that the temporary "StrOrNull *str_or_null" might
be allocated and is simply never returned to the caller nor freed.
Fix the possible leaks:
1) at set_StrOrNull(): change the allocation of str_or_null to happen
only after the visit call has returned successfully.
2) at get_StrOrNull(): assert that the object is non-NULL, there is no
need for a temporary object.
The reason it should be non-NULL is that the property is initialized
by the default setter of the qdev property. The initialization is
unlikely to fail because the call to the setter is setup by qdev,
which has boilerplate ensuring the to-be-set object is allocated and
of the correct type. Moreover, passing NULL via command line to
-global migration.tls-* is not possible.
A programming error could result in an invalid call to the setter,
which would leave the object NULL and cause a crash in the getter, but
that's not a worthwhile scenario to protect against given the low
probability of this code being even reached.
While here, update the comment about why there's no QNULL in this
StrOrNull property to be more clear.
Fixes: CID 1643919
Fixes: CID 1643920
Cc: Markus Armbruster <armbru@redhat.com>
Reported-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Prasad Pandit <pjp@fedoraproject.org>
Link: https://lore.kernel.org/qemu-devel/20260312204619.1969-1-farosas@suse.de
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
migration/options.c | 35 +++++++++++++++++++++--------------
1 file changed, 21 insertions(+), 14 deletions(-)
diff --git a/migration/options.c b/migration/options.c
index f33b297929..658c578191 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -220,14 +220,12 @@ static void get_StrOrNull(Object *obj, Visitor *v, const char *name,
StrOrNull **ptr = object_field_prop_ptr(obj, prop);
StrOrNull *str_or_null = *ptr;
- if (!str_or_null) {
- str_or_null = g_new0(StrOrNull, 1);
- str_or_null->type = QTYPE_QSTRING;
- str_or_null->u.s = g_strdup("");
- } else {
- /* the setter doesn't allow QNULL */
- assert(str_or_null->type != QTYPE_QNULL);
- }
+ /*
+ * The property should never be NULL because it's part of
+ * s->parameters and a default value is always set by qdev. It
+ * should also never be QNULL as the setter doesn't allow it.
+ */
+ assert(str_or_null && str_or_null->type != QTYPE_QNULL);
visit_type_str(v, name, &str_or_null->u.s, errp);
}
@@ -236,16 +234,25 @@ static void set_StrOrNull(Object *obj, Visitor *v, const char *name,
{
const Property *prop = opaque;
StrOrNull **ptr = object_field_prop_ptr(obj, prop);
- StrOrNull *str_or_null = g_new0(StrOrNull, 1);
+ StrOrNull *str_or_null;
+ char *str;
+
+ if (!visit_type_str(v, name, &str, errp)) {
+ return;
+ }
/*
- * Only str to keep compatibility, QNULL was never used via
- * command line.
+ * This property only applies to the command line usage of
+ * migration's TLS options (-global migration.tls-*) where the
+ * NULL value cannot be provided as input (only strings are
+ * allowed). Therefore, this StrOrNull implementation never
+ * produces a QNULL value to avoid ever returning values outside
+ * the range of what was previously handled by consumers of the
+ * TLS options.
*/
+ str_or_null = g_new0(StrOrNull, 1);
str_or_null->type = QTYPE_QSTRING;
- if (!visit_type_str(v, name, &str_or_null->u.s, errp)) {
- return;
- }
+ str_or_null->u.s = str;
qapi_free_StrOrNull(*ptr);
*ptr = str_or_null;
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PULL 08/10] migration: fix implicit integer division in migration_update_counters
2026-03-17 18:23 [PULL 00/10] Migration/Qtest patches for 2026-03-17 Fabiano Rosas
` (6 preceding siblings ...)
2026-03-17 18:23 ` [PULL 07/10] migration/options: Fix leaks in StrOrNull qdev accessors Fabiano Rosas
@ 2026-03-17 18:23 ` Fabiano Rosas
2026-03-17 18:23 ` [PULL 09/10] tests/qtest: Don't dup machine name in qtest_cb_for_every_machine callbacks Fabiano Rosas
` (2 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Fabiano Rosas @ 2026-03-17 18:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Xu, Aadeshveer Singh
From: Aadeshveer Singh <aadeshveer07@gmail.com>
switchover_bw is a uint64_t, so switchover_bw / 1000 results in an
integer division. This value is then assigned to expected_bw_per_ms
which is of type double. This results in losing precision and is type
unsafe. Adding explicit cast ensures floating-point division.
Signed-off-by: Aadeshveer Singh <aadeshveer07@gmail.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260316134509.157964-1-aadeshveer07@gmail.com
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
migration/migration.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/migration/migration.c b/migration/migration.c
index 7feeba804f..5c9aaa6e58 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -3141,7 +3141,7 @@ static void migration_update_counters(MigrationState *s,
* If the user specified a switchover bandwidth, let's trust the
* user so that can be more accurate than what we estimated.
*/
- expected_bw_per_ms = switchover_bw / 1000;
+ expected_bw_per_ms = (double)switchover_bw / 1000;
} else {
/* If the user doesn't specify bandwidth, we use the estimated */
expected_bw_per_ms = bandwidth;
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PULL 09/10] tests/qtest: Don't dup machine name in qtest_cb_for_every_machine callbacks
2026-03-17 18:23 [PULL 00/10] Migration/Qtest patches for 2026-03-17 Fabiano Rosas
` (7 preceding siblings ...)
2026-03-17 18:23 ` [PULL 08/10] migration: fix implicit integer division in migration_update_counters Fabiano Rosas
@ 2026-03-17 18:23 ` Fabiano Rosas
2026-03-17 18:23 ` [PULL 10/10] tests/qtest/test-hmp: Free machine options Fabiano Rosas
2026-03-18 13:26 ` [PULL 00/10] Migration/Qtest patches for 2026-03-17 Peter Maydell
10 siblings, 0 replies; 16+ messages in thread
From: Fabiano Rosas @ 2026-03-17 18:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Xu, Philippe Mathieu-Daudé
The qtest_get_machines function caches the list of machines in a
static variable. Dup'ing the machine->name string only serves to leak
that memory when a single test is executed.
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Link: https://lore.kernel.org/qemu-devel/20260313182957.28432-2-farosas@suse.de
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/cpu-plug-test.c | 11 +++++------
tests/qtest/qom-test.c | 3 +--
tests/qtest/test-hmp.c | 3 +--
3 files changed, 7 insertions(+), 10 deletions(-)
diff --git a/tests/qtest/cpu-plug-test.c b/tests/qtest/cpu-plug-test.c
index 0aa4ccc5b6..e68f39cf56 100644
--- a/tests/qtest/cpu-plug-test.c
+++ b/tests/qtest/cpu-plug-test.c
@@ -14,7 +14,7 @@
#include "qobject/qlist.h"
struct PlugTestData {
- char *machine;
+ const char *machine;
const char *cpu_model;
char *device_model;
unsigned sockets;
@@ -73,7 +73,6 @@ static void test_data_free(gpointer data)
{
PlugTestData *pc = data;
- g_free(pc->machine);
g_free(pc->device_model);
g_free(pc);
}
@@ -87,7 +86,7 @@ static void add_pc_test_case(const char *mname)
return;
}
data = g_new(PlugTestData, 1);
- data->machine = g_strdup(mname);
+ data->machine = mname;
data->cpu_model = "Haswell"; /* 1.3+ theoretically */
data->device_model = g_strdup_printf("%s-%s-cpu", data->cpu_model,
qtest_get_arch());
@@ -114,7 +113,7 @@ static void add_pseries_test_case(const char *mname)
return;
}
data = g_new(PlugTestData, 1);
- data->machine = g_strdup(mname);
+ data->machine = mname;
data->cpu_model = "power8_v2.0";
data->device_model = g_strdup("power8_v2.0-spapr-cpu-core");
data->sockets = 2;
@@ -140,7 +139,7 @@ static void add_s390x_test_case(const char *mname)
}
data = g_new(PlugTestData, 1);
- data->machine = g_strdup(mname);
+ data->machine = mname;
data->cpu_model = "qemu";
data->device_model = g_strdup("qemu-s390x-cpu");
data->sockets = 1;
@@ -162,7 +161,7 @@ static void add_loongarch_test_case(const char *mname)
PlugTestData *data;
data = g_new(PlugTestData, 1);
- data->machine = g_strdup(mname);
+ data->machine = mname;
data->cpu_model = "la464";
data->device_model = g_strdup("la464-loongarch-cpu");
data->sockets = 1;
diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c
index 2da9918e16..6421f2d9d9 100644
--- a/tests/qtest/qom-test.c
+++ b/tests/qtest/qom-test.c
@@ -216,7 +216,6 @@ static void test_machine(gconstpointer data)
test_list_get_value(qts);
qtest_quit(qts);
- g_free((void *)machine);
}
static void add_machine_test_case(const char *mname)
@@ -224,7 +223,7 @@ static void add_machine_test_case(const char *mname)
char *path;
path = g_strdup_printf("qom/%s", mname);
- qtest_add_data_func(path, g_strdup(mname), test_machine);
+ qtest_add_data_func(path, mname, test_machine);
g_free(path);
}
diff --git a/tests/qtest/test-hmp.c b/tests/qtest/test-hmp.c
index 1b2e07522f..60f742e83f 100644
--- a/tests/qtest/test-hmp.c
+++ b/tests/qtest/test-hmp.c
@@ -135,7 +135,6 @@ static void test_machine(gconstpointer data)
qtest_quit(qts);
g_free(args);
- g_free((void *)data);
}
static void add_machine_test_case(const char *mname)
@@ -143,7 +142,7 @@ static void add_machine_test_case(const char *mname)
char *path;
path = g_strdup_printf("hmp/%s", mname);
- qtest_add_data_func(path, g_strdup(mname), test_machine);
+ qtest_add_data_func(path, mname, test_machine);
g_free(path);
}
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PULL 10/10] tests/qtest/test-hmp: Free machine options
2026-03-17 18:23 [PULL 00/10] Migration/Qtest patches for 2026-03-17 Fabiano Rosas
` (8 preceding siblings ...)
2026-03-17 18:23 ` [PULL 09/10] tests/qtest: Don't dup machine name in qtest_cb_for_every_machine callbacks Fabiano Rosas
@ 2026-03-17 18:23 ` Fabiano Rosas
2026-03-18 13:26 ` [PULL 00/10] Migration/Qtest patches for 2026-03-17 Peter Maydell
10 siblings, 0 replies; 16+ messages in thread
From: Fabiano Rosas @ 2026-03-17 18:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Xu, Philippe Mathieu-Daudé
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Link: https://lore.kernel.org/qemu-devel/20260313182957.28432-3-farosas@suse.de
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
tests/qtest/test-hmp.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tests/qtest/test-hmp.c b/tests/qtest/test-hmp.c
index 60f742e83f..a523dbb446 100644
--- a/tests/qtest/test-hmp.c
+++ b/tests/qtest/test-hmp.c
@@ -149,6 +149,7 @@ static void add_machine_test_case(const char *mname)
int main(int argc, char **argv)
{
char *v_env = getenv("V");
+ g_autofree char *machine_opts = g_strdup("none -m 2");
if (v_env && atoi(v_env) >= 2) {
verbose = true;
@@ -159,7 +160,7 @@ int main(int argc, char **argv)
qtest_cb_for_every_machine(add_machine_test_case, g_test_quick());
/* as none machine has no memory by default, add a test case with memory */
- qtest_add_data_func("hmp/none+2MB", g_strdup("none -m 2"), test_machine);
+ qtest_add_data_func("hmp/none+2MB", machine_opts, test_machine);
return g_test_run();
}
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PULL 00/10] Migration/Qtest patches for 2026-03-17
2026-03-17 18:23 [PULL 00/10] Migration/Qtest patches for 2026-03-17 Fabiano Rosas
` (9 preceding siblings ...)
2026-03-17 18:23 ` [PULL 10/10] tests/qtest/test-hmp: Free machine options Fabiano Rosas
@ 2026-03-18 13:26 ` Peter Maydell
10 siblings, 0 replies; 16+ messages in thread
From: Peter Maydell @ 2026-03-18 13:26 UTC (permalink / raw)
To: Fabiano Rosas; +Cc: qemu-devel, Peter Xu
On Tue, 17 Mar 2026 at 18:23, Fabiano Rosas <farosas@suse.de> wrote:
>
> The following changes since commit 6667668e0cf369e47aa9dd0bae40989896e9ed9f:
>
> Merge tag 'pr-plugins-20260316' of https://gitlab.com/pbo-linaro/qemu into staging (2026-03-17 10:20:30 +0000)
>
> are available in the Git repository at:
>
> https://gitlab.com/farosas/qemu.git tags/migration-20260317-pull-request
>
> for you to fetch changes up to 8f627bf1be077382f8ebf2cbda5cfb7e94006c54:
>
> tests/qtest/test-hmp: Free machine options (2026-03-17 14:58:09 -0300)
>
> ----------------------------------------------------------------
> Migration/Qtest pull request
>
> Various fixes
Applied, thanks.
Please update the changelog at https://wiki.qemu.org/ChangeLog/11.0
for any user-visible changes.
-- PMM
^ permalink raw reply [flat|nested] 16+ messages in thread