SUMMARY When a user exits KDevelop while debugging a program, depending on the relative timing of KDevelop exit and the 5 second interval of the singleshot timer in MIDebugSession::stopDebugger(), a DebugSession may be destroyed safely in time or not destroyed at all and possibly cause a crash by accessing the already destroyed DebugController or its children. A DebugSession's state transition signals lead to accesses to DebugController and its children, such as a call to BreakpointModel::updateState(). Even though IBreakpointController::breakpointModel() returns nullptr when ICore::self() is nullptr, IBreakpointController::updateState() doesn't check for it. That is why this=0x0 in BreakpointModel::updateState() in the backtrace below. SOFTWARE/OS VERSIONS Manjaro GNU/Linux, Xfce KDE Frameworks Version: 5.73.0 Qt Version: 5.15.0 ADDITIONAL INFORMATION I am going to create a Merge Request with a fix soon. BACKTRACE (Debug) Application: KDevelop (kdevelop), signal: Segmentation fault [KCrash Handler] #4 0x00007f38447fd190 in QScopedPointer<KDevelop::BreakpointModelPrivate, QScopedPointerDeleter<KDevelop::BreakpointModelPrivate> >::operator->() const (this=0x10) at /usr/include/qt/QtCore/qscopedpointer.h:118 #5 0x00007f38447fc57b in qGetPtrHelper<QScopedPointer<KDevelop::BreakpointModelPrivate, QScopedPointerDeleter<KDevelop::BreakpointModelPrivate> > const>(QScopedPointer<KDevelop::BreakpointModelPrivate, QScopedPointerDeleter<KDevelop::BreakpointModelPrivate> > const&) (ptr=...) at /usr/include/qt/QtCore/qglobal.h:1133 #6 0x00007f38447fc442 in KDevelop::BreakpointModel::d_func() (this=0x0) at ../kdevplatform/debugger/breakpoint/breakpointmodel.h:198 #7 0x00007f38447f9b3e in KDevelop::BreakpointModel::updateState(int, KDevelop::Breakpoint::BreakpointState) (this=0x0, row=0, state=KDevelop::Breakpoint::NotStartedState) at ../kdevplatform/debugger/breakpoint/breakpointmodel.cpp:326 #8 0x00007f38447e4fcd in KDevelop::IBreakpointController::updateState(int, KDevelop::Breakpoint::BreakpointState) (this=0x55c5920a9f10, row=0, state=KDevelop::Breakpoint::NotStartedState) at ../kdevplatform/debugger/interfaces/ibreakpointcontroller.cpp:61 #9 0x00007f37d2b7800b in KDevMI::MIBreakpointController::debuggerStateChanged(KDevelop::IDebugSession::DebuggerState) (this=0x55c5920a9f10, state=KDevelop::IDebugSession::EndedState) at ../plugins/debuggercommon/mibreakpointcontroller.cpp:301 #10 0x00007f38447e7dea in QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<KDevelop::IDebugSession::DebuggerState>, void, void (KDevelop::IBreakpointController::*)(KDevelop::IDebugSession::DebuggerState)>::call(void (KDevelop::IBreakpointController::*)(KDevelop::IDebugSession::DebuggerState), KDevelop::IBreakpointController*, void**) (f=&virtual table offset 120, o=0x55c5920a9f10, arg=0x7ffe806e5750) at /usr/include/qt/QtCore/qobjectdefs_impl.h:152 #11 0x00007f38447e799b in QtPrivate::FunctionPointer<void (KDevelop::IBreakpointController::*)(KDevelop::IDebugSession::DebuggerState)>::call<QtPrivate::List<KDevelop::IDebugSession::DebuggerState>, void>(void (KDevelop::IBreakpointController::*)(KDevelop::IDebugSession::DebuggerState), KDevelop::IBreakpointController*, void**) (f=&virtual table offset 120, o=0x55c5920a9f10, arg=0x7ffe806e5750) at /usr/include/qt/QtCore/qobjectdefs_impl.h:185 #12 0x00007f38447e72bc in QtPrivate::QSlotObject<void (KDevelop::IBreakpointController::*)(KDevelop::IDebugSession::DebuggerState), QtPrivate::List<KDevelop::IDebugSession::DebuggerState>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) (which=1, this_=0x55c5a684ba10, r=0x55c5920a9f10, a=0x7ffe806e5750, ret=0x0) at /usr/include/qt/QtCore/qobjectdefs_impl.h:418 #13 0x00007f3845036a26 in () at /usr/lib/libQt5Core.so.5 #14 0x00007f38447e3c27 in KDevelop::IDebugSession::stateChanged(KDevelop::IDebugSession::DebuggerState) (this=0x55c5c0f70b30, _t1=KDevelop::IDebugSession::EndedState) at kdevplatform/debugger/KDevPlatformDebugger_autogen/include/moc_idebugsession.cpp:284 #15 0x00007f37d2b65243 in KDevMI::MIDebugSession::setSessionState(KDevelop::IDebugSession::DebuggerState) (this=0x55c5c0f70b30, state=KDevelop::IDebugSession::EndedState) at ../plugins/debuggercommon/midebugsession.cpp:369 #16 0x00007f37d2b661e7 in KDevMI::MIDebugSession::handleDebuggerStateChange(QFlags<KDevMI::DBGStateFlag>, QFlags<KDevMI::DBGStateFlag>) (this=0x55c5c0f70b30, oldState=..., newState=...) at ../plugins/debuggercommon/midebugsession.cpp:499 #17 0x00007f37d2b65506 in KDevMI::MIDebugSession::setDebuggerState(QFlags<KDevMI::DBGStateFlag>) (this=0x55c5c0f70b30, newState=...) at ../plugins/debuggercommon/midebugsession.cpp:410 #18 0x00007f37d2b665d8 in operator()() const (__closure=0x55c5b06343f0) at ../plugins/debuggercommon/midebugsession.cpp:567 #19 0x00007f37d2b6be54 in QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, KDevMI::MIDebugSession::stopDebugger()::<lambda()> >::call(struct {...} &, void **) (f=..., arg=0x7ffe806e5a00) at /usr/include/qt/QtCore/qobjectdefs_impl.h:146 #20 0x00007f37d2b6be26 in QtPrivate::Functor<KDevMI::MIDebugSession::stopDebugger()::<lambda()>, 0>::call<QtPrivate::List<>, void>(struct {...} &, void *, void **) (f=..., arg=0x7ffe806e5a00) at /usr/include/qt/QtCore/qobjectdefs_impl.h:256 #21 0x00007f37d2b6bd8f in QtPrivate::QFunctorSlotObject<KDevMI::MIDebugSession::stopDebugger()::<lambda()>, 0, QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *, void **, bool *) (which=1, this_=0x55c5b06343e0, r=0x55c5c0f70b30, a=0x7ffe806e5a00, ret=0x0) at /usr/include/qt/QtCore/qobjectdefs_impl.h:443 #22 0x00007f384503ab33 in () at /usr/lib/libQt5Core.so.5 #23 0x00007f384502c10f in QObject::event(QEvent*) () at /usr/lib/libQt5Core.so.5 #24 0x00007f3845e83702 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/lib/libQt5Widgets.so.5 #25 0x00007f3844fff7ba in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /usr/lib/libQt5Core.so.5 #26 0x00007f38450576b5 in QTimerInfoList::activateTimers() () at /usr/lib/libQt5Core.so.5 #27 0x00007f3845057f62 in () at /usr/lib/libQt5Core.so.5 #28 0x00007f3842eed43c in g_main_context_dispatch () at /usr/lib/libglib-2.0.so.0 #29 0x00007f3842f3b1d9 in () at /usr/lib/libglib-2.0.so.0 #30 0x00007f3842eec221 in g_main_context_iteration () at /usr/lib/libglib-2.0.so.0 #31 0x00007f3845058331 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt5Core.so.5 #32 0x00007f3844ffe13c in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt5Core.so.5 #33 0x00007f38450065c4 in QCoreApplication::exec() () at /usr/lib/libQt5Core.so.5 #34 0x000055c5701b267c in main(int, char**) (argc=3, argv=0x7ffe806e6218) at ../app/main.cpp:850 [Inferior 1 (process 15477) detached]
Git commit 37ff587ebe2a2c355d71cb583fb5f058c5301ff3 by Igor Kushnir. Committed on 15/10/2020 at 15:26. Pushed by igorkushnir into branch '5.6'. Safely finalize and destroy DebugSession objects DebugController is responsible for destroying debug sessions and it can not do so past its own destructor. Currently, depending on the relative timing of KDevelop exit and the 5 second interval of the singleshot timer in MIDebugSession::stopDebugger(), a DebugSession may be destroyed safely in time or not destroyed at all and possibly cause a crash by accessing the already destroyed DebugController or its children. Let us kill debugger processes and thus finalize the debug sessions' states in ~DebugController(). We delay the killing of debugger processes for as long as possible to give them a chance to exit on their own. Check ICore::documentController() for nullptr in BreakpointModel::updateMarks() and DebugController::clearExecutionPoint() to prevent crashes when these slots are invoked by DebugSession's state transition signals from inside ~DebugController(), which is called after ~DocumentController(). FIXED-IN: 5.6.1 M +9 -2 kdevplatform/debugger/breakpoint/breakpointmodel.cpp M +2 -0 kdevplatform/debugger/interfaces/idebugsession.h M +20 -2 kdevplatform/shell/debugcontroller.cpp M +1 -0 kdevplatform/shell/debugcontroller.h M +5 -0 kdevplatform/tests/testdebugsession.cpp M +1 -0 kdevplatform/tests/testdebugsession.h M +17 -3 plugins/debuggercommon/midebugsession.cpp M +4 -0 plugins/debuggercommon/midebugsession.h https://invent.kde.org/kdevelop/kdevelop/commit/37ff587ebe2a2c355d71cb583fb5f058c5301ff3
Had to revert the fix in the stable 5.6 branch, because it broke the ABI. The commit is restored in the master branch, so I set Version Fixed In to 5.7.0.