Fix crashes when accessing environment variables concurrently
We've seen crashes with QThreadPrivate::start using qgetenv during the creation of the event dispatcher, while another thread (for example the gui thread) called qputenv. This is inherently thread-unsafe and there are many places where we make the assumption that using the environment is safe. However access to the environment is inherently unsafe in the C runtime and the best that we can do is add a mutex around the Qt environment access functions, to at least protect ourselves and our users. Change-Id: Ie9a718d9f7ce63c423c645f0be3e3f4933e1cb08 Reviewed-by: Marc Mutz <marc.mutz@kdab.com> Reviewed-by: Jørgen Lind <jorgen.lind@theqtcompany.com>
This commit is contained in:
parent
4b9cf0379f
commit
8272f5415b
@ -42,6 +42,7 @@
|
|||||||
#include <private/qlocale_tools_p.h>
|
#include <private/qlocale_tools_p.h>
|
||||||
|
|
||||||
#include <private/qsystemlibrary_p.h>
|
#include <private/qsystemlibrary_p.h>
|
||||||
|
#include <qmutex.h>
|
||||||
|
|
||||||
#ifndef QT_NO_QOBJECT
|
#ifndef QT_NO_QOBJECT
|
||||||
#include <private/qthread_p.h>
|
#include <private/qthread_p.h>
|
||||||
@ -3029,6 +3030,10 @@ QString qt_error_string(int errorCode)
|
|||||||
return ret.trimmed();
|
return ret.trimmed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In the C runtime on all platforms access to the environment is not thread-safe. We
|
||||||
|
// add thread-safety for the Qt wrappers.
|
||||||
|
static QBasicMutex environmentMutex;
|
||||||
|
|
||||||
// getenv is declared as deprecated in VS2005. This function
|
// getenv is declared as deprecated in VS2005. This function
|
||||||
// makes use of the new secure getenv function.
|
// makes use of the new secure getenv function.
|
||||||
/*!
|
/*!
|
||||||
@ -3046,6 +3051,7 @@ QString qt_error_string(int errorCode)
|
|||||||
*/
|
*/
|
||||||
QByteArray qgetenv(const char *varName)
|
QByteArray qgetenv(const char *varName)
|
||||||
{
|
{
|
||||||
|
QMutexLocker locker(&environmentMutex);
|
||||||
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||||
size_t requiredSize = 0;
|
size_t requiredSize = 0;
|
||||||
QByteArray buffer;
|
QByteArray buffer;
|
||||||
@ -3079,6 +3085,7 @@ QByteArray qgetenv(const char *varName)
|
|||||||
*/
|
*/
|
||||||
bool qEnvironmentVariableIsEmpty(const char *varName) Q_DECL_NOEXCEPT
|
bool qEnvironmentVariableIsEmpty(const char *varName) Q_DECL_NOEXCEPT
|
||||||
{
|
{
|
||||||
|
QMutexLocker locker(&environmentMutex);
|
||||||
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||||
// we provide a buffer that can only hold the empty string, so
|
// we provide a buffer that can only hold the empty string, so
|
||||||
// when the env.var isn't empty, we'll get an ERANGE error (buffer
|
// when the env.var isn't empty, we'll get an ERANGE error (buffer
|
||||||
@ -3110,6 +3117,7 @@ bool qEnvironmentVariableIsEmpty(const char *varName) Q_DECL_NOEXCEPT
|
|||||||
*/
|
*/
|
||||||
int qEnvironmentVariableIntValue(const char *varName, bool *ok) Q_DECL_NOEXCEPT
|
int qEnvironmentVariableIntValue(const char *varName, bool *ok) Q_DECL_NOEXCEPT
|
||||||
{
|
{
|
||||||
|
QMutexLocker locker(&environmentMutex);
|
||||||
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||||
// we provide a buffer that can hold any int value:
|
// we provide a buffer that can hold any int value:
|
||||||
static const int NumBinaryDigitsPerOctalDigit = 3;
|
static const int NumBinaryDigitsPerOctalDigit = 3;
|
||||||
@ -3158,6 +3166,7 @@ int qEnvironmentVariableIntValue(const char *varName, bool *ok) Q_DECL_NOEXCEPT
|
|||||||
*/
|
*/
|
||||||
bool qEnvironmentVariableIsSet(const char *varName) Q_DECL_NOEXCEPT
|
bool qEnvironmentVariableIsSet(const char *varName) Q_DECL_NOEXCEPT
|
||||||
{
|
{
|
||||||
|
QMutexLocker locker(&environmentMutex);
|
||||||
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||||
size_t requiredSize = 0;
|
size_t requiredSize = 0;
|
||||||
(void)getenv_s(&requiredSize, 0, 0, varName);
|
(void)getenv_s(&requiredSize, 0, 0, varName);
|
||||||
@ -3187,6 +3196,7 @@ bool qEnvironmentVariableIsSet(const char *varName) Q_DECL_NOEXCEPT
|
|||||||
*/
|
*/
|
||||||
bool qputenv(const char *varName, const QByteArray& value)
|
bool qputenv(const char *varName, const QByteArray& value)
|
||||||
{
|
{
|
||||||
|
QMutexLocker locker(&environmentMutex);
|
||||||
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||||
return _putenv_s(varName, value.constData()) == 0;
|
return _putenv_s(varName, value.constData()) == 0;
|
||||||
#elif (defined(_POSIX_VERSION) && (_POSIX_VERSION-0) >= 200112L) || defined(Q_OS_HAIKU)
|
#elif (defined(_POSIX_VERSION) && (_POSIX_VERSION-0) >= 200112L) || defined(Q_OS_HAIKU)
|
||||||
@ -3217,6 +3227,7 @@ bool qputenv(const char *varName, const QByteArray& value)
|
|||||||
*/
|
*/
|
||||||
bool qunsetenv(const char *varName)
|
bool qunsetenv(const char *varName)
|
||||||
{
|
{
|
||||||
|
QMutexLocker locker(&environmentMutex);
|
||||||
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||||
return _putenv_s(varName, "") == 0;
|
return _putenv_s(varName, "") == 0;
|
||||||
#elif (defined(_POSIX_VERSION) && (_POSIX_VERSION-0) >= 200112L) || defined(Q_OS_BSD4) || defined(Q_OS_HAIKU)
|
#elif (defined(_POSIX_VERSION) && (_POSIX_VERSION-0) >= 200112L) || defined(Q_OS_BSD4) || defined(Q_OS_HAIKU)
|
||||||
|
@ -405,7 +405,7 @@ void QProcessPrivate::startProcess()
|
|||||||
char **path = 0;
|
char **path = 0;
|
||||||
int pathc = 0;
|
int pathc = 0;
|
||||||
if (!program.contains(QLatin1Char('/'))) {
|
if (!program.contains(QLatin1Char('/'))) {
|
||||||
const QString pathEnv = QString::fromLocal8Bit(::getenv("PATH"));
|
const QString pathEnv = QString::fromLocal8Bit(qgetenv("PATH"));
|
||||||
if (!pathEnv.isEmpty()) {
|
if (!pathEnv.isEmpty()) {
|
||||||
QStringList pathEntries = pathEnv.split(QLatin1Char(':'), QString::SkipEmptyParts);
|
QStringList pathEntries = pathEnv.split(QLatin1Char(':'), QString::SkipEmptyParts);
|
||||||
if (!pathEntries.isEmpty()) {
|
if (!pathEntries.isEmpty()) {
|
||||||
@ -1187,7 +1187,7 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
|
|||||||
argv[arguments.size() + 1] = 0;
|
argv[arguments.size() + 1] = 0;
|
||||||
|
|
||||||
if (!program.contains(QLatin1Char('/'))) {
|
if (!program.contains(QLatin1Char('/'))) {
|
||||||
const QString path = QString::fromLocal8Bit(::getenv("PATH"));
|
const QString path = QString::fromLocal8Bit(qgetenv("PATH"));
|
||||||
if (!path.isEmpty()) {
|
if (!path.isEmpty()) {
|
||||||
QStringList pathEntries = path.split(QLatin1Char(':'));
|
QStringList pathEntries = path.split(QLatin1Char(':'));
|
||||||
for (int k = 0; k < pathEntries.size(); ++k) {
|
for (int k = 0; k < pathEntries.size(); ++k) {
|
||||||
|
@ -1048,12 +1048,12 @@ static void initDefaultPaths(QMutexLocker *locker)
|
|||||||
// Non XDG platforms (OS X, iOS, Blackberry, Android...) have used this code path erroneously
|
// Non XDG platforms (OS X, iOS, Blackberry, Android...) have used this code path erroneously
|
||||||
// for some time now. Moving away from that would require migrating existing settings.
|
// for some time now. Moving away from that would require migrating existing settings.
|
||||||
QString userPath;
|
QString userPath;
|
||||||
char *env = getenv("XDG_CONFIG_HOME");
|
QByteArray env = qgetenv("XDG_CONFIG_HOME");
|
||||||
if (env == 0) {
|
if (env.isEmpty()) {
|
||||||
userPath = QDir::homePath();
|
userPath = QDir::homePath();
|
||||||
userPath += QLatin1Char('/');
|
userPath += QLatin1Char('/');
|
||||||
userPath += QLatin1String(".config");
|
userPath += QLatin1String(".config");
|
||||||
} else if (*env == '/') {
|
} else if (env.startsWith('/')) {
|
||||||
userPath = QFile::decodeName(env);
|
userPath = QFile::decodeName(env);
|
||||||
} else {
|
} else {
|
||||||
userPath = QDir::homePath();
|
userPath = QDir::homePath();
|
||||||
|
@ -340,7 +340,7 @@ QIBusPlatformInputContextPrivate::QIBusPlatformInputContextPrivate()
|
|||||||
|
|
||||||
QDBusConnection *QIBusPlatformInputContextPrivate::createConnection()
|
QDBusConnection *QIBusPlatformInputContextPrivate::createConnection()
|
||||||
{
|
{
|
||||||
QByteArray display(getenv("DISPLAY"));
|
QByteArray display(qgetenv("DISPLAY"));
|
||||||
QByteArray host = "unix";
|
QByteArray host = "unix";
|
||||||
QByteArray displayNumber = "0";
|
QByteArray displayNumber = "0";
|
||||||
|
|
||||||
|
@ -3768,7 +3768,7 @@ QString QFileDialogPrivate::getEnvironmentVariable(const QString &string)
|
|||||||
{
|
{
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
if (string.size() > 1 && string.startsWith(QLatin1Char('$'))) {
|
if (string.size() > 1 && string.startsWith(QLatin1Char('$'))) {
|
||||||
return QString::fromLocal8Bit(getenv(string.mid(1).toLatin1().constData()));
|
return QString::fromLocal8Bit(qgetenv(string.mid(1).toLatin1().constData()));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (string.size() > 2 && string.startsWith(QLatin1Char('%')) && string.endsWith(QLatin1Char('%'))) {
|
if (string.size() > 2 && string.startsWith(QLatin1Char('%')) && string.endsWith(QLatin1Char('%'))) {
|
||||||
|
Loading…
Reference in New Issue
Block a user