QProcess: ensure we don't accidentally execute something from CWD
authorDebian Qt/KDE Maintainers <debian-qt-kde@lists.debian.org>
Sun, 7 Aug 2022 13:56:40 +0000 (14:56 +0100)
committerDmitry Shachnev <mitya57@debian.org>
Sun, 7 Aug 2022 13:56:40 +0000 (14:56 +0100)
Origin: upstream, https://download.qt.io/official_releases/qt/5.15/CVE-2022-25255-qprocess5-15.diff
Last-Update: 2022-02-21

Unless "." (or the empty string) is in $PATH, we're not supposed to find
executables in the current directory. This is how the Unix shells behave
and we match their behavior. It's also the behavior Qt had prior to 5.9
(commit 28666d167aa8e602c0bea25ebc4d51b55005db13). On Windows, searching
the current directory is the norm, so we keep that behavior.

This commit does not add an explicit check for an empty return from
QStandardPaths::findExecutable(). Instead, we allow that empty string to
go all the way to execve(2), which will fail with ENOENT. We could catch
it early, before fork(2), but why add code for the error case?

See https://kde.org/info/security/advisory-20220131-1.txt

Gbp-Pq: Name CVE-2022-25255.diff

src/corelib/io/qprocess_unix.cpp
tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp

index 50390e57f5f8ccd39ea45e024e0eaaca83e4ab37..15c8f307450dcbf8abf6bf3784ae8c6d6079368e 100644 (file)
@@ -1,7 +1,7 @@
 /****************************************************************************
 **
 ** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
+** Copyright (C) 2022 Intel Corporation.
 ** Contact: https://www.qt.io/licensing/
 **
 ** This file is part of the QtCore module of the Qt Toolkit.
@@ -422,14 +422,15 @@ void QProcessPrivate::startProcess()
     // Add the program name to the argument list.
     argv[0] = nullptr;
     if (!program.contains(QLatin1Char('/'))) {
+        // findExecutable() returns its argument if it's an absolute path,
+        // otherwise it searches $PATH; returns empty if not found (we handle
+        // that case much later)
         const QString &exeFilePath = QStandardPaths::findExecutable(program);
-        if (!exeFilePath.isEmpty()) {
-            const QByteArray &tmp = QFile::encodeName(exeFilePath);
-            argv[0] = ::strdup(tmp.constData());
-        }
-    }
-    if (!argv[0])
+        const QByteArray &tmp = QFile::encodeName(exeFilePath);
+        argv[0] = ::strdup(tmp.constData());
+    } else {
         argv[0] = ::strdup(encodedProgramName.constData());
+    }
 
     // Add every argument to the list
     for (int i = 0; i < arguments.count(); ++i)
@@ -983,15 +984,16 @@ bool QProcessPrivate::startDetached(qint64 *pid)
                 envp = _q_dupEnvironment(environment.d.constData()->vars, &envc);
             }
 
-            QByteArray tmp;
             if (!program.contains(QLatin1Char('/'))) {
+                // findExecutable() returns its argument if it's an absolute path,
+                // otherwise it searches $PATH; returns empty if not found (we handle
+                // that case much later)
                 const QString &exeFilePath = QStandardPaths::findExecutable(program);
-                if (!exeFilePath.isEmpty())
-                    tmp = QFile::encodeName(exeFilePath);
+                const QByteArray &tmp = QFile::encodeName(exeFilePath);
+                argv[0] = ::strdup(tmp.constData());
+            } else {
+                argv[0] = ::strdup(QFile::encodeName(program));
             }
-            if (tmp.isEmpty())
-                tmp = QFile::encodeName(program);
-            argv[0] = tmp.data();
 
             if (envp)
                 qt_safe_execve(argv[0], argv, envp);
index a3b8ef78d8fa9f408eb04875511366891d2383df..4955cebd5d99e80b48944780ecaeec7a2a9208b1 100644 (file)
@@ -1498,7 +1498,7 @@ void tst_QApplication::desktopSettingsAware()
     environment += QLatin1String("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM=1");
     testProcess.setEnvironment(environment);
 #endif
-    testProcess.start("desktopsettingsaware_helper");
+    testProcess.start("./desktopsettingsaware_helper");
     QVERIFY2(testProcess.waitForStarted(),
              qPrintable(QString::fromLatin1("Cannot start 'desktopsettingsaware_helper': %1").arg(testProcess.errorString())));
     QVERIFY(testProcess.waitForFinished(10000));
@@ -2452,7 +2452,7 @@ void tst_QApplication::qtbug_12673()
 #if QT_CONFIG(process)
     QProcess testProcess;
     QStringList arguments;
-    testProcess.start("modal_helper", arguments);
+    testProcess.start("./modal_helper", arguments);
     QVERIFY2(testProcess.waitForStarted(),
              qPrintable(QString::fromLatin1("Cannot start 'modal_helper': %1").arg(testProcess.errorString())));
     QVERIFY(testProcess.waitForFinished(20000));