I got a crash in Qt Creator. I have a crash dump, but have not been able to reproduce the crash. I think I was doing the following steps around the time:
- edit cmakelists and save
- qt reruns cmake
- I minimize the window
- qt creator runs the completion engine? idk.
This is not a reproducible crash, but an interesting one I decided to spend several days debugging. I debugged the crashed program in Visual Studio, and took a "minidump with heap". Problem is it's huge and contains lots of unrelated stuff.
Unfortunately the crash dump is 652MB and I can't figure out how to remove the heap. Dump file (compressed via 7z) at https://drive.google.com/file/d/1AOb1km7pDduQTYw1NpZi6kHqZQj8WVOg/view?usp=sharing.
The backtrace is as follows:
> Qt5Core.dll!QString::QString(const QString & other) Line 504 C++
CMakeProjectManager4.dll!QList<QString>::node_copy(QList<QString>::Node * from, QList<QString>::Node * to, QList<QString>::Node * src) Line 506 C++
CMakeProjectManager4.dll!QList<QString>::operator+=(const QList<QString> & l) Line 988 C++
[Inline Frame] CMakeProjectManager4.dll!QList<QString>::append(const QList<QString> &) Line 1003 C++
CMakeProjectManager4.dll!CMakeProjectManager::CMakeTool::keywords() Line 315 C++
CMakeProjectManager4.dll!CMakeProjectManager::Internal::CMakeFileCompletionAssist::perform(const TextEditor::AssistInterface * interface) Line 70 C++
TextEditor4.dll!TextEditor::Internal::ProcessorRunner::run() Line 52 C++
Note that this is actually a QStringList, variable IntrospectionData::m_variables.
Going to the operator+= stack frame (link to code)...
- d->ref.atomic has value 1, so isShared() should return false, and p.append(l.p) should be called.
- Probably void **QListData::append(int n) is called (https://code.woboq.org/qt5/qtbase/src/corelib/tools/qlist.cpp.html#_ZN9QListData6appendEi)...
- Probably void QListData::realloc_grow(int growth) is called (https://code.woboq.org/qt5/qtbase/src/corelib/tools/qlist.cpp.html#_ZN9QListData12realloc_growEi), otherwise there wouldn't be two QString with the same d-ptr...
- Somewhere along the line, one of these functions malfunctioned, and the returned value doesn't point into this->d->array anymore.
- Whether detach_helper_grow or append is called, d may be modified to point to a new block of memory. Either way, the previously initialized elements should have been moved to [p.begin() .. n), and there should be room for more elements between [n .. p.end()).
- Problem is, n and p point to different blocks of memory!
Loading the minidump in windbg (not windbg preview) and looking at the assembly code (I'm not an assembly expert)...
64396e83 8bcf mov ecx,edi
64396e85 c745fc00000000 mov dword ptr [ebp-4],0
64396e8c ff15a8a14064 call dword ptr [CMakeProjectManager4!_imp_?beginQListDataQBEPAPAXXZ (6440a1a8)]
64396e92 50 push eax // pushes l.p.begin() as argument node_copy(src)
- stack: 078afb38 1d2e5880
- &l->d->array also has value 0x1d2e5880.
64396e93 8bce mov ecx,esi
64396e95 ff15a4a14064 call dword ptr [CMakeProjectManager4!_imp_?endQListDataQBEPAPAXXZ (6440a1a4)]
64396e9b 50 push eax // pushes p.end() as argument node_copy(to)
- stack: 078afb34 17f96040
- But d->array + d->end has value 0x17fa5fc8 which does not match the stack!!!
64396e9c 53 push ebx // pushes local variable n as argument node_copy(from)
- stack: 078afb30 22954c44
- Local variable n also has value 22954c44.
64396e9d 8bce mov ecx,esi
64396e9f e8ac12ffff call CMakeProjectManager4!QList<QString>::node_copy (64388150)
- stack: 078afb2c 64396ea4 CMakeProjectManager4!QList<QString>::operator+=+0xd4 [c:\users\qt\work\build\qt5_install_dir\include\qtcore\qlist.h @ 988]
Why does n not point into this? Did append() reallocate the memory but forget to update this.p? Or did it update this.p but return a pointer to the old memory? Is there a race condition where two threads are modifying the same QList but failed to increment the refcount? Is there memory corruption?
- CMakeTool *cmake = CMakeKitAspect::cmakeTool(p->activeTarget()->kit());
- static CMakeTool *cmakeTool(const ProjectExplorer::Kit *k);
- return CMakeToolManager::findById(cmakeToolId(k));
- kw = cmake->keywords();
- There might be a race condition where two threads are accessing this CMakeTool concurrently, one thread is rerunning CMake while the other thread is trying to process text editor completion, but the QList's "copy refcount" is only 1. However I can't find any other threads doing CMake-related work.
Both this and n seem to point to valid-looking memory, and n points to more of it!
Debugging in Visual Studio:
- (QString*)(&*(n-575)),2000 contains valid strings, then this's strings, then a single "", then l's strings, then gibberish.
- The data before n is quite different from the data found in this. The entries aren't identical, and the ordering is different.
- (QString*)(&*n),2000 contains l's strings, then gibberish.
What does this hold?
- &(*this), 2000 has value 0x17fa4ca0, which equals d->array.
- Both this and &(*this), 2000 start with a single empty string.
Where did n and this.d originate from? They seem to be copies of each other.
- (QString*)(&*(n-2)) is "XCTEST" with d-pointer 0x10e04bb0.
- (*this) is also "XCTEST" and shares the same d-pointer (0x10e04bb0)!
- It has a refcount of 1, not 2.
Was this->size() modified? Was (*this)[...] modified?
Is there a race condition where two threads mutate one CMakeTool concurrently? Is there a bug in QList's appending functions? (I didn't poke through the assembly yet to find out.) Is there some other memory corruption?