Priority: P2: Important
Affects Version/s: 4.8.7, 5.9.1, 5.10.0
Fix Version/s: None
Component/s: GUI: Text handling
I just hit a bad memory corruption bug in QTextDocument. Investigating it took me quite a lot of time, because it manifested in a crashing QFileSystemWatcher at first, misleading me completely.
Now I fully understand what is happening:
The problem is a slot I connected to QTextDocument (very indirectly through QTextEdit->verticalScrollbar rangeChanged event).
This slot is called when QTextDocumentPrivate::clear executes QTextDocumentPrivate::init.
In the slot I adjusted the cursor of the QTextEdit (QTextEdit::moveCursor).
The problem here is the following:
QTextDocumentPrivate::clear stores the list of cursors private data associated to the document in oldCursors and clears the list.
Now the slot is called and calls QTextEdit::moveCursor which calls QTextControl::moveCursor.
QTextControl has a member cursor, that has a reference to one of the cursors in QTextDocumentPrivate:cursors. This cursor gets deleted later. Normally the deletion of a cursor also removes it from QTextDocumentPrivate::cursors, but since the list was cleared and copied, it is not removed.
Back in QTextDocumentPrivate::clear the old list of cursors is restored with a pointer to a now freed or already newly allocated memory.
In the best case the next change to the cursors of the textdocument result in an asssertion in QFragmentMap::setRoot. In the worst case it passes all assertions and just writes to memory, that belongs to someone else, like in the case where I first encountered the bug.
In the attached example I tried to reduce the required code and use classes to a minimum of layers. Therefore I just used a QTextDocument directly and created a cursor to the document on the heap, to mimic the persistent cursor in QTextWidget that gets delete during slot evaluation.
The sample shows how the bug could lead to memory corruption, by accquiring the memory released from the cursor in the slot (this could very well be in another thread). After the QTextDocument::clear was executed, QTextDocument::setPlainText is called, which manipulates all cursors associated with the document. After this call, the allocated memory is corrupted.