Bug 1433028

Summary: Custom event loop impl calls ff callback from *Remove(Handle|Timeout)Func
Product: [Community] Virtualization Tools Reporter: wojciech
Component: libvirt-pythonAssignee: Libvirt Maintainers <libvirt-maint>
Status: NEW --- QA Contact:
Severity: unspecified Docs Contact:
Priority: unspecified    
Version: unspecifiedCC: libvirt-maint
Target Milestone: ---Keywords: Reopened
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2025-10-17 00:10:32 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description wojciech 2017-03-16 15:37:27 UTC
Description of problem:
As of current libvirt-python.git, according to libvirt-override.c, if
implementing custom event loop in Python, ff callback is called from
libvirt_virEventRemoveHandleFunc, which is a C glue between
virEventRegisterImpl and actual removeHandle function written in Python:

> result = PyEval_CallObject(removeHandleObj, pyobj_args);
> if (!result) {
>     PyErr_Print();
>     PyErr_Clear();
> } else if (!PyTuple_Check(result) || PyTuple_Size(result) != 3) {
>     DEBUG("%s: %s must return opaque obj registered with %s"
>           "to avoid leaking libvirt memory\n",
>           __FUNCTION__, NAME(removeHandle), NAME(addHandle));
> } else {
>     opaque = PyTuple_GetItem(result, 1);
>     ff = PyTuple_GetItem(result, 2);
>     cff = PyvirFreeCallback_Get(ff);
>     if (cff)
>         (*cff)(PyvirVoidPtr_Get(opaque));
>     retval = 0;
> }

This is exactly what one should not be doing according to documentation [1]:

> If the opaque user data requires free'ing when the handle is unregistered,
> then a 2nd callback can be supplied for this purpose. This callback needs to
> be invoked from a clean stack. If 'ff' callbacks are invoked directly from the
> virEventRemoveHandleFunc they will likely deadlock in libvirt.

[1] https://libvirt.org/html/libvirt-libvirt-event.html#virEventAddHandleFunc

This is true, the deadlock occurs. When the "result" tuple is mangled to have
None as third item ("ff"), then cff = PyvirFreeCallback_Get(ff) is NULL and
the deadlock does not happen.


Version-Release number of selected component (if applicable):
Current git as of January-March 2017


How reproducible:
always


Steps to Reproduce:

def remove_handle(...):
    #...
    return opaque
libvirt.virEventRegisterImpl(..., remove_handle, ...)
libvirt.open(uri).close()

Actual results:
deadlock inside libvirtmod

Additional info:
The script examples/event-test.py does not deadlock, because it
does not return anything (that is, returns None) from Python, so the second if
block happens and the ff callback, if any, is not executed (and probably
something leaks, but I didn't check for that).

Everything also applies to to timeouts (libvirt_virEventRemoteTimeoutFunc).

Comment 1 Red Hat Bugzilla 2025-10-17 00:10:32 UTC
This product has been discontinued or is no longer tracked in Red Hat Bugzilla.

Comment 2 Alasdair Kergon 2025-10-17 12:52:13 UTC
Reopening because Virtualization Tools has not been discontinued.