Bug 427378 - Segfault in ProjectFileDataProvider::projectOpened()'s nested event loop when exiting KDevelop right after a large project is opened
Summary: Segfault in ProjectFileDataProvider::projectOpened()'s nested event loop when...
Status: RESOLVED FIXED
Alias: None
Product: kdevelop
Classification: Applications
Component: UI: QuickOpen (show other bugs)
Version: git master
Platform: Compiled Sources Linux
: NOR crash
Target Milestone: ---
Assignee: Igor Kushnir
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-10-06 09:08 UTC by Igor Kushnir
Modified: 2021-02-13 08:37 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In: 5.7.0


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Igor Kushnir 2020-10-06 09:08:46 UTC
SUMMARY
When a user exits KDevelop right after a large project is opened, before ProjectFileDataProvider::projectOpened() slot returns, IProjectController::projectClosing() signal is emitted before some slots connected to IProjectController::projectOpened() signal ale invoked (more precisely, the slots that were connected to projectOpened() signal later than the ProjectFileDataProvider::projectOpened() slot). Thus a valid assumption that a slot connected to projectOpened() is always called before a slot connected to projectClosing() becomes incorrect and causes a segmentation fault in ClassModel::removeProjectNode().

STEPS TO REPRODUCE
1. Open a large project with tens of thousands of files, such as KDevelop itself.
2. Exit KDevelop as soon as the just opened project appears in Projects tool view.

SOFTWARE/OS VERSIONS
Manjaro GNU/Linux, Xfce
KDE Frameworks Version: 5.74.0
Qt Version: 5.15.1

ADDITIONAL INFORMATION
I am going to create a Merge Request with a fix soon.

BACKTRACE (RelWithDebInfo)
Application: KDevelop (kdevelop), signal: Segmentation fault
Using host libthread_db library "/usr/lib/libthread_db.so.1".
[Current thread is 1 (Thread 0x7f3bdd900580 (LWP 40040))]

