Fix SIGSEGV on process exit in Qt6 module#1219
Fix SIGSEGV on process exit in Qt6 module#1219wltechblog wants to merge 3 commits intomltframework:masterfrom
Conversation
The QApplication created in createQApplicationIfNeeded() was never
deleted, causing a crash during C++ static destruction when Qt6's
XCB event queue thread is still running.
Two fixes:
1. Store QApplication in std::unique_ptr for controlled destruction
during this DSO's __cxa_finalize, before Qt dependencies are unmapped.
2. Pin the DSO with RTLD_NODELETE to prevent dlclose() from unmapping
Qt dependencies while their static destructors are still pending.
Backtrace:
exit() -> __cxa_finalize() -> QObject::~QObject() -> setParent_helper()
-> QCoreApplication::sendEvent() -> SEGFAULT
Tested on FreeBSD 15 with Qt 6.10.2, XCB platform.
There was a problem hiding this comment.
Pull request overview
Fixes a crash on process exit in the Qt6 module by ensuring the Qt application object and its dependent DSOs remain valid through C++ static destruction, avoiding unload-order issues triggered by dlclose().
Changes:
- Pin the Qt module DSO in memory on UNIX using
dlopen(..., RTLD_NODELETE)via a constructor hook. - Store the
QApplicationcreated by MLT in a staticstd::unique_ptrso it is deleted during DSO finalization.
- Check dlopen() return value and log warning via mlt_log_warning if pinning fails, rather than silently ignoring the failure - Clear stale dlerror() before calling dladdr for correct error reporting - Replace C-style cast with reinterpret_cast for C++ consistency
|
If you want to fix the clang-format failure (not strictly necessary) |
|
This is actually causing a crash on exit for me when I never had that problem before, in general: Thread 1 "melt" received signal SIGSEGV, Segmentation fault.
0x00007fffe75e26c4 in QGuiApplication::~QGuiApplication() () from /home/ddennedy/Qt/6.10.1/gcc_64/lib/libQt6Gui.so.6
(gdb) bt
#0 0x00007fffe75e26c4 in QGuiApplication::~QGuiApplication() ()
at /home/ddennedy/Qt/6.10.1/gcc_64/lib/libQt6Gui.so.6
#1 0x00007fffe8192c9d in QApplication::~QApplication() ()
at /home/ddennedy/Qt/6.10.1/gcc_64/lib/libQt6Widgets.so.6
#2 0x00007fffe7f8ce38 in std::default_delete<QApplication>::operator()(QApplication*) const
(this=0x7fffe7fff008 <s_app>, __ptr=0x55555556d110)
at /usr/include/c++/12/bits/unique_ptr.h:95
#3 0x00007fffe7f8cae0 in std::unique_ptr<QApplication, std::default_delete<QApplication> >::~unique_ptr() (this=0x7fffe7fff008 <s_app>, __in_chrg=<optimized out>)
at /usr/include/c++/12/bits/unique_ptr.h:396
#4 0x00007ffff7a45495 in __run_exit_handlers
(status=0, listp=0x7ffff7c1a838 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at ./stdlib/exit.c:113
#5 0x00007ffff7a45610 in __GI_exit (status=<optimized out>) at ./stdlib/exit.c:143
#6 0x00007ffff7a29d97 in __libc_start_call_main
(main=main@entry=0x555555559d7c <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffdd48)
at ../sysdeps/nptl/libc_start_call_main.h:74
#7 0x00007ffff7a29e40 in __libc_start_main_impl
(main=0x555555559d7c <main>, argc=2, argv=0x7fffffffdd48, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdd38) at ../csu/libc-start.c:392
#8 0x0000555555557c85 in _start ()Have you tried using a build of MLT from the tip of git master? See #1175. |
The previous patch stored QApplication in a static unique_ptr, relying on its destructor during __cxa_finalize to clean up. However, unique_ptr's destructor is registered with __cxa_atexit at DSO load time, BEFORE QApplication's constructor registers its own atexit handlers. Since atexit handlers run in LIFO order, this means the unique_ptr destructor runs AFTER Qt's internal cleanup, causing QGuiApplication::~QGuiApplication() to crash accessing already-torn-down state. Fix: keep unique_ptr for RAII leak protection, but add an explicit atexit(destroy_qapplication) registered AFTER QApplication construction. This ensures QApplication is deleted BEFORE Qt's internal cleanup (correct LIFO ordering). The unique_ptr destructor then becomes a harmless no-op since s_app is already null. Also improves the RTLD_NODELETE constructor: - Clear stale dlerror() before dlopen - Log a warning if dlopen fails instead of silently ignoring it - Use reinterpret_cast for C++ compatibility
|
I'll test with the tip. The previous diff may address the issue, although there may be something i'm missing with differences between clang and gcc builds. I'm testing on GhostBSD (based on FreeBSD) currently, which reliably crashes. I haven't had a crash on my Linux systems. |
|
The hard exit may have eliminated the crash, i'll do additional testing tomorrow. Thanks! |
Issue:
This bug is visible when using kdenlive, when rendering otherwise completes, mlt throws a segfault during exit. This causes kdenlive to display that the process has failed.
Cause:
The QApplication created in createQApplicationIfNeeded() was never deleted, causing a crash during C++ static destruction when Qt6's XCB event queue thread is still running.
Two fixes:
Store QApplication in std::unique_ptr for controlled destruction during this DSO's __cxa_finalize, before Qt dependencies are unmapped.
Pin the DSO with RTLD_NODELETE to prevent dlclose() from unmapping Qt dependencies while their static destructors are still pending.
Backtrace:
exit() -> __cxa_finalize() -> QObject::~QObject() -> setParent_helper()
-> QCoreApplication::sendEvent() -> SEGFAULT
Tested on GhostBSD 15 with Qt 6.10.2, XCB platform.