Application: kmymoney (5.1.3) Qt Version: 5.15.8 Frameworks Version: 5.102.0 Operating System: Linux 6.1.7-1-default x86_64 Windowing System: X11 Distribution: "openSUSE Tumbleweed" DrKonqi: 5.26.5 [KCrashBackend] -- Information about the crash: When importing valid CSV file with transaction details app crashes when clicking next on screen with columns assignment selection. Nothing special is contained in CSV file. The crash can be reproduced every time. -- Backtrace: Application: KMyMoney (kmymoney), signal: Segmentation fault [KCrash Handler] #4 QStandardItem::text() const (this=0x0) at /usr/include/qt5/QtGui/qstandarditemmodel.h:75 #5 0x00007f130fe90429 in CSVImporterCore::sortSecurities(QSet<QString>&, QSet<QString>&, QMap<QString, QString>&) (this=0x7f12ec005b40, onlySymbols=..., onlyNames=..., mapSymbolName=...) at /usr/src/debug/kmymoney-5.1.3/kmymoney/plugins/csv/import/core/csvimportercore.cpp:1306 #6 0x00007f12e40b3369 in InvestmentPage::validateSecurities() (this=0x55fd100b8820) at /usr/src/debug/kmymoney-5.1.3/kmymoney/plugins/csv/import/investmentwizardpage.cpp:492 #7 InvestmentPage::validatePage() (this=0x55fd100b8820) at /usr/src/debug/kmymoney-5.1.3/kmymoney/plugins/csv/import/investmentwizardpage.cpp:161 #8 0x00007f130edf2b52 in QWizard::next() (this=0x55fd0fed1ef0) at dialogs/qwizard.cpp:3140 #9 0x00007f130df1355c in doActivate<false>(QObject*, int, void**) (sender=0x55fd0ff82300, signal_index=9, argv=0x7ffe0de864e0) at kernel/qobject.cpp:3935 #10 0x00007f130df0c75f in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (sender=sender@entry=0x55fd0ff82300, m=m@entry=0x7f130f0be160, local_signal_index=local_signal_index@entry=2, argv=argv@entry=0x7ffe0de864e0) at kernel/qobject.cpp:3983 #11 0x00007f130ec97212 in QAbstractButton::clicked(bool) (this=this@entry=0x55fd0ff82300, _t1=<optimized out>) at .moc/moc_qabstractbutton.cpp:308 #12 0x00007f130ec9747a in QAbstractButtonPrivate::emitClicked() (this=0x55fd0fef35b0) at widgets/qabstractbutton.cpp:416 #13 0x00007f130ec98d18 in QAbstractButtonPrivate::click() (this=0x55fd0fef35b0) at widgets/qabstractbutton.cpp:409 #14 0x00007f130ec98f37 in QAbstractButton::mouseReleaseEvent(QMouseEvent*) (this=0x55fd0ff82300, e=0x7ffe0de86a60) at widgets/qabstractbutton.cpp:1045 #15 0x00007f130ebe6d78 in QWidget::event(QEvent*) (this=0x55fd0ff82300, event=0x7ffe0de86a60) at kernel/qwidget.cpp:9045 #16 0x00007f130eba544e in QApplicationPrivate::notify_helper(QObject*, QEvent*) (this=this@entry=0x55fd0f36e310, receiver=receiver@entry=0x55fd0ff82300, e=e@entry=0x7ffe0de86a60) at kernel/qapplication.cpp:3640 #17 0x00007f130ebad972 in QApplication::notify(QObject*, QEvent*) (this=<optimized out>, receiver=0x55fd0ff82300, e=<optimized out>) at kernel/qapplication.cpp:3084 #18 0x00007f130dedc138 in QCoreApplication::notifyInternal2(QObject*, QEvent*) (receiver=0x55fd0ff82300, event=0x7ffe0de86a60) at kernel/qcoreapplication.cpp:1064 #19 0x00007f130ebabaee in QApplicationPrivate::sendMouseEvent(QWidget*, QMouseEvent*, QWidget*, QWidget*, QWidget**, QPointer<QWidget>&, bool, bool) (receiver=receiver@entry=0x55fd0ff82300, event=event@entry=0x7ffe0de86a60, alienWidget=<optimized out>, nativeWidget=0x55fd0fefbd70, buttonDown=buttonDown@entry=0x7f130f0f0330 <qt_button_down>, lastMouseReceiver=..., spontaneous=true, onlyDispatchEnterLeave=false) at kernel/qapplication.cpp:2622 #20 0x00007f130ebffb98 in QWidgetWindow::handleMouseEvent(QMouseEvent*) (this=0x55fd0ffcec60, event=0x7ffe0de86d10) at kernel/qwidgetwindow.cpp:683 #21 0x00007f130ec030f0 in QWidgetWindow::event(QEvent*) (this=0x55fd0ffcec60, event=0x7ffe0de86d10) at kernel/qwidgetwindow.cpp:300 #22 0x00007f130eba544e in QApplicationPrivate::notify_helper(QObject*, QEvent*) (this=<optimized out>, receiver=0x55fd0ffcec60, e=0x7ffe0de86d10) at kernel/qapplication.cpp:3640 #23 0x00007f130dedc138 in QCoreApplication::notifyInternal2(QObject*, QEvent*) (receiver=0x55fd0ffcec60, event=0x7ffe0de86d10) at kernel/qcoreapplication.cpp:1064 #24 0x00007f130e37b89d in QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent*) (e=0x55fd101e5210) at kernel/qguiapplication.cpp:2285 #25 0x00007f130e34f26c in QWindowSystemInterface::sendWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>) (flags=flags@entry=...) at kernel/qwindowsysteminterface.cpp:1169 #26 0x00007f12fa91914a in xcbSourceDispatch(GSource*, GSourceFunc, gpointer) (source=<optimized out>) at qxcbeventdispatcher.cpp:105 #27 0x00007f1303b1ba90 in g_main_context_dispatch () at /lib64/libglib-2.0.so.0 #28 0x00007f1303b1be48 in () at /lib64/libglib-2.0.so.0 #29 0x00007f1303b1bedc in g_main_context_iteration () at /lib64/libglib-2.0.so.0 #30 0x00007f130df33b66 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (this=0x55fd0f4503d0, flags=...) at kernel/qeventdispatcher_glib.cpp:423 #31 0x00007f130dedabab in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) (this=this@entry=0x7ffe0de87040, flags=..., flags@entry=...) at ../../include/QtCore/../../src/corelib/global/qflags.h:69 #32 0x00007f130edad827 in QDialog::exec() (this=0x55fd0fefbd70) at ../../include/QtCore/../../src/corelib/global/qflags.h:121 #33 0x00007f12e409d069 in CSVImporter::import(QString const&) (this=0x55fd0f986bb0, filename=...) at /usr/src/debug/kmymoney-5.1.3/kmymoney/plugins/csv/import/csvimporter.cpp:104 #34 0x00007f12e40950de in CSVImporter::startWizardRun() (this=<optimized out>) at /usr/src/debug/kmymoney-5.1.3/kmymoney/plugins/csv/import/csvimporter.cpp:95 #35 0x00007f130df132dd in QtPrivate::QSlotObjectBase::call(QObject*, void**) (a=0x7ffe0de87210, r=0x55fd0f986bb0, this=0x55fd0f9c34a0) at ../../include/QtCore/../../src/corelib/kernel/qobjectdefs_impl.h:398 #36 doActivate<false>(QObject*, int, void**) (sender=0x55fd0f927240, signal_index=4, argv=0x7ffe0de87210) at kernel/qobject.cpp:3923 #37 0x00007f130df0c75f in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (sender=sender@entry=0x55fd0f927240, m=m@entry=0x7f130f0b8d20, local_signal_index=local_signal_index@entry=1, argv=argv@entry=0x7ffe0de87210) at kernel/qobject.cpp:3983 #38 0x00007f130eb9ebb2 in QAction::triggered(bool) (this=this@entry=0x55fd0f927240, _t1=<optimized out>) at .moc/moc_qaction.cpp:376 #39 0x00007f130eba16fb in QAction::activate(QAction::ActionEvent) (this=0x55fd0f927240, event=<optimized out>) at kernel/qaction.cpp:1161 #40 0x00007f130ed26392 in QMenuPrivate::activateCausedStack(QVector<QPointer<QWidget> > const&, QAction*, QAction::ActionEvent, bool) (this=this@entry=0x55fd0fa2dbc0, causedStack=..., action=action@entry=0x55fd0f927240, action_e=action_e@entry=QAction::Trigger, self=self@entry=true) at widgets/qmenu.cpp:1384 #41 0x00007f130ed2e1f4 in QMenuPrivate::activateAction(QAction*, QAction::ActionEvent, bool) (this=0x55fd0fa2dbc0, action=0x55fd0f927240, action_e=QAction::Trigger, self=<optimized out>) at widgets/qmenu.cpp:1461 #42 0x00007f130ebe6d78 in QWidget::event(QEvent*) (this=0x55fd0f823a60, event=0x7ffe0de877f0) at kernel/qwidget.cpp:9045 #43 0x00007f130eba544e in QApplicationPrivate::notify_helper(QObject*, QEvent*) (this=this@entry=0x55fd0f36e310, receiver=receiver@entry=0x55fd0f823a60, e=e@entry=0x7ffe0de877f0) at kernel/qapplication.cpp:3640 #44 0x00007f130ebad972 in QApplication::notify(QObject*, QEvent*) (this=<optimized out>, receiver=0x55fd0f823a60, e=<optimized out>) at kernel/qapplication.cpp:3084 #45 0x00007f130dedc138 in QCoreApplication::notifyInternal2(QObject*, QEvent*) (receiver=0x55fd0f823a60, event=0x7ffe0de877f0) at kernel/qcoreapplication.cpp:1064 #46 0x00007f130ebabaee in QApplicationPrivate::sendMouseEvent(QWidget*, QMouseEvent*, QWidget*, QWidget*, QWidget**, QPointer<QWidget>&, bool, bool) (receiver=0x55fd0f823a60, event=event@entry=0x7ffe0de877f0, alienWidget=<optimized out>, nativeWidget=0x55fd0f823a60, buttonDown=buttonDown@entry=0x7f130f0f0330 <qt_button_down>, lastMouseReceiver=..., spontaneous=true, onlyDispatchEnterLeave=false) at kernel/qapplication.cpp:2622 #47 0x00007f130ebfffe5 in QWidgetWindow::handleMouseEvent(QMouseEvent*) (this=0x7f12ec007fd0, event=0x7ffe0de87aa0) at kernel/qwidgetwindow.cpp:580 #48 0x00007f130ec030f0 in QWidgetWindow::event(QEvent*) (this=0x7f12ec007fd0, event=0x7ffe0de87aa0) at kernel/qwidgetwindow.cpp:300 #49 0x00007f130eba544e in QApplicationPrivate::notify_helper(QObject*, QEvent*) (this=<optimized out>, receiver=0x7f12ec007fd0, e=0x7ffe0de87aa0) at kernel/qapplication.cpp:3640 #50 0x00007f130dedc138 in QCoreApplication::notifyInternal2(QObject*, QEvent*) (receiver=0x7f12ec007fd0, event=0x7ffe0de87aa0) at kernel/qcoreapplication.cpp:1064 #51 0x00007f130e37b89d in QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent*) (e=0x55fd0ff62fa0) at kernel/qguiapplication.cpp:2285 #52 0x00007f130e34f26c in QWindowSystemInterface::sendWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>) (flags=flags@entry=...) at kernel/qwindowsysteminterface.cpp:1169 #53 0x00007f12fa91914a in xcbSourceDispatch(GSource*, GSourceFunc, gpointer) (source=<optimized out>) at qxcbeventdispatcher.cpp:105 #54 0x00007f1303b1ba90 in g_main_context_dispatch () at /lib64/libglib-2.0.so.0 #55 0x00007f1303b1be48 in () at /lib64/libglib-2.0.so.0 #56 0x00007f1303b1bedc in g_main_context_iteration () at /lib64/libglib-2.0.so.0 #57 0x00007f130df33b66 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (this=0x55fd0f4503d0, flags=...) at kernel/qeventdispatcher_glib.cpp:423 #58 0x00007f130dedabab in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) (this=this@entry=0x7ffe0de87dd0, flags=..., flags@entry=...) at ../../include/QtCore/../../src/corelib/global/qflags.h:69 #59 0x00007f130dee2d16 in QCoreApplication::exec() () at ../../include/QtCore/../../src/corelib/global/qflags.h:121 #60 0x000055fd0e1843eb in main(int, char**) (argc=<optimized out>, argv=<optimized out>) at /usr/src/debug/kmymoney-5.1.3/kmymoney/main.cpp:305 [Inferior 1 (process 14098) detached] Reported using DrKonqi
Could it be, that the selection for the symbol column is out of range? Can you attach the file $HOME/.config/kmymoney/csvimporterrc (the most personal info in there are your profile names) and a sample CSV file that shows the crash here (does not need to be real data - it should only reproduce the crash)?
Created attachment 155756 [details] csvimporterrc
Created attachment 155757 [details] CSV to import
I have reproduced the crash with a new user using the csvimporterrc from comment2. Running in a debugger, in CSVImporterCore::sortSecurities, the row number starts out negative, even when I set the start and end rows to import, so I suspect something is getting invalid data, and the crash seems to be in the next call into some qt internal routine (which I don't currently have symbols for.) Can you try finding a csv file which imports with the same profile successfully, and then see if this csv file still causes the crash?
I did two things to get a successful import. First, I edited the csvimporterrc and changed StartLine to 1 and TrailerLines to 0, and I created a new col identical to the first col so I could specify both a symbol and name col, as I don't have any of these securities in the test data file. I haven't fully followed the code, but I wonder if m_profile->m_startLine is somehow getting mis-set to a negative number.
Looking at the following two parameters in the rc file StartLine=2 TrailerLines=12 and the code in CSVFile::getStartEndRow(CSVProfile *profile) explains how m_startLine can become negative if the number of rows is less than 12 (-7 using the files attached). Note: this is what I found out by code reading not executing.
I wonder whether it is StartLine (or m_profile->m_startline) being negative that leads to the crash. The importer has plenty of loops with "for (int row = m_profile->m_startLine; row <= m_profile->m_endLine; ++row)" which use row to find a value (such as symbol and/or name in CSVImporterCore::sortSecurities) essentially leading to an out-of-bounds issue. Might it be necessary to confirm that m_startLine and m_endLine are reasonable before any of those loops?
I would not want to check that before every loop but in a central spot. If start is -7 and end is -1 then the for loop still gets executed because -7 is less than -1. The crash happens on this line symbol = m_file->m_model->item(row, symbolCol)->text().trimmed(); and with row being negative, m_file->m_model->item() will definitely return a null pointer. OTOH, it could be that the item does not even exist. In this case, item() also returns a null pointer according to the Qt docs.
I agree a central check is good - perhaps whenever those values get changed. I don't think this crash is simply because item() returns a null pointer, but because something crashes in the depths of Qt during the call to item(). I'm recompiling Qt with debug symbols, although even if we see what actually triggers the crash, I don't think it will change the conclusion. I can't find any documentation that says whether QStandardItemModel allows negative row numbers.
Even though the csv import wizard clearly shows Start Line as 1 and End Line as 6, within sortLines m_startLine is -2 (from csvimporterrc) and m_endLine is 5 (which I assume is correct after changing 1 based counting for humans to 0 based counting internally.) I'm still trying to figure out why Qt crashes instead of just returning a nullpointer for the invalid row, and why the row seems to be wrong in the first place.
From my reading of Qt code, QStandardItemModel should not allow negative rows (or columns) but this is apparently only checked on creation, not retrieval. However, I'm still unable find why it crashes instead of just returning a nullpointer. I'll try to get an answer on a qt list or forum, but will now try to create an MR, following from Comment #6.
QStandardItem has no problem. Looking at the codeline in question symbol = m_file->m_model->item(row, symbolCol)->text().trimmed(); the first part "m_file->m_model->item(row, symbolCol)" returns a nullpointer which is then used to call the "->text()" member with (this=0x0). That is clearly identified in the stacktrace as #4 QStandardItem::text() const (this=0x0) at /usr/include/qt5/QtGui/qstandarditemmodel.h:75 #5 0x00007f130fe90429 in CSVImporterCore::sortSecurities(QSet<QString>&, QSet<QString>&, QMap<QString, QString>&) (this=0x7f12ec005b40, onlySymbols=..., onlyNames=..., mapSymbolName=...) at /usr/src/debug/kmymoney-5.1.3/kmymoney/plugins/csv/import/core/csvimportercore.cpp:1306 Calling text() via an invalid (null-) pointer is the problem and that is in our code. No need to get answers from other places when we already have them.
OK, my c++ reading ability is far worse than I thought. Even debugging through the Qt code, I somehow missed the switch from the call to item() to the call to text(). Adding a check before calling text() would certainly work - but I'll have to look at how many of them there are. If more than just a few, might it still be better to assure the starting row is not negative?
Git commit a8a631dc3f0a85a5594674c05a461a534a6a900a by Jack Ostroff. Committed on 21/02/2023 at 22:17. Pushed by ostroffjh into branch '5.1'. Prevent crash in csv importer FIXED-IN: 5.1.4 M +18 -8 kmymoney/plugins/csv/import/core/csvimportercore.cpp https://invent.kde.org/office/kmymoney/commit/a8a631dc3f0a85a5594674c05a461a534a6a900a