Thread 1 (Thread 0x7f3bdd900580 (LWP 40040)):
[KCrash Handler]
#6  0x00007f3bf519a711 in ClassModelNodes::Node::row() (this=0x0) at ../kdevplatform/language/classmodel/classmodelnode.cpp:487
#7  0x00007f3bf519a514 in ClassModelNodes::Node::removeNode(ClassModelNodes::Node*) (this=0x559e9ad20ae0, a_child=0x0) at ../kdevplatform/language/classmodel/classmodelnode.cpp:446
#8  0x00007f3bf51967c0 in ClassModel::removeProjectNode(KDevelop::IProject*) (this=0x559e9ad20aa0, project=0x559e9d2c3760) at ../kdevplatform/language/classmodel/classmodel.cpp:272
#9  0x00007f3bf5197fa9 in QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<KDevelop::IProject*>, void, void (ClassModel::*)(KDevelop::IProject*)>::call(void (ClassModel::*)(KDevelop::IProject*), ClassModel*, void**) (f=(void (ClassModel::*)(ClassModel * const, KDevelop::IProject *)) 0x7f3bf5196772 <ClassModel::removeProjectNode(KDevelop::IProject*)>, o=0x559e9ad20aa0, arg=0x7ffc5526f790) at /usr/include/qt/QtCore/qobjectdefs_impl.h:152
#10 0x00007f3bf5197dfd in QtPrivate::FunctionPointer<void (ClassModel::*)(KDevelop::IProject*)>::call<QtPrivate::List<KDevelop::IProject*>, void>(void (ClassModel::*)(KDevelop::IProject*), ClassModel*, void**) (f=(void (ClassModel::*)(ClassModel * const, KDevelop::IProject *)) 0x7f3bf5196772 <ClassModel::removeProjectNode(KDevelop::IProject*)>, o=0x559e9ad20aa0, arg=0x7ffc5526f790) at /usr/include/qt/QtCore/qobjectdefs_impl.h:185
#11 0x00007f3bf519791a in QtPrivate::QSlotObject<void (ClassModel::*)(KDevelop::IProject*), QtPrivate::List<KDevelop::IProject*>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) (which=1, this_=0x559e9ad1d730, r=0x559e9ad20aa0, a=0x7ffc5526f790, ret=0x0) at /usr/include/qt/QtCore/qobjectdefs_impl.h:414
#12 0x00007f3bf21fd50e in  () at /usr/lib/libQt5Core.so.5
#13 0x00007f3bf4cc18da in KDevelop::IProjectController::projectClosing(KDevelop::IProject*) (this=0x559e97168110, _t1=0x559e9d2c3760) at kdevplatform/interfaces/KDevPlatformInterfaces_autogen/EWIEGA46WW/moc_iprojectcontroller.cpp:273
#14 0x00007f3bf61ed683 in KDevelop::ProjectController::takeProject(KDevelop::IProject*) (this=0x559e97168110, proj=0x559e9d2c3760) at ../kdevplatform/shell/projectcontroller.cpp:1070
#15 0x00007f3bf61ed759 in KDevelop::ProjectController::closeProject(KDevelop::IProject*) (this=0x559e97168110, proj=0x559e9d2c3760) at ../kdevplatform/shell/projectcontroller.cpp:1090
#16 0x00007f3bf61ed81e in KDevelop::ProjectController::closeAllProjects() (this=0x559e97168110) at ../kdevplatform/shell/projectcontroller.cpp:1100
#17 0x00007f3bf61ea918 in KDevelop::ProjectController::cleanup() (this=0x559e97168110) at ../kdevplatform/shell/projectcontroller.cpp:665
#18 0x00007f3bf61d9c6d in KDevelop::Core::cleanup() (this=0x559e97676230) at ../kdevplatform/shell/core.cpp:395
#19 0x00007f3bf61d996e in KDevelop::Core::shutdown() (this=0x559e97676230) at ../kdevplatform/shell/core.cpp:356
#20 0x00007f3bf61b47e3 in KDevelop::MainWindow::~MainWindow() (this=0x559e97695730, __in_chrg=<optimized out>, __vtt_parm=<optimized out>) at ../kdevplatform/shell/mainwindow.cpp:150
#21 0x00007f3bf61b48a6 in KDevelop::MainWindow::~MainWindow() (this=0x559e97695730, __in_chrg=<optimized out>, __vtt_parm=<optimized out>) at ../kdevplatform/shell/mainwindow.cpp:154
#22 0x00007f3bf21f50a0 in QObject::event(QEvent*) () at /usr/lib/libQt5Core.so.5
#23 0x00007f3bf30ac439 in QWidget::event(QEvent*) () at /usr/lib/libQt5Widgets.so.5
#24 0x00007f3bf31c6515 in QMainWindow::event(QEvent*) () at /usr/lib/libQt5Widgets.so.5
#25 0x00007f3bf3d1fefc in KMainWindow::event(QEvent*) () at /usr/lib/libKF5XmlGui.so.5
#26 0x00007f3bf3d6b578 in KXmlGuiWindow::event(QEvent*) () at /usr/lib/libKF5XmlGui.so.5
#27 0x00007f3bf61b4de3 in KDevelop::MainWindow::event(QEvent*) (this=0x559e97695730, ev=0x559e9d3a63e0) at ../kdevplatform/shell/mainwindow.cpp:208
#28 0x00007f3bf3068362 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/lib/libQt5Widgets.so.5
#29 0x00007f3bf3071819 in QApplication::notify(QObject*, QEvent*) () at /usr/lib/libQt5Widgets.so.5
#30 0x00007f3bf21c7a12 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /usr/lib/libQt5Core.so.5
#31 0x00007f3bf21ca276 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) () at /usr/lib/libQt5Core.so.5
#32 0x00007f3bf2220a84 in  () at /usr/lib/libQt5Core.so.5
#33 0x00007f3befcfd9be in g_main_context_dispatch () at /usr/lib/libglib-2.0.so.0
#34 0x00007f3befcff831 in  () at /usr/lib/libglib-2.0.so.0
#35 0x00007f3befcff871 in g_main_context_iteration () at /usr/lib/libglib-2.0.so.0
#36 0x00007f3bf2220143 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt5Core.so.5
#37 0x00007f3bc59b7124 in ProjectFileDataProvider::projectOpened(KDevelop::IProject*) (this=0x559e98cdbb00, project=0x559e9d2c3760) at ../plugins/quickopen/projectfilequickopen.cpp:248
#38 0x00007f3bc59c092f in QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<KDevelop::IProject*>, void, void (ProjectFileDataProvider::*)(KDevelop::IProject*)>::call(void (ProjectFileDataProvider::*)(KDevelop::IProject*), ProjectFileDataProvider*, void**) (f=(void (ProjectFileDataProvider::*)(ProjectFileDataProvider * const, KDevelop::IProject *)) 0x7f3bc59b702e <ProjectFileDataProvider::projectOpened(KDevelop::IProject*)>, o=0x559e98cdbb00, arg=0x7ffc552702f0) at /usr/include/qt/QtCore/qobjectdefs_impl.h:152
#39 0x00007f3bc59bf2fe in QtPrivate::FunctionPointer<void (ProjectFileDataProvider::*)(KDevelop::IProject*)>::call<QtPrivate::List<KDevelop::IProject*>, void>(void (ProjectFileDataProvider::*)(KDevelop::IProject*), ProjectFileDataProvider*, void**) (f=(void (ProjectFileDataProvider::*)(ProjectFileDataProvider * const, KDevelop::IProject *)) 0x7f3bc59b702e <ProjectFileDataProvider::projectOpened(KDevelop::IProject*)>, o=0x559e98cdbb00, arg=0x7ffc552702f0) at /usr/include/qt/QtCore/qobjectdefs_impl.h:185
#40 0x00007f3bc59bdbc4 in QtPrivate::QSlotObject<void (ProjectFileDataProvider::*)(KDevelop::IProject*), QtPrivate::List<KDevelop::IProject*>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) (which=1, this_=0x559e98cdcaf0, r=0x559e98cdbb00, a=0x7ffc552702f0, ret=0x0) at /usr/include/qt/QtCore/qobjectdefs_impl.h:414
#41 0x00007f3bf21fd50e in  () at /usr/lib/libQt5Core.so.5
#42 0x00007f3bf4cc1864 in KDevelop::IProjectController::projectOpened(KDevelop::IProject*) (this=0x559e97168110, _t1=0x559e9d2c3760) at kdevplatform/interfaces/KDevPlatformInterfaces_autogen/EWIEGA46WW/moc_iprojectcontroller.cpp:266
#43 0x00007f3bf61ecf38 in KDevelop::ProjectController::projectImportingFinished(KDevelop::IProject*) (this=0x559e97168110, project=0x559e9d2c3760) at ../kdevplatform/shell/projectcontroller.cpp:1000
#44 0x00007f3bf62058a3 in KDevelop::ProjectPrivate::importDone(KJob*) (this=0x559e9d2c5a00, job=0x559e9cb327c0) at ../kdevplatform/shell/project.cpp:221
#45 0x00007f3bf62030e4 in KDevelop::Project::<lambda(KJob*)>::operator()(KJob *) const (__closure=0x559e9cb36f10, job=0x559e9cb327c0) at ../kdevplatform/shell/project.cpp:548
#46 0x00007f3bf620525b in QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<KJob*>, void, KDevelop::Project::open(const KDevelop::Path&)::<lambda(KJob*)> >::call(KDevelop::Project::<lambda(KJob*)> &, void **) (f=..., arg=0x7ffc552705b0) at /usr/include/qt/QtCore/qobjectdefs_impl.h:146
#47 0x00007f3bf62051b3 in QtPrivate::Functor<KDevelop::Project::open(const KDevelop::Path&)::<lambda(KJob*)>, 1>::call<QtPrivate::List<KJob*>, void>(KDevelop::Project::<lambda(KJob*)> &, void *, void **) (f=..., arg=0x7ffc552705b0) at /usr/include/qt/QtCore/qobjectdefs_impl.h:256
#48 0x00007f3bf6205157 in QtPrivate::QFunctorSlotObject<KDevelop::Project::open(const KDevelop::Path&)::<lambda(KJob*)>, 1, QtPrivate::List<KJob*>, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *, void **, bool *) (which=1, this_=0x559e9cb36f00, r=0x559e9d2c3760, a=0x7ffc552705b0, ret=0x0) at /usr/include/qt/QtCore/qobjectdefs_impl.h:439
#49 0x00007f3bf21fd50e in  () at /usr/lib/libQt5Core.so.5
#50 0x00007f3bf26f4e4d in KJob::result(KJob*, KJob::QPrivateSignal) () at /usr/lib/libKF5CoreAddons.so.5
#51 0x00007f3bf26f593c in KJob::finishJob(bool) () at /usr/lib/libKF5CoreAddons.so.5
#52 0x00007f3bf4d2f52a in KDevelop::ExecuteCompositeJob::slotResult(KJob*) (this=0x559e9cb327c0, job=0x559e9ccb82b0) at ../kdevplatform/util/executecompositejob.cpp:129
#53 0x00007f3bf21fd50e in  () at /usr/lib/libQt5Core.so.5
#54 0x00007f3bf26f4e4d in KJob::result(KJob*, KJob::QPrivateSignal) () at /usr/lib/libKF5CoreAddons.so.5
#55 0x00007f3bf26f593c in KJob::finishJob(bool) () at /usr/lib/libKF5CoreAddons.so.5
#56 0x00007f3bf1948aa5 in KDevelop::FileManagerListJob::handleResults(QList<KIO::UDSEntry> const&) (this=0x559e9ccb82b0, entriesIn=...) at ../kdevplatform/project/filemanagerlistjob.cpp:196
#57 0x00007f3bf1914a43 in KDevelop::FileManagerListJob::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) (_o=0x559e9ccb82b0, _c=QMetaObject::InvokeMetaMethod, _id=4, _a=0x559e9d5bbc48) at kdevplatform/project/KDevPlatformProject_autogen/EWIEGA46WW/moc_filemanagerlistjob.cpp:108
#58 0x00007f3bf21f530a in QObject::event(QEvent*) () at /usr/lib/libQt5Core.so.5
#59 0x00007f3bf3068362 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/lib/libQt5Widgets.so.5
#60 0x00007f3bf3071819 in QApplication::notify(QObject*, QEvent*) () at /usr/lib/libQt5Widgets.so.5
#61 0x00007f3bf21c7a12 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /usr/lib/libQt5Core.so.5
#62 0x00007f3bf21ca276 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) () at /usr/lib/libQt5Core.so.5
#63 0x00007f3bf2220a84 in  () at /usr/lib/libQt5Core.so.5
#64 0x00007f3befcfd9be in g_main_context_dispatch () at /usr/lib/libglib-2.0.so.0
#65 0x00007f3befcff831 in  () at /usr/lib/libglib-2.0.so.0
#66 0x00007f3befcff871 in g_main_context_iteration () at /usr/lib/libglib-2.0.so.0
#67 0x00007f3bf22200d0 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt5Core.so.5
#68 0x00007f3bf21c657c in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt5Core.so.5
#69 0x00007f3bf21ce586 in QCoreApplication::exec() () at /usr/lib/libQt5Core.so.5
#70 0x0000559e96202588 in main(int, char**) (argc=3, argv=0x7ffc55271218) at ../app/main.cpp:850
[Inferior 1 (process 40040) detached]
Comment 1 Igor Kushnir 2021-02-13 08:37:25 UTC
Git commit 3f1b6392cb528d7b8986ab9a0a641cd9e346ef8d by Igor Kushnir.
Committed on 13/02/2021 at 08:36.
Pushed by igorkushnir into branch 'master'.

