From 6b42792cfee124a742999e698e348e99b382ba16 Mon Sep 17 00:00:00 2001 From: c00207022 Date: Thu, 2 Mar 2017 12:07:27 +0800 Subject: [PATCH] libvirt-remote:fix use-after-free when sending event message [Changelog]:If there is a process with a client which registers event callbacks, and it calls libvirt's API which uses the same virConnectPtr in that callback function. When this process exit abnormally lead to client disconnect, there is a possibility that the libvirtd's main thread is use virServerClient to send event just after the virServerClient been freed by job thread. Following is backtrace: #0 0x00007fda223d66d8 in virClassIsDerivedFrom (klass=0xdeadbeef, parent=0x7fda24c81b40) at util/virobject.c:169 #1 0x00007fda223d6a1e in virObjectIsClass (anyobj=anyobj@entry=0x7fd9e575b400, klass=) at util/virobject.c:365 #2 0x00007fda223d6a44 in virObjectLock (anyobj=0x7fd9e575b400) at util/virobject.c:317 #3 0x00007fda22507f71 in virNetServerClientSendMessage (client=client@entry=0x7fd9e575b400, msg=msg@entry=0x7fd9ec30de90) at rpc/virnetserverclient.c:1422 #4 0x00007fda230d714d in remoteDispatchObjectEventSend (client=0x7fd9e575b400, program=0x7fda24c844e0, procnr=348, proc=0x7fda2310e5e0 , data=0x7ffc3857fdb0) at remote.c:3803 #5 0x00007fda230dd71b in remoteRelayDomainEventTunable (conn=, dom=0x7fda27cd7660, params=0x7fda27f3aae0, nparams=1, opaque=0x7fd9e6c99e00) at remote.c:1033 #6 0x00007fda224484cb in virDomainEventDispatchDefaultFunc (conn=0x7fda27cd0120, event=0x7fda2736ea00, cb=0x7fda230dd610 , cbopaque=0x7fd9e6c99e00) at conf/domain_event.c:1910 #7 0x00007fda22446871 in virObjectEventStateDispatchCallbacks (callbacks=, callbacks=, event=0x7fda2736ea00, state=0x7fda24ca3960) at conf/object_event.c:722 #8 virObjectEventStateQueueDispatch (callbacks=0x7fda24c65800, queue=0x7ffc3857fe90, state=0x7fda24ca3960) at conf/object_event.c:736 #9 virObjectEventStateFlush (state=0x7fda24ca3960) at conf/object_event.c:814 #10 virObjectEventTimer (timer=, opaque=0x7fda24ca3960) at conf/object_event.c:560 #11 0x00007fda223ae8b9 in virEventPollDispatchTimeouts () at util/vireventpoll.c:458 #12 virEventPollRunOnce () at util/vireventpoll.c:654 #13 0x00007fda223ad1d2 in virEventRunDefaultImpl () at util/virevent.c:314 #14 0x00007fda225046cd in virNetDaemonRun (dmn=0x7fda24c775c0) at rpc/virnetdaemon.c:818 #15 0x00007fda230d6351 in main (argc=, argv=) at libvirtd.c:1623 Let's clean all event callback when client close. Signed-off-by: Caoxinhua --- daemon/remote.c | 76 +++++++++++++++++++++------------------------------------ 1 file changed, 28 insertions(+), 48 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 23c9de4..fb7cd50 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -101,6 +101,7 @@ static void make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNod static void make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr secret_src); static void make_nonnull_nwfilter(remote_nonnull_nwfilter *net_dst, virNWFilterPtr nwfilter_src); static void make_nonnull_domain_snapshot(remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src); +static void remoteClientCleanEventCallbacks(struct daemonClientPrivate *priv); static int remoteSerializeDomainDiskErrors(virDomainDiskErrorPtr errors, @@ -1483,24 +1484,15 @@ void remoteRelayConnectionClosedEvent(virConnectPtr conn ATTRIBUTE_UNUSED, int r &msg); } -/* - * You must hold lock for at least the client - * We don't free stuff here, merely disconnect the client's - * network socket & resources. - * We keep the libvirt connection open until any async - * jobs have finished, then clean it up elsewhere - */ -void remoteClientFreeFunc(void *data) +static void remoteClientCleanEventCallbacks(struct daemonClientPrivate *priv) { - struct daemonClientPrivate *priv = data; - /* Deregister event delivery callback */ - if (priv->conn) { - virIdentityPtr sysident = virIdentityGetSystem(); + if (priv && priv->conn) { size_t i; - + virIdentityPtr sysident = virIdentityGetSystem(); virIdentitySetCurrent(sysident); + virMutexLock(&priv->lock); for (i = 0; i < priv->ndomainEventCallbacks; i++) { int callbackID = priv->domainEventCallbacks[i]->callbackID; if (callbackID < 0) { @@ -1514,6 +1506,7 @@ void remoteClientFreeFunc(void *data) VIR_WARN("unexpected domain event deregister failure"); } VIR_FREE(priv->domainEventCallbacks); + priv->ndomainEventCallbacks = 0; for (i = 0; i < priv->nnetworkEventCallbacks; i++) { int callbackID = priv->networkEventCallbacks[i]->callbackID; @@ -1529,36 +1522,7 @@ void remoteClientFreeFunc(void *data) VIR_WARN("unexpected network event deregister failure"); } VIR_FREE(priv->networkEventCallbacks); - - for (i = 0; i < priv->nstorageEventCallbacks; i++) { - int callbackID = priv->storageEventCallbacks[i]->callbackID; - if (callbackID < 0) { - VIR_WARN("unexpected incomplete storage pool callback %zu", i); - continue; - } - VIR_DEBUG("Deregistering remote storage pool event relay %d", - callbackID); - priv->storageEventCallbacks[i]->callbackID = -1; - if (virConnectStoragePoolEventDeregisterAny(priv->conn, - callbackID) < 0) - VIR_WARN("unexpected storage pool event deregister failure"); - } - VIR_FREE(priv->storageEventCallbacks); - - for (i = 0; i < priv->nnodeDeviceEventCallbacks; i++) { - int callbackID = priv->nodeDeviceEventCallbacks[i]->callbackID; - if (callbackID < 0) { - VIR_WARN("unexpected incomplete node device callback %zu", i); - continue; - } - VIR_DEBUG("Deregistering remote node device event relay %d", - callbackID); - priv->nodeDeviceEventCallbacks[i]->callbackID = -1; - if (virConnectNodeDeviceEventDeregisterAny(priv->conn, - callbackID) < 0) - VIR_WARN("unexpected node device event deregister failure"); - } - VIR_FREE(priv->nodeDeviceEventCallbacks); + priv->nnetworkEventCallbacks = 0; for (i = 0; i < priv->nqemuEventCallbacks; i++) { int callbackID = priv->qemuEventCallbacks[i]->callbackID; @@ -1574,31 +1538,47 @@ void remoteClientFreeFunc(void *data) VIR_WARN("unexpected qemu monitor event deregister failure"); } VIR_FREE(priv->qemuEventCallbacks); + priv->nqemuEventCallbacks = 0; if (priv->closeRegistered) { if (virConnectUnregisterCloseCallback(priv->conn, remoteRelayConnectionClosedEvent) < 0) VIR_WARN("unexpected close callback event deregister failure"); } - - virConnectClose(priv->conn); - + virMutexUnlock(&priv->lock); virIdentitySetCurrent(NULL); virObjectUnref(sysident); } +} + + +/* + * You must hold lock for at least the client + * We don't free stuff here, merely disconnect the client's + * network socket & resources. + * We keep the libvirt connection open until any async + * jobs have finished, then clean it up elsewhere + */ +void remoteClientFreeFunc(void *data) +{ + struct daemonClientPrivate *priv = data; + if (!priv || !priv->conn) + return; + + remoteClientCleanEventCallbacks(priv); + virConnectClose(priv->conn); VIR_FREE(priv); } - static void remoteClientCloseFunc(virNetServerClientPtr client) { struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); daemonRemoveAllClientStreams(priv->streams); + remoteClientCleanEventCallbacks(priv); } - void *remoteClientInitHook(virNetServerClientPtr client, void *opaque ATTRIBUTE_UNUSED) { -- 1.8.3.1