c++qtsegmentation-faultstdqfileinfo

(valid) QFileInfo causes segv when calling QFileInfo::filename() in std::sort


I need to sort a list of files based on filename. These files are gathered from a QDirIterator.

I am using the solution provided here. TL;DR - use std::sort with a QCollator.

Since QCollator::compare(QString&, QString&) uses strings, I need to get the file path or the filename (or some unique string representation of each file to sort - I need to sort based on filename)

Problem:

When sorting, I get the QFileInfo::filename() of each file, and attempt to sort based on this value. However, I reach a point where this causes a segv (see code below). When debugging, in an attempt to find the cause of the problem, (in my specific case) a reports as inaccessible (see second screenshot).

Code for MVCE:

// header
QList<QFileInfo> fileInfoImageList, fileInfoVideoList;
QDir dataDirectory; // path that contains folders of various media type

// source
QCollator collator;
collator.setNumericMode(true);
QDirIterator it(dataDirectory.path(), combinedList, QDir::Filter::Files | QDir::Filter::NoDotAndDotDot | QDir::Filter::NoSymLinks, QDirIterator::IteratorFlag::Subdirectories);

while (it.hasNext() && !stopIteratorLoading) {
   QFileInfo fi(it.next());

   if (isImage(fi)) {
        fileInfoImageList.append(fi);
   }
   else if (isVideo(fi)) {
        fileInfoVideoList.append(fi);
   }
}

std::sort(fileInfoImageList.begin(), fileInfoImageList.end(), [&collator](const QFileInfo & a, const QFileInfo & b) {
   QString nameA = a.fileName();     < ------------- offending line causing segv
   QString nameB = b.fileName();
   return collator.compare(nameA, nameB);
});

std::sort(fileInfoVideoList.begin(), fileInfoVideoList.end(), [&collator](const QFileInfo & a, const QFileInfo & b) {
   QString nameA = a.fileName();
   QString nameB = b.fileName();
   return collator.compare(nameA, nameB);
});

if (stopIteratorLoading) {
   return;
}

//...

What I can say about the file paths is they some may extend to as far as (and possibly further than 180 characters in length, but this is less that the maximum file path length limited by Windows.


Source of SEGV screenshot enter image description here

Offending line in std::sort screenshot enter image description here

Stack Trace

1 QFileInfo::fileName qfileinfo.cpp 758 0x65a1a5a 2 ExplorerDialog::<lambda()>::<lambda(const QFileInfo&, const QFileInfo&)>::operator()(const QFileInfo &, const QFileInfo &) const ExplorerView.cpp 309 0x44f30c
3 __gnu_cxx::__ops::_Iter_comp_iterExplorerDialog::refreshAggregatedFiles()::<lambda()::<lambda(const QFileInfo&, const QFileInfo&)>>::operator()<QList::iterator, QList::iterator>(QList::iterator, QList::iterator) predefined_ops.h 143 0x462e42
4 std::__unguarded_partition<QList::iterator, __gnu_cxx::__ops::_Iter_comp_iterExplorerDialog::refreshAggregatedFiles()::<lambda()::<lambda(const QFileInfo&, const QFileInfo&)>>>(QList::iterator, QList::iterator, QList::iterator, __gnu_cxx::__ops::_Iter_comp_iterExplorerDialog::refreshAggregatedFiles()::<lambda()::<lambda(const QFileInfo&, const QFileInfo&)>>) stl_algo.h 1902 0x462d2c
5 std::__unguarded_partition_pivot<QList::iterator, __gnu_cxx::__ops::_Iter_comp_iterExplorerDialog::refreshAggregatedFiles()::<lambda()::<lambda(const QFileInfo&, const QFileInfo&)>>>(QList::iterator, QList::iterator, __gnu_cxx::__ops::_Iter_comp_iterExplorerDialog::refreshAggregatedFiles()::<lambda()::<lambda(const QFileInfo&, const QFileInfo&)>>) stl_algo.h 1923 0x46198e
6 std::__introsort_loop<QList::iterator, int, __gnu_cxx::__ops::_Iter_comp_iterExplorerDialog::refreshAggregatedFiles()::<lambda()::<lambda(const QFileInfo&, const QFileInfo&)>>>(QList::iterator, QList::iterator, int, __gnu_cxx::__ops::_Iter_comp_iterExplorerDialog::refreshAggregatedFiles()::<lambda()::<lambda(const QFileInfo&, const QFileInfo&)>>) stl_algo.h 1952 0x45fed6
7 std::__sort<QList::iterator, __gnu_cxx::__ops::_Iter_comp_iterExplorerDialog::refreshAggregatedFiles()::<lambda()::<lambda(const QFileInfo&, const QFileInfo&)>>>(QList::iterator, QList::iterator, __gnu_cxx::__ops::_Iter_comp_iterExplorerDialog::refreshAggregatedFiles()::<lambda()::<lambda(const QFileInfo&, const QFileInfo&)>>) stl_algo.h 1968 0x45eadd
8 std::sort<QList::iterator, ExplorerDialog::refreshAggregatedFiles()::<lambda()>::<lambda(const QFileInfo&, const QFileInfo&)>>(QList::iterator, QList::iterator, ExplorerDialog::<lambda()>::<lambda(const QFileInfo&, const QFileInfo&)>) stl_algo.h 4868 0x45d9be
9 ExplorerDialog::<lambda()>::operator()(void) const ExplorerView.cpp 308 0x44f673
10 std::_Function_handler<void(), ExplorerDialog::refreshAggregatedFiles()::<lambda()>>::_M_invoke(const std::_Any_data &) std_function.h 316 0x45ec7b
11 std::function<void ()>::operator()() const std_function.h 706 0x525442


Solution

  • QCollater::compare(s1, s2) returns "an integer less than, equal to, or greater than zero depending on whether s1 sorts before, with or after s2", hence your comparator doesn't obey strict weak ordering -- it's effectively checking the relationship 's1 != s2' rather than 's1 < s2'. That's undefined behaviour.

    Try changing the sort comparator to...

    std::sort(fileInfoImageList.begin(), fileInfoImageList.end(),
              [&collator](const QFileInfo &a, const QFileInfo &b)
              {
                  QString nameA = a.fileName();
                  QString nameB = b.fileName();
                  return collator.compare(nameA, nameB) < 0;
              });