Skip to content

Fix random crash in ItemPrivate copy ctor due to QHash rehashing

Daniel Vrátil requested to merge work/dvratil/fix-crash-itemchangelog into master

The bug was triggered by those statements in ItemPrivate's copy constructor:

auto *changelog = ItemChangeLog::instance();
changelog->addedFlags(this) = changelog->addedFlags(&other);

ItemChangeLog::addedFlags(ItemPrivate *priv) is a non-const method that simply does:

return m_hashTable[priv];

and returns a non-const reference to the value. Since it's non-const, if the key is not present in the hash table, it is inserted and a reference to the newly-inserted value is returned.

The right-hand-side of the assignment is evaluated first, obtaining a reference to the value for a key matching &other. Then, the left-hand-side of the assignment is evaluated. When value of this does not exist in the underlying hash table it is inserted, possibly triggering a rehashing of the table. When the rehashing happens, the reference on the right-hand-side of the assignment is invalidated and we end up with a dangling reference, and a crash later on when we try to access it.

This fix introduces a const overload for addedFlags() (and all other ItemChangeLog methods) that returns the result by value rather than by reference, so that even when the left-hand-side of the assignment operation triggers rehashing, the value is not affected.

I started seeing those crashes randomly since I switched to Qt6. The randomness is caused by the fact that the key to the hash table is a pointer, so triggerring the rehashing depended on the memory address assigned to the ItemPrivate. The fact that it started happening in Qt6 is likely due to a change in QHash implementation in Qt6.

Merge request reports