Skip to content

Commit c60b7cb

Browse files
committed
virsh: cmdEvent: Ensure that event callbacks are unregistered before returning
Successful return from 'virConnectDomainEventDeregisterAny' does not guarantee that there aren't still in-progress events being handled by the callbacks. Since 'cmdEvent' passes in a slice from an array as the private data of the callbacks, we must ensure that the array stays in scope (it's auto-freed) for the whole time there are possible callbacks being executed. While in practice this doesn't happen as the callbacks are usually quick enough to finish while unregistering stuff, placing a 'sleep(1)' into e.g. 'virshEventLifecyclePrint' and starting a domain results in crash of virsh with the following backtrace: Program terminated with signal SIGSEGV, Segmentation fault. #0 0x00005557b5cfd343 in virshEventPrintf (data=data@entry=0x5557db9619b0, fmt=fmt@entry=0x5557b5d5e527 "%s") at ../../../libvirt/tools/virsh-domain-event.c:252 Thread 2 (Thread 0x7f59a54b7d00 (LWP 2097121)): #0 0x00007f59a6cadbf9 in __futex_abstimed_wait_common () at /lib64/libc.so.6 #1 0x00007f59a6cb2cf3 in __pthread_clockjoin_ex () at /lib64/libc.so.6 #2 0x00005557b5cd57f6 in virshDeinit (ctl=0x7ffc7b615140) at ../../../libvirt/tools/virsh.c:408 #3 0x00005557b5cd5391 in main (argc=<optimized out>, argv=<optimized out>) at ../../../libvirt/tools/virsh.c:932 Thread 1 (Thread 0x7f59a51a66c0 (LWP 2097122)): #0 0x00005557b5cfd343 in virshEventPrintf (data=data@entry=0x5557db9619b0, fmt=fmt@entry=0x5557b5d5e527 "%s") at ../../../libvirt/tools/virsh-domain-event.c:252 #1 0x00005557b5cffa10 in virshEventPrint (data=0x5557db9619b0, buf=0x7f59a51a55c0) at ../../../libvirt/tools/virsh-domain-event.c:290 #2 virshEventLifecyclePrint (conn=<optimized out>, dom=<optimized out>, event=<optimized out>, detail=<optimized out>, opaque=0x5557db9619b0) at ../../../libvirt/ [snipped] From the backtrace you can see that the 'main()' thread is already shutting down virsh, which means that 'cmdEvent' terminated and the private data was freed. The event loop thread is still execing the callback which accesses the data. To fix this add a condition and wait on all of the callbacks to be unregistered first (their private data freeing function will be called). This bug was observed when I've copied the event code for a new virsh command which had a bit more involved callbacks. Fixes: 99fa96c Signed-off-by: Peter Krempa <[email protected]> Reviewed-by: Ján Tomko <[email protected]>
1 parent 258f61b commit c60b7cb

File tree

1 file changed

+32
-2
lines changed

1 file changed

+32
-2
lines changed

tools/virsh-domain-event.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,23 @@ struct virshDomEventData {
237237
bool timestamp;
238238
virshDomainEventCallback *cb;
239239
int id;
240+
241+
virMutex *m; /* needed to signal that handler was unregistered for clean shutdown */
242+
virCond *c;
240243
};
241244
typedef struct virshDomEventData virshDomEventData;
242245

243246

247+
static void
248+
virshDomEventDataUnregistered(virshDomEventData *d)
249+
{
250+
g_auto(virLockGuard) name = virLockGuardLock(d->m);
251+
/* signal that the handler was unregistered */
252+
d->id = -1;
253+
virCondSignal(d->c);
254+
}
255+
256+
244257
static void G_GNUC_PRINTF(2, 3)
245258
virshEventPrintf(virshDomEventData *data,
246259
const char *fmt,
@@ -936,6 +949,8 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd)
936949
bool timestamp = vshCommandOptBool(cmd, "timestamp");
937950
int count = 0;
938951
virshControl *priv = ctl->privData;
952+
g_auto(virMutex) m = VIR_MUTEX_INITIALIZER;
953+
g_auto(virCond) c = VIR_COND_INITIALIZER;
939954

940955
VSH_EXCLUSIVE_OPTIONS("all", "event");
941956
VSH_EXCLUSIVE_OPTIONS("list", "all");
@@ -969,6 +984,8 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd)
969984
data[ndata].count = &count;
970985
data[ndata].timestamp = timestamp;
971986
data[ndata].cb = &virshDomainEventCallbacks[i];
987+
data[ndata].m = &m;
988+
data[ndata].c = &c;
972989
data[ndata].id = -1;
973990
ndata++;
974991
}
@@ -994,7 +1011,7 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd)
9941011
data[i].event,
9951012
data[i].cb->cb,
9961013
&data[i],
997-
NULL)) < 0) {
1014+
(virFreeCallback) virshDomEventDataUnregistered)) < 0) {
9981015
/* When registering for all events: if the first
9991016
* registration succeeds, silently ignore failures on all
10001017
* later registrations on the assumption that the server
@@ -1022,14 +1039,27 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd)
10221039
ret = true;
10231040

10241041
cleanup:
1025-
vshEventCleanup(ctl);
10261042
if (data) {
10271043
for (i = 0; i < ndata; i++) {
10281044
if (data[i].id >= 0 &&
10291045
virConnectDomainEventDeregisterAny(priv->conn, data[i].id) < 0)
10301046
ret = false;
10311047
}
1048+
1049+
virMutexLock(&m);
1050+
while (true) {
1051+
for (i = 0; i < ndata; i++) {
1052+
if (data[i].id >= 0)
1053+
break;
1054+
}
1055+
1056+
if (i == ndata ||
1057+
virCondWait(&c, &m) < 0)
1058+
break;
1059+
}
1060+
virMutexUnlock(&m);
10321061
}
1062+
vshEventCleanup(ctl);
10331063
return ret;
10341064
}
10351065

0 commit comments

Comments
 (0)