-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[CLI] static deinitialization order 'fiasco' results in SEGFAULT #5166
Description
Overview
By design, the CLI tool resolves commands through the static QMap:
namespace Commands
{
QMap<QString, QSharedPointer<Command>> s_commands;
void setupCommands(bool interactive)
{
s_commands.clear();
s_commands.insert(QStringLiteral("add"), QSharedPointer<Command>(new Add()));
...
QSharedPointer<Command> getCommand(const QString& commandName)
{
return s_commands.value(commandName);
}Each Command object keeps shared pointer to current database.
This pointer takes non-null value only when the command itself is being processed (resolved through map and executed).
When running interactive shell (command 'open'), after doing some stuff and 'exit' after all, it turns out that there is the only one shared ptr to database resides in static QMap.
In turn, the 'Database' object registers its uuid in another static hash map:
QHash<QUuid, QPointer<Database>> Database::s_uuidMap;
Database::Database()
{
...
s_uuidMap.insert(m_uuid, this);
...'Database' dtor removes its uuid from the hash map:
void Database::releaseData()
{
...
s_uuidMap.remove(m_uuid);Taking it all together. Both the s_commands and s_uuidMap are static objects. Their initialization/deinitialization order is undefined.
If s_uuidMap destroyed first, then after invoking the Database dtor (through s_commands dtor) we'll get a segfault:
ASAN trace
==32064==ERROR: AddressSanitizer: heap-use-after-free on address 0x6040000111a4 at pc 0x556994c3257a bp 0x7ffd3e5483f0 sp 0x7ffd3e5483e8
READ of size 4 at 0x6040000111a4 thread T0
#0 0x556994c32579 in QHash<QUuid, QPointer<Database> >::isEmpty() const (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x1c4579)
#1 0x556994c2f2f9 in QHash<QUuid, QPointer<Database> >::remove(QUuid const&) (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x1c12f9)
#2 0x556994c275b8 in Database::releaseData() /home/anton/dev/keepassxc/src/core/Database.cpp:436
#3 0x556994c24368 in Database::~Database() /home/anton/dev/keepassxc/src/core/Database.cpp:75
#4 0x556994bdc7a9 in QtSharedPointer::ExternalRefCountWithContiguousData<Database>::deleter(QtSharedPointer::ExternalRefCountData*) /usr/include/qt5/QtCore/qsharedpointer_impl.h:255
#5 0x556994bafd23 in QtSharedPointer::ExternalRefCountData::destroy() /usr/include/qt5/QtCore/qsharedpointer_impl.h:157
#6 0x556994bb219a in QSharedPointer<Database>::deref(QtSharedPointer::ExternalRefCountData*) /usr/include/qt5/QtCore/qsharedpointer_impl.h:461
#7 0x556994bb19ea in QSharedPointer<Database>::deref() /usr/include/qt5/QtCore/qsharedpointer_impl.h:456
#8 0x556994bb0b1b in QSharedPointer<Database>::~QSharedPointer() /usr/include/qt5/QtCore/qsharedpointer_impl.h:313
#9 0x556994bb3762 in Command::~Command() /home/anton/dev/keepassxc/src/cli/Command.cpp:108
#10 0x556994bb2cd8 in DatabaseCommand::~DatabaseCommand() /home/anton/dev/keepassxc/src/cli/DatabaseCommand.h:27
#11 0x556994be4586 in Show::~Show() (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x176586)
#12 0x556994be45a1 in Show::~Show() (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x1765a1)
#13 0x556994bc48db in QtSharedPointer::CustomDeleter<Show, QtSharedPointer::NormalDeleter>::execute() (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x1568db)
#14 0x556994bc28cb in QtSharedPointer::ExternalRefCountWithCustomDeleter<Show, QtSharedPointer::NormalDeleter>::deleter(QtSharedPointer::ExternalRefCountData*) /usr/include/qt5/QtCore/qsharedpointer_impl.h:213
#15 0x556994bafd23 in QtSharedPointer::ExternalRefCountData::destroy() /usr/include/qt5/QtCore/qsharedpointer_impl.h:157
#16 0x556994bb2288 in QSharedPointer<Command>::deref(QtSharedPointer::ExternalRefCountData*) /usr/include/qt5/QtCore/qsharedpointer_impl.h:461
#17 0x556994bb1ae2 in QSharedPointer<Command>::deref() /usr/include/qt5/QtCore/qsharedpointer_impl.h:456
#18 0x556994bb0c43 in QSharedPointer<Command>::~QSharedPointer() /usr/include/qt5/QtCore/qsharedpointer_impl.h:313
#19 0x556994bc4cce in std::enable_if<QTypeInfo<QSharedPointer<Command> >::isComplex, void>::type QMapNodeBase::callDestructorIfNecessary<QSharedPointer<Command> >(QSharedPointer<Command>&) (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x156cce)
#20 0x556994bc351f in QMapNode<QString, QSharedPointer<Command> >::destroySubTree() (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x15551f)
#21 0x556994bc4d69 in QMapNode<QString, QSharedPointer<Command> >::doDestroySubTree(std::integral_constant<bool, true>) (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x156d69)
#22 0x556994bc3534 in QMapNode<QString, QSharedPointer<Command> >::destroySubTree() (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x155534)
#23 0x556994bc4d69 in QMapNode<QString, QSharedPointer<Command> >::doDestroySubTree(std::integral_constant<bool, true>) (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x156d69)
#24 0x556994bc3534 in QMapNode<QString, QSharedPointer<Command> >::destroySubTree() (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x155534)
#25 0x556994bc4d69 in QMapNode<QString, QSharedPointer<Command> >::doDestroySubTree(std::integral_constant<bool, true>) (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x156d69)
#26 0x556994bc3534 in QMapNode<QString, QSharedPointer<Command> >::destroySubTree() (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x155534)
#27 0x556994bc4d69 in QMapNode<QString, QSharedPointer<Command> >::doDestroySubTree(std::integral_constant<bool, true>) (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x156d69)
#28 0x556994bc3534 in QMapNode<QString, QSharedPointer<Command> >::destroySubTree() (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x155534)
#29 0x556994bc4d69 in QMapNode<QString, QSharedPointer<Command> >::doDestroySubTree(std::integral_constant<bool, true>) (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x156d69)
#30 0x556994bc3534 in QMapNode<QString, QSharedPointer<Command> >::destroySubTree() (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x155534)
#31 0x556994bc0385 in QMapData<QString, QSharedPointer<Command> >::destroy() (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x152385)
#32 0x556994bbdee8 in QMap<QString, QSharedPointer<Command> >::~QMap() (/home/anton/dev/keepassxc/build/src/cli/keepassxc-cli+0x14fee8)
#33 0x7fadfe936137 in __run_exit_handlers (/lib64/libc.so.6+0x3c137)
#34 0x7fadfe936189 in exit (/lib64/libc.so.6+0x3c189)
https://isocpp.org/wiki/faq/ctors#static-init-order
Steps to Reproduce
- Its better to build with sanitizer
- Run interactive shell (
opencommand) - Run some 1 or more commands, except
exit - Close the shell (by sending signal or by typing
exit/quit)
Expected Behavior
Normal exit.
Actual Behavior
Segfault.
Context
KeePassXC - Version 2.6.0-snapshot
Build Type: Snapshot
Revision: 71b05db