Don't process events in ProjectFileDataProvider::projectOpened()

When a user exits KDevelop or closes the just opened project in the
ProjectFileDataProvider::projectOpened()'s nested event loop,
IProjectController::projectClosing() signal is emitted before some slots
connected to IProjectController::projectOpened() signal ale invoked
(more precisely, the slots that were connected to projectOpened() signal
later than the ProjectFileDataProvider::projectOpened() slot). Thus a
valid assumption that a slot connected to projectOpened() is always
called before a slot connected to projectClosing() becomes incorrect and
causes a crash, e.g. a segmentation fault in
ClassModel::removeProjectNode(). In addition,
ProjectController::projectImportingFinished() calls reparseProject()
after emitting the projectOpened() signal. Delaying this call until
after the project is closed and possibly destroyed would likely case a
crash or some other bug.

Queuing the slow code and its associated QApplication::processEvents()
instead of calling it directly in
ProjectFileDataProvider::projectOpened() would not eliminate the crash,
but move the segmentation fault from an unrelated class to
ProjectFileDataProvider::fileAddedToSet().

When the user disables the Quick Open plugin in this projectOpened()'s
nested event loop, a similar crash occurs.

These crashes could be fixed by checking whether the project is still
open and checking QPointer{this} after the call to
QApplication::processEvents(). But the code wouldn't become crash-proof
even then. ProjectFileDataProvider should not enter a nested event loop
while iterating over the project tree, because a project item can be
destroyed during the event loop when a user removes a file, which can
make one of the pointers to ProjectBaseItem held by
KDevelop::forEachFile() dangling.

The benefits of eliminating the nested event loop:
1) no need for verbose crash workarounds;
2) opening projects can be optimized by sorting and merging sorted
ranges. In particular, a performance pitfall, when multiple large
projects are opened in a certain order, can be eliminated.
Related: bug 427379
FIXED-IN: 5.7.0

M  +0    -8    plugins/quickopen/projectfilequickopen.cpp

https://invent.kde.org/kdevelop/kdevelop/commit/3f1b6392cb528d7b8986ab9a0a641cd9e346ef8d