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 <marc.mutz@kdab.com>
Reviewed-by: Maurice Kalinowski <maurice.kalinowski@theqtcompany.com>
This commit is contained in:
Samuel Nevala 2015-12-07 10:51:18 +02:00 committed by Maurice Kalinowski
parent b12e876efc
commit 7fdfaad43f

View File

@ -46,51 +46,72 @@
//
#include "qbytearray.h"
#include "qhash.h"
#include "qvector.h"
QT_BEGIN_NAMESPACE
// Environment ------------------------------------------------------
Q_CORE_EXPORT QHash<QByteArray, QByteArray> &qt_app_environment()
{
static QHash<QByteArray, QByteArray> 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<Variable>, 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<QByteArray, QByteArray>::const_iterator iterator = qt_app_environment().constFind(varName);
if (iterator == qt_app_environment().constEnd()) {
QVector<Variable>::const_iterator end = qt_app_environment->constEnd();
QVector<Variable>::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<QByteArray, QByteArray>::iterator iterator = qt_app_environment().find(varName);
QHash<QByteArray, QByteArray>::iterator end = qt_app_environment().end();
QVector<Variable>::iterator end = qt_app_environment->end();
QVector<Variable>::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;