Summary: | Plasma System Monitor segfaults when viewing processes tab | ||
---|---|---|---|
Product: | [Applications] plasma-systemmonitor | Reporter: | Alek Evans <kdebugs> |
Component: | general | Assignee: | KSysGuard Developers <ksysguard-bugs> |
Status: | RESOLVED FIXED | ||
Severity: | crash | CC: | admin, ahiemstra, kde, nate, nyanpasu64, plasma-bugs, wfaehnle |
Priority: | NOR | Keywords: | drkonqi |
Version: | 5.21.5 | ||
Target Milestone: | --- | ||
Platform: | Neon | ||
OS: | Linux | ||
See Also: | https://bugs.kde.org/show_bug.cgi?id=437835 | ||
Latest Commit: | https://invent.kde.org/plasma/plasma-systemmonitor/commit/e9abcd11d553bd93ed143e81d8cd3fe226faa606 | Version Fixed In: | 5.23 |
Attachments: | Screenshot of the crash's code and variable viewer in Qt Creator's debugger. |
Description
Alek Evans
2021-05-30 15:58:57 UTC
Similar to Bug 437835? *** Bug 437835 has been marked as a duplicate of this bug. *** I hope this is fixed in 5.22, the dupe is in the beta, but I made a relevant change afterwards. Please reopen if it happens after. 1. Open system monitor processes tab 2. Open yakuake or konsole and run this: while :; do /usr/bin/bash &; done 3. Wait when many processes start 4. Close yakuake or konsole 5. Observe crash in a few seconds I could not reproduce with the steps suggested Also cannot reproduce. Is anyone still seeing this in 5.22, or was it only seen in 5.21.x? I opened plasma monitor to test if i can crash it, it crashed before i ran shell command Stacktraces: https://invent.kde.org/-/snippets/1742 System monitor 5.22.3 KF 5.83.0 Qt 5.15.2+kde+r207-1 Enable tree view in process list and try to reproduce again. I can reproduce with 100% reliability. I can't reproduce it in non-tree process view. 1. Open system monitor processes tab 2. Enable tree view instead of list view 3. Open yakuake or konsole and run this: while :; do /usr/bin/bash &; done 4. Wait when many processes start 5. Close yakuake or konsole 6. Observe crash in a few seconds *** Bug 440573 has been marked as a duplicate of this bug. *** *** Bug 440579 has been marked as a duplicate of this bug. *** Created attachment 140702 [details] Screenshot of the crash's code and variable viewer in Qt Creator's debugger. Seems `QModelIndex ProcessDataModel::parent(const QModelIndex &index) const` is being passed an index whose internalPointer(), when casted to a KSysGuard::Process *, has a corrupted d-pointer with value 0x21 (close to a nullptr). I made several execution runs with debug statements in libksysguard's Process and ~Process, and found that this happens when a Process is created at addr, freed at addr, and then internalPointer() has value addr + 0x10. I also found crashes where a Process is created at addr, freed at addr, and then proc->parent() has value addr (a dangling pointer) but ProcessDataModel::parent() calls d->getQModelIndex(proc->parent(), 0) anyway. Backtrace at https://gist.github.com/nyanpasu64/0efbf70fc300203700233d1a56488453/raw/2ef75597b717c9dcf945acfa1afcf0e77b7e07d0/gistfile1.txt. I think the bug is that when a system creates a parent process with a child, it's represented as a parent and child Process. And when the parent gets killed before the child, I guess there's a race condition where sometimes the Process::d::parent pointer isn't updated to init by Process::setParent(). I also saw another crash where index.internalPointer() was null: https://gist.githubusercontent.com/nyanpasu64/0efbf70fc300203700233d1a56488453/raw/2ef75597b717c9dcf945acfa1afcf0e77b7e07d0/gistfile2.txt I didn't investigate how this happened though. I think I found the crash (or at least one cause of it). Crash call stack: #0 KSysGuard::Process::parent (this=<optimized out>) at /usr/src/debug/libksysguard-5.22.4/processcore/process.cpp:248 #1 0x00007fffe011dd76 in KSysGuard::ProcessDataModel::parent (this=<optimized out>, index=...) at /usr/src/debug/libksysguard-5.22.4/processcore/process_data_model.cpp:181 #2 0x00007ffff5d52f83 in QIdentityProxyModel::parent(QModelIndex const&) const () from /usr/lib/libQt5Core.so.5 #3 0x00007ffff5d52f83 in QIdentityProxyModel::parent(QModelIndex const&) const () from /usr/lib/libQt5Core.so.5 #4 0x00007ffff5d65b41 in ?? () from /usr/lib/libQt5Core.so.5 #5 0x00007ffff5dc8790 in ?? () from /usr/lib/libQt5Core.so.5 #6 0x00007ffff5d2a186 in QAbstractItemModel::dataChanged(QModelIndex const&, QModelIndex const&, QVector<int> const&) () from /usr/lib/libQt5Core.so.5 #7 0x00007fffe015c95b in ComponentCacheProxyModel::createPendingInstance (this=<optimized out>) at /usr/src/debug/plasma-systemmonitor-5.22.4/src/table/ComponentCacheProxyModel.cpp:126 #8 ComponentCacheProxyModel::createPendingInstance (this=0x555557efe9b0) at /usr/src/debug/plasma-systemmonitor-5.22.4/src/table/ComponentCacheProxyModel.cpp:106 #9 0x00007ffff5dbe4ff in QObject::event(QEvent*) () from /usr/lib/libQt5Core.so.5 For some reason, plasma-systemmonitor creates a ComponentCacheProxyModel (https://github.com/KDE/plasma-systemmonitor/blob/1612127fb300a71dfa187cb78128f6728e489352/src/table/ComponentCacheProxyModel.cpp) to wrap a KSysGuard::ProcessDataModel. When a ComponentCacheProxyModel receives a ComponentCacheProxyModel::data() request with role CachedComponentRole, it *queues* the QModelIndex passed in (whose internalPointer() points to a KSysGuard::Process) into ComponentCacheProxyModel::m_pendingInstances, and *schedules* a call to ComponentCacheProxyModel::createPendingInstance(). By the time createPendingInstance() is called, Processes::processesUpdated() has been called which deletes the KSysGuard::Process. createPendingInstance() calls Q_EMIT dataChanged(index, index, {CachedComponentRole}) on a QModelIndex index, whose internalPointer() is dangling, but gets used anyway (use-after-free) with unpredictable results. I think it's wrong for ComponentCacheProxyModel to be caching QModelIndex values (which are ephemeral and "borrowed" from Processes) across event loop iterations, since they can get invalidated by other event loop callbacks. I think that looking for bugs in Processes::processesUpdated() was a false lead. The code is confusing to follow and not obviously correct, but I haven't found any errors that occur in practice (d->mAbstractProcesses->getParentPid(pid) never returns a Pid not found in d->mAbstractProcesses->getAllPids()). However, Processes is *very* precarious and may segfault if mAbstractProcesses's AbstractProcesses subclass returns malformed data. A possibly relevant merge request was started @ https://invent.kde.org/plasma/plasma-systemmonitor/-/merge_requests/158 Git commit f96f1d20ee03e36bd9d59cf145bf26d15ef860d7 by Arjen Hiemstra. Committed on 24/08/2021 at 19:37. Pushed by ahiemstra into branch 'master'. Do not emit dataChange for a row that is being removed This keeps track of which process that is being removed and prevents dataChange signals from being emitted for that process, since this is something that can trip up proxy models. M +7 -2 processcore/process_data_model.cpp https://invent.kde.org/plasma/libksysguard/commit/f96f1d20ee03e36bd9d59cf145bf26d15ef860d7 Git commit e9abcd11d553bd93ed143e81d8cd3fe226faa606 by Arjen Hiemstra. Committed on 26/08/2021 at 08:06. Pushed by ahiemstra into branch 'master'. Use QPersistentModelIndex to store indices in ComponentCacheProxyModel QModelIndex becomes invalid if anything in the model it comes from changes. This can lead to crashes if the underlying item has been removed. M +3 -0 src/table/ComponentCacheProxyModel.cpp M +2 -2 src/table/ComponentCacheProxyModel.h https://invent.kde.org/plasma/plasma-systemmonitor/commit/e9abcd11d553bd93ed143e81d8cd3fe226faa606 |