python-tdbus
python-tdbus copied to clipboard
Segfault when sending large messages
I guess this module is unmaintained but I found a bug that leads to a segfault when large messages are sent. Neither the coredump backtrace, pdb nor faulthandler helped me to track it down to it's source. It's basically the same for strings ("s") and byte arrays ("ay") and probably other types, too. It also happens with both libev and libuv as gevent backends.
Minimal program to reproduce the problem:
from gevent import monkey
monkey.patch_all()
import contextlib
import gevent
import gevent.hub
import tdbus
DATA_LEN = 5000000
# DATA_LEN = 50000 # doesn't happen with this size
OBJECT_PATH = "/com/example/TDBus"
METHOD = "Hello"
INTERFACE = "com.example.Hello"
DESTINATION = "com.example.Hello"
# dbus-test-tool echo --name=com.example.Hello
def do_call(conn):
data = ("x"*DATA_LEN).encode()
result = conn.call_method(
OBJECT_PATH,
METHOD,
INTERFACE,
destination=DESTINATION,
timeout=2,
format="ay",
args=[data])
print(repr(result))
return result
def main_simple():
conn = tdbus.SimpleDBusConnection(tdbus.DBUS_BUS_SESSION)
do_call(conn)
def main_gevent():
conn = tdbus.GEventDBusConnection(tdbus.DBUS_BUS_SESSION)
gevent.spawn(do_call, conn)
with contextlib.suppress(KeyboardInterrupt):
gevent.hub.get_hub().switch()
if __name__ == "__main__":
#main_simple() # doesn't happen with SimpleDBusConnection
main_gevent() # does happen here!
This is from the coredump:
Program terminated with signal SIGSEGV, Segmentation fault.
#0 Py_INCREF (op=0x0) at /usr/include/python3.11/object.h:502
502 op->ob_refcnt++;
Backtrace:
#0 Py_INCREF (op=0x0) at /usr/include/python3.11/object.h:502
#1 __pyx_pf_6gevent_5libev_8corecext_7watcher_4stop (__pyx_v_self=0x7c7e4551a8c0) at src/gevent/libev/corecext.c:15339
#2 __pyx_pw_6gevent_5libev_8corecext_7watcher_5stop (__pyx_v_self=0x7c7e4551a8c0, unused=0x0) at src/gevent/libev/corecext.c:15321
#3 0x00007c7e469ad973 in ?? () from /usr/lib/libpython3.11.so.1.0
#4 0x00007c7e469ad100 in ?? () from /usr/lib/libpython3.11.so.1.0
#5 0x00007c7e45fc2cb4 in gevent_stop (watcher=0x7c7e4551a8c0, loop=0x7c7e45202b20) at src/gevent/libev/callbacks.c:67
#6 0x00007c7e45fc3b68 in gevent_callback (revents=2, c_watcher=0x7c7e4551a900, watcher=0x7c7e4551a8c0, args=0x7c7e4553c3c0, callback=0x7c7e45265200, loop=0x7c7e45202b20) at src/gevent/libev/callbacks.c:131
#7 gevent_callback_io (_loop=<optimized out>, c_watcher=0x7c7e4551a900, revents=2) at src/gevent/libev/callbacks.c:214
#8 0x00007c7e45fbaa23 in ev_invoke_pending (loop=0x7c7e45ffeb40 <default_loop_struct>) at /opt/flo_nobackup/gevent/deps/libev/ev.c:3770
#9 0x00007c7e45fc0c23 in ev_run (loop=0x7c7e45ffeb40 <default_loop_struct>, flags=0) at /opt/flo_nobackup/gevent/deps/libev/ev.c:4190
#10 0x00007c7e45fcd7cb in __pyx_pf_6gevent_5libev_8corecext_4loop_14run (__pyx_v_once=<optimized out>, __pyx_v_nowait=<optimized out>, __pyx_v_self=0x7c7e45202b20) at src/gevent/libev/corecext.c:10211
#11 __pyx_pw_6gevent_5libev_8corecext_4loop_15run (__pyx_v_self=0x7c7e45202b20, __pyx_args=<optimized out>, __pyx_nargs=<optimized out>, __pyx_kwds=<optimized out>) at src/gevent/libev/corecext.c:10161
#12 0x00007c7e46974237 in PyObject_Vectorcall () from /usr/lib/libpython3.11.so.1.0
#13 0x00007c7e469665d3 in _PyEval_EvalFrameDefault () from /usr/lib/libpython3.11.so.1.0
#14 0x00007c7e469ad613 in ?? () from /usr/lib/libpython3.11.so.1.0
#15 0x00007c7e469ad100 in ?? () from /usr/lib/libpython3.11.so.1.0
#16 0x00007c7e465249a3 in greenlet::UserGreenlet::inner_bootstrap (this=this@entry=0x7c7e45537570, origin_greenlet=<optimized out>, run=run@entry=0x7c7e45264f80) at src/greenlet/TUserGreenlet.cpp:464
#17 0x00007c7e46525c8f in greenlet::UserGreenlet::g_initialstub (this=0x7c7e45537570, mark=0x7fff1fbf2ee8) at src/greenlet/TUserGreenlet.cpp:311
#18 0x00007c7e4652446f in greenlet::UserGreenlet::g_switch (this=0x7c7e45537570) at src/greenlet/TUserGreenlet.cpp:179
#19 0x00007c7e4651f331 in green_switch (self=0x7c7e45887a10, args=0x7c7e46cf3338 <_PyRuntime+58904>, kwargs=0x0) at src/greenlet/greenlet.cpp:530
#20 0x00007c7e45e94222 in __pyx_f_6gevent_29_gevent_c_greenlet_primitives__greenlet_switch (__pyx_v_self=0x7c7e45887a10) at src/gevent/_greenlet_primitives.c:2649
#21 __pyx_f_6gevent_29_gevent_c_greenlet_primitives_25SwitchOutGreenletWithLoop_switch (__pyx_v_self=0x7c7e45887a10, __pyx_skip_dispatch=__pyx_skip_dispatch@entry=1) at src/gevent/_greenlet_primitives.c:3180
#22 0x00007c7e45e94d02 in __pyx_pf_6gevent_29_gevent_c_greenlet_primitives_25SwitchOutGreenletWithLoop_switch (__pyx_v_self=<optimized out>) at src/gevent/_greenlet_primitives.c:3235
#23 __pyx_pw_6gevent_29_gevent_c_greenlet_primitives_25SwitchOutGreenletWithLoop_1switch (__pyx_v_self=<optimized out>, unused=<optimized out>) at src/gevent/_greenlet_primitives.c:3219
#24 0x00007c7e46974237 in PyObject_Vectorcall () from /usr/lib/libpython3.11.so.1.0
#25 0x00007c7e469665d3 in _PyEval_EvalFrameDefault () from /usr/lib/libpython3.11.so.1.0
#26 0x00007c7e46a1fae4 in ?? () from /usr/lib/libpython3.11.so.1.0
#27 0x00007c7e46a1f4cc in PyEval_EvalCode () from /usr/lib/libpython3.11.so.1.0
#28 0x00007c7e46a3cd03 in ?? () from /usr/lib/libpython3.11.so.1.0
#29 0x00007c7e46a38e0a in ?? () from /usr/lib/libpython3.11.so.1.0
#30 0x00007c7e46a4f383 in ?? () from /usr/lib/libpython3.11.so.1.0
#31 0x00007c7e46a4ecf5 in _PyRun_SimpleFileObject () from /usr/lib/libpython3.11.so.1.0
#32 0x00007c7e46a4d5f8 in _PyRun_AnyFileObject () from /usr/lib/libpython3.11.so.1.0
#33 0x00007c7e46a48098 in Py_RunMain () from /usr/lib/libpython3.11.so.1.0
#34 0x00007c7e46a131eb in Py_BytesMain () from /usr/lib/libpython3.11.so.1.0
#35 0x00007c7e46643cd0 in __libc_start_call_main (main=main@entry=0x5bc5076e0120, argc=argc@entry=2, argv=argv@entry=0x7fff1fbf36a8) at ../sysdeps/nptl/libc_start_call_main.h:58
#36 0x00007c7e46643d8a in __libc_start_main_impl (main=0x5bc5076e0120, argc=2, argv=0x7fff1fbf36a8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fff1fbf3698) at ../csu/libc-start.c:360
#37 0x00005bc5076e0045 in _start ()
From the perspective of the python code, it's
class GEventDBusConnection(DBusConnection):
Loop = GEventLoop
Local = local.local
def call_method(self, *args, **kwargs):
"""Call a method."""
callback = kwargs.get('callback')
if callback is not None:
super(GEventDBusConnection, self).call_method(*args, **kwargs)
return
waiter = Waiter()
def _gevent_callback(message):
waiter.switch(message)
kwargs['callback'] = _gevent_callback
super(GEventDBusConnection, self).call_method(*args, **kwargs)
reply = waiter.get() # <--- here
self._handle_errors(reply)
return reply