From nobody Thu Nov 12 15:08:33 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) client-ip=38.145.34.151; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1793041912; cv=none; d=zohomail.com; s=zohoarc; b=bvCOf+cUIVprtFVgdzLlDNxXzpCHzx1bE0FtspJBoA0+jHRfOG6vJoPhWTZXVlGJQBgPTQr+/vnsYancFAgIkIXKjYzn8EzEb1eKSl+JHC4ZoF7cfiMOMaL1juy1KZgOrkdtKUUwh66ORUeXBB7QNIp9UCOHPw9KO39+2fcLYJk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1793041912; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:Subject:Subject:To:To:Message-Id; bh=VfqblLyZPAoD1SnDdgaGohydGqcjjhWZz0ZyU3ixheY=; b=KzJqpFQ+bwsUFrlQk/uRcsG3sSSzwccDNu9jLIMYpuiFCtXIX2b6Cgp19/w71eznQc7OISxqn+TIAVhL2Q0wZV/xCbhsZG/lvU+kLdSYsD6miKui3G+sKXBKfHjfBpnRMNYoyBIoF1aUz/0owPFHQZxSsLSLE1C3sqiy/II5JTQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [38.145.34.151]) by mx.zohomail.com with SMTPS id 1793041912042155.19091031282096; Mon, 26 Oct 2026 12:11:52 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 993) id D9DB341B44; Mon, 26 Oct 2026 15:11:50 -0400 (EDT) Received: from [172.19.199.19] (unknown [10.16.107.18]) by lists.libvirt.org (Postfix) with ESMTP id 687E741C4B; Mon, 26 Oct 2026 15:08:34 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 993) id 2F51C4188C; Mon, 26 Oct 2026 11:05:18 -0400 (EDT) Received: from mail-wr1-x42c.google.com (mail-wr1-x42c.google.com [IPv6:2a00:1450:4864:20::42c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id E260F3F289 for ; Mon, 26 Oct 2026 11:05:16 -0400 (EDT) Received: by mail-wr1-x42c.google.com with SMTP id ffacd0b85a97d-48df8fe8513so2926594f8f.0 for ; Mon, 26 Oct 2026 08:05:16 -0700 (PDT) Received: from athena.sw.ru ([2a06:5b06:b600:300:1a6:7f7f:f573:ba9e]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-48e3eb8f387sm16597293f8f.4.2026.10.26.08.05.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2026 08:05:14 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-2.7 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=4.0.1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=openvz.org; s=google; t=1793027116; x=1793631916; darn=lists.libvirt.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=VfqblLyZPAoD1SnDdgaGohydGqcjjhWZz0ZyU3ixheY=; b=TTFlj5iwL98obngR3dJMA2LKc/OnwnS0gQUleUxYutBK0sQYsIbGzsR9ym+zh8M7Kj mtAftrZOXe20zSEFEHZUrencNmd4g9K34S/VQ8P67NTfaF+ZkrDuadyE7SdXUEws1Mrq XqFUsGSSigbMkyrj+Bi4IVgx+n1OUPIZX5k7UJlwaWuJ9Ufvw6QdLalwDXiFBN+uuHll clff9tHA/cB6Z6S0Nlr97tsjSUmPo6QImhUM1e8vhUT06OH1YAKPQRpWfoZvtFHzyrPK 3eH9unLNhijEb2skiGN4ascgP8SCpd2cm3dTgcFFqhr4bYY3YID4DLJl10RGvmHB/Vwi Aq3A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793027116; x=1793631916; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=VfqblLyZPAoD1SnDdgaGohydGqcjjhWZz0ZyU3ixheY=; b=2zCP8CFAzEIpN9IGTUXFQY7DR2WGV+FUi0mYLM5QB2Vb/yDRp86HHf7uwJT0GV3Pf6 h4ESF8o1h5Ym6CH5um9c40QrTCAfTn1NEXqjUtZu939WvSQq+fRzLBDSPKnviN5yqYzA LuWDmmqoErT7UWAt21lX+1/6ENkmfzKiPO3MmX9mpOl89aG4Kjboe+rN8QeZbyPgBMKq yUtnA5yccSO1l21WsKx/Ixz4lEAn4CSHSZ5F4wORRAAa6VoGD+bpjoCW52AjoS0ERIPh tHz/LAqpU22fEQqp4KuhybC5Gf+zZdXYWfk4crK32PmdjFrRG7QNUnhzp6+HFZ7M/ukw /g2w== X-Gm-Message-State: AFq9FYI0FAHWxMps8aSpG1XL4EmtbwqyjimbU1g6ktLqV/TFipaq83fs IUilcHs0z4V0rIHm4OEPr/sf1P+n8Y1b55/GzIHuK9PTLkdNVTxVDp50+P5jW9rwz24rKYF7wNu Wf3lC X-Gm-Gg: AYBFou36uGEGO/WVLEH89gMuz5XmuE0xOYqYapa1XJUJie6B68l6Ql+mkaoYkS6VjLw WZpUr8yV/ASqZTK/fksyNXV/7WS9QN5WJVww5Tjfu+auf5bfyRKN6McPS5HujuI+CagZGK+9Uun Z3z5i0iQsAmY+70r4ti4eFu88tFJZRC3oIWBKG2sN9fzRxPw7UtSYwL+H++B7XFlmKpJrlzNA/t fLFK5RUvEQ7cAYU4Dg54S/ZX+4l5XIfVlp0brP5AJ14bUPwWn3BlYWMy7gO/n5MacNKktBIdr+P m/gFZXSM/cH3ihIHMF6iTLWDfxSuvNEPG+IgrZw14wiNIednsFDi+Oojwpc8YcJxfZjWGO3N21O /Tq4hleHOxqJUrma/xwCox1qq64x6g4C9XKQjv9u7u+Z1vgTC+7uQOmjn3p+QuJDlKezREmiaNG sKM9rOFoXzHzemidxj0FMiElOXn2F6TILyHtQNERGBAD8G2O5WCKcwcm7XWSJ22Yugm3+lmadqe Ft4ozAj0wPEULXNCJq8rmU7se9I+l2vlBedwohX8CUC3Ll0iMo= X-Received: by 2002:a05:6000:25fc:b0:48a:fb1c:c2a1 with SMTP id ffacd0b85a97d-48e3eb946b0mr12589509f8f.47.1793027115544; Mon, 26 Oct 2026 08:05:15 -0700 (PDT) To: devel@lists.libvirt.org Subject: [PATCH] conf: Fix race that stops all event delivery until daemon restart Date: Mon, 26 Oct 2026 16:05:11 +0100 Message-ID: <20261026150511.453362-1-den@openvz.org> X-Mailer: git-send-email 2.53.0 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-MailFrom: den@openvz.org X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation Message-ID-Hash: OVZL5CV4NGGZD2QTNSK5PEZMZBC7WV5S X-Message-ID-Hash: OVZL5CV4NGGZD2QTNSK5PEZMZBC7WV5S X-Mailman-Approved-At: Mon, 26 Oct 2026 19:08:20 +0000 CC: den@openvz.org X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: "Denis V. Lunev via Devel" Reply-To: "Denis V. Lunev" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1793041913666158500 Content-Type: text/plain; charset="utf-8" From: Denis V. Lunev virObjectEventStateQueueRemote() checks state->timer before taking the state lock. If the last callback is removed in between, virObjectEventStateCleanupTimer() removes the timer and clears the queue, and the event is then queued with no timer to flush it. A callback registered later gets a new timer, but that timer is armed only when a push takes the queue from empty to one event, which never happens again. Neither the stale event nor anything queued after it is dispatched, and no client receives events until the daemon restarts. A client holding the last callback can trigger this by exiting: its connection drops the callbacks while the hypervisor reports the side effects of the exit, e.g. VNC_DISCONNECTED for a VNC session it had open. Check the timer under the lock. The test removes the last callback while another thread is blocked queueing an event, using a freecb that runs under the state lock, and then checks that a newly registered callback still receives events. Signed-off-by: Denis V. Lunev Reviewed-by: Michal Privoznik --- Seen on a host where a management daemon was the only event consumer: after it was restarted with a VNC session open, libvirtd delivered no domain events to anyone, a fresh 'virsh event --all' included, until libvirtd itself was restarted. Restarting that daemon in a loop with a VM running reproduced it in the 11th round; gdb on libvirtd showed timer =3D=3D -1 with queue->count =3D=3D 1 right after the daemon exited. The new test failed 5 of 5 runs without the fix and passed 20 of 20 with it. With the fix the event is simply dropped, so the test does not depend on thread timing. The whole test suite, syntax-check included, passes. object_event.c has had the unlocked check since it was split out of domain_event.c in v1.2.1, and domain_event.c had it before that. src/conf/object_event.c | 5 ++- tests/objecteventtest.c | 92 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/conf/object_event.c b/src/conf/object_event.c index 5fb96cb7aa..8b0669d660 100644 --- a/src/conf/object_event.c +++ b/src/conf/object_event.c @@ -727,13 +727,14 @@ virObjectEventStateQueueRemote(virObjectEventState *s= tate, if (!event) return; =20 + virObjectLock(state); + if (state->timer < 0) { + virObjectUnlock(state); virObjectUnref(event); return; } =20 - virObjectLock(state); - event->remoteID =3D remoteID; if (virObjectEventQueuePush(state->queue, event) < 0) { VIR_DEBUG("Error adding event to queue"); diff --git a/tests/objecteventtest.c b/tests/objecteventtest.c index 7110f54139..6b43d3c44d 100644 --- a/tests/objecteventtest.c +++ b/tests/objecteventtest.c @@ -22,6 +22,7 @@ #include =20 #include "testutils.h" +#include "virthread.h" =20 #define VIR_FROM_THIS VIR_FROM_NONE =20 @@ -444,6 +445,94 @@ testDomainStartStopEvent(const void *data) return ret; } =20 +typedef struct { + lifecycleEventCounter counter; + virConnectPtr conn; + virThread thread; + bool started; +} queueRaceData; + +static void +queueRaceDefine(void *opaque) +{ + virDomainPtr dom =3D virDomainDefineXML(opaque, domainDef); + + if (dom) + virDomainFree(dom); +} + +/* Runs under the event state lock while the last callback is removed */ +static void +queueRaceFree(void *opaque) +{ + queueRaceData *race =3D opaque; + + if (virThreadCreate(&race->thread, true, queueRaceDefine, race->conn) = < 0) + return; + race->started =3D true; + g_usleep(100 * 1000); +} + +static void +queueRaceWatchdog(int timer G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ +} + +static int +testDomainEventQueueRace(const void *data) +{ + const objecteventTest *test =3D data; + queueRaceData race =3D { .conn =3D test->conn }; + lifecycleEventCounter counter =3D { 0 }; + int eventId =3D VIR_DOMAIN_EVENT_ID_LIFECYCLE; + virDomainPtr dom =3D NULL; + int timer =3D -1; + int id; + int ret =3D -1; + + id =3D virConnectDomainEventRegisterAny(test->conn, NULL, eventId, + VIR_DOMAIN_EVENT_CALLBACK(&domainLifecycleCb), + &race, queueRaceFree); + if (id < 0) + return -1; + + virConnectDomainEventDeregisterAny(test->conn, id); + if (!race.started) + return -1; + virThreadJoin(&race.thread); + + id =3D virConnectDomainEventRegisterAny(test->conn, NULL, eventId, + VIR_DOMAIN_EVENT_CALLBACK(&domainLifecycleCb), + &counter, NULL); + if (id < 0) + return -1; + + if (!(dom =3D virDomainLookupByName(test->conn, "test-domain")) || + virDomainUndefine(dom) < 0) + goto cleanup; + + /* A stuck queue never fires, so do not wait for it forever */ + if ((timer =3D virEventAddTimeout(1000, queueRaceWatchdog, NULL, NULL)= ) < 0) + goto cleanup; + + if (virEventRunDefaultImpl() < 0) + goto cleanup; + + if (counter.undefineEvents !=3D 1 || counter.unexpectedEvents > 0) + goto cleanup; + + ret =3D 0; + cleanup: + if (timer >=3D 0) + virEventRemoveTimeout(timer); + virConnectDomainEventDeregisterAny(test->conn, id); + if (dom) + virDomainFree(dom); + + return ret; +} + static int testNetworkCreateXML(const void *data) { @@ -804,6 +893,9 @@ mymain(void) ret =3D EXIT_FAILURE; if (virTestRun("Domain start stop events", testDomainStartStopEvent, &= test) < 0) ret =3D EXIT_FAILURE; + if (virTestRun("Domain event queued while last callback is removed", + testDomainEventQueueRace, &test) < 0) + ret =3D EXIT_FAILURE; =20 /* Network event tests */ /* Tests requiring the test network not to be set up */ base-commit: 4d514f852c44f05f34e024438a7be004822c059d --=20 2.53.0