From 7fdfaad43f75bab9d701b0b55c13570d2140fde4 Mon Sep 17 00:00:00 2001 From: Samuel Nevala Date: Mon, 7 Dec 2015 10:51:18 +0200 Subject: [PATCH] Fix deadlock when setting environment variables. Qt uses QHash as the container for faking environment variables on Windows Runtime and CE. Environment variable manipulation functions are protected by mutex. Accessing the QT_HASH_SEED environment variable inside QHash can lead to situation where qputenv() call leads to qgetenv() call and that leads to a deadlock. Change the container from QHash to QVector to avoid deadlock. Task-number: QTBUG-49529 Change-Id: I550ead4ab12e7abebc044f52339063a44fcf0170 Reviewed-by: Marc Mutz Reviewed-by: Maurice Kalinowski --- src/corelib/kernel/qfunctions_fake_env_p.h | 51 +++++++++++++++------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/src/corelib/kernel/qfunctions_fake_env_p.h b/src/corelib/kernel/qfunctions_fake_env_p.h index 8b1ab44696..b4c5ffb079 100644 --- a/src/corelib/kernel/qfunctions_fake_env_p.h +++ b/src/corelib/kernel/qfunctions_fake_env_p.h @@ -46,51 +46,72 @@ // #include "qbytearray.h" -#include "qhash.h" +#include "qvector.h" QT_BEGIN_NAMESPACE // Environment ------------------------------------------------------ -Q_CORE_EXPORT QHash &qt_app_environment() -{ - static QHash internalEnvironment; - return internalEnvironment; -} +struct Variable { + Variable() { } + + Variable(const QByteArray &name, const QByteArray &value) + : name(name), value(value) { } + + QByteArray name; + QByteArray value; +}; + +Q_DECLARE_TYPEINFO(Variable, Q_MOVABLE_TYPE); + +struct NameEquals { + typedef bool result_type; + const char *name; + explicit NameEquals(const char *name) Q_DECL_NOTHROW : name(name) {} + result_type operator()(const Variable &other) const Q_DECL_NOTHROW + { return qstrcmp(other.name, name) == 0; } +}; + +Q_GLOBAL_STATIC(QVector, qt_app_environment) errno_t qt_fake_getenv_s(size_t *sizeNeeded, char *buffer, size_t bufferSize, const char *varName) { if (!sizeNeeded) return EINVAL; - QHash::const_iterator iterator = qt_app_environment().constFind(varName); - if (iterator == qt_app_environment().constEnd()) { + QVector::const_iterator end = qt_app_environment->constEnd(); + QVector::const_iterator iterator = std::find_if(qt_app_environment->constBegin(), + end, + NameEquals(varName)); + if (iterator == end) { if (buffer) buffer[0] = '\0'; return ENOENT; } - const int size = iterator->size() + 1; + const int size = iterator->value.size() + 1; if (bufferSize < size_t(size)) { *sizeNeeded = size; return ERANGE; } - qstrcpy(buffer, iterator->constData()); + qstrcpy(buffer, iterator->value.constData()); return 0; } errno_t qt_fake__putenv_s(const char *varName, const char *value) { - QHash::iterator iterator = qt_app_environment().find(varName); - QHash::iterator end = qt_app_environment().end(); + QVector::iterator end = qt_app_environment->end(); + QVector::iterator iterator = std::find_if(qt_app_environment->begin(), + end, + NameEquals(varName)); if (!value || !*value) { if (iterator != end) - qt_app_environment().erase(iterator); + qt_app_environment->erase(iterator); } else { if (iterator == end) - qt_app_environment()[varName] = QByteArray(value); + qt_app_environment->append(Variable(varName, value)); else - (*iterator) = value; + iterator->value = value; } return 0;