From cbc3e7276e128d52edd35196f73e635471678669 Mon Sep 17 00:00:00 2001 From: wk333 <13474090681@163.com> Date: Tue, 26 Jul 2022 17:25:43 +0800 Subject: [PATCH] Fix CVE-2021-38593 and CVE-2022-25255 (cherry picked from commit fb537edb24b6d18187c0d177fd0d6c22423a70b3) --- CVE-2021-38593.patch | 84 +++++++++++++++++ CVE-2022-25255.patch | 219 +++++++++++++++++++++++++++++++++++++++++++ qt5-qtbase.spec | 9 +- 3 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 CVE-2021-38593.patch create mode 100644 CVE-2022-25255.patch diff --git a/CVE-2021-38593.patch b/CVE-2021-38593.patch new file mode 100644 index 0000000..05fbb4b --- /dev/null +++ b/CVE-2021-38593.patch @@ -0,0 +1,84 @@ +Description: avoid processing-intensive painting of high number of tiny dashes + When stroking a dashed path, an unnecessary amount of processing would + be spent if there is a huge number of dashes visible, e.g. because of + scaling. Since the dashes are too small to be individually visible + anyway, just replace with a semi-transparent solid line for such + cases. +Origin: upstream, commits: + https://code.qt.io/cgit/qt/qtbase.git/commit/?id=7f345f2a1c8d9f60 + https://code.qt.io/cgit/qt/qtbase.git/commit/?id=9378ba2ae857df7e + https://code.qt.io/cgit/qt/qtbase.git/commit/?id=81998f50d039a631 + https://code.qt.io/cgit/qt/qtbase.git/commit/?id=cca8ed0547405b1c +Last-Update: 2021-12-12 + +--- a/src/gui/painting/qpaintengineex.cpp ++++ b/src/gui/painting/qpaintengineex.cpp +@@ -385,10 +385,10 @@ QPainterState *QPaintEngineEx::createSta + + Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp + +-void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) ++void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &inPen) + { + #ifdef QT_DEBUG_DRAW +- qDebug() << "QPaintEngineEx::stroke()" << pen; ++ qDebug() << "QPaintEngineEx::stroke()" << inPen; + #endif + + Q_D(QPaintEngineEx); +@@ -403,6 +403,38 @@ void QPaintEngineEx::stroke(const QVecto + d->stroker.setCubicToHook(qpaintengineex_cubicTo); + } + ++ QRectF clipRect; ++ QPen pen = inPen; ++ if (pen.style() > Qt::SolidLine) { ++ QRectF cpRect = path.controlPointRect(); ++ const QTransform &xf = state()->matrix; ++ if (qt_pen_is_cosmetic(pen, state()->renderHints)) { ++ clipRect = d->exDeviceRect; ++ cpRect.translate(xf.dx(), xf.dy()); ++ } else { ++ clipRect = xf.inverted().mapRect(QRectF(d->exDeviceRect)); ++ } ++ // Check to avoid generating unwieldy amount of dashes that will not be visible anyway ++ qreal pw = pen.widthF() ? pen.widthF() : 1; ++ QRectF extentRect = cpRect.adjusted(-pw, -pw, pw, pw) & clipRect; ++ qreal extent = qMax(extentRect.width(), extentRect.height()); ++ qreal patternLength = 0; ++ const QVector pattern = pen.dashPattern(); ++ const int patternSize = qMin(pattern.size(), 32); ++ for (int i = 0; i < patternSize; i++) ++ patternLength += qMax(pattern.at(i), qreal(0)); ++ patternLength *= pw; ++ if (qFuzzyIsNull(patternLength)) { ++ pen.setStyle(Qt::NoPen); ++ } else if (extent / patternLength > 10000) { ++ // approximate stream of tiny dashes with semi-transparent solid line ++ pen.setStyle(Qt::SolidLine); ++ QColor color(pen.color()); ++ color.setAlpha(color.alpha() / 2); ++ pen.setColor(color); ++ } ++ } ++ + if (!qpen_fast_equals(pen, d->strokerPen)) { + d->strokerPen = pen; + d->stroker.setJoinStyle(pen.joinStyle()); +@@ -430,14 +462,8 @@ void QPaintEngineEx::stroke(const QVecto + return; + } + +- if (pen.style() > Qt::SolidLine) { +- if (qt_pen_is_cosmetic(pen, state()->renderHints)){ +- d->activeStroker->setClipRect(d->exDeviceRect); +- } else { +- QRectF clipRect = state()->matrix.inverted().mapRect(QRectF(d->exDeviceRect)); +- d->activeStroker->setClipRect(clipRect); +- } +- } ++ if (!clipRect.isNull()) ++ d->activeStroker->setClipRect(clipRect); + + const QPainterPath::ElementType *types = path.elements(); + const qreal *points = path.points(); diff --git a/CVE-2022-25255.patch b/CVE-2022-25255.patch new file mode 100644 index 0000000..83fc328 --- /dev/null +++ b/CVE-2022-25255.patch @@ -0,0 +1,219 @@ +From ab3e0383b9de49c61bc49f5fbef80d1409fcafde Mon Sep 17 00:00:00 2001 +From: Thiago Macieira +Date: Mon, 31 Jan 2022 11:00:19 -0800 +Subject: [PATCH] QProcess/Unix: ensure we don't accidentally execute something + from CWD +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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 + +[ChangeLog][Important Behavior Changes] When passed a simple program +name with no slashes, QProcess on Unix systems will now only search the +current directory if "." is one of the entries in the PATH environment +variable. This bug fix restores the behavior QProcess had before Qt 5.9. +If launching an executable in the directory set by setWorkingDirectory() +or inherited from the parent is intended, pass a program name starting +with "./". For more information and best practices about finding an +executable, see QProcess' documentation. + +Pick-to: 5.15 6.2 6.3 +Change-Id: I54f205f6b7314351b078fffd16cf7013c97ee9fb +Reviewed-by: Qt CI Bot +Reviewed-by: MÃ¥rten Nordheim +Reviewed-by: Thiago Macieira +(cherry picked from commit 29fceed2ffb41954a63001414bd042611f2d4980) + +Note: This currently breaks various autotests, as they rely on the test +helpers (same directory as the test) to be in $PATH. In Qt 6, the CMake +test code sets this explicitly, which is not the case in Qt 5 (yet). +--- + src/corelib/io/qprocess_unix.cpp | 24 ++--- + .../auto/corelib/io/qprocess/tst_qprocess.cpp | 93 ++++++++++++++++++- + .../kernel/qapplication/tst_qapplication.cpp | 4 +- + 3 files changed, 107 insertions(+), 14 deletions(-) + +diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp +index 50390e57f5..5bd3b7f297 100644 +--- a/src/corelib/io/qprocess_unix.cpp ++++ b/src/corelib/io/qprocess_unix.cpp +@@ -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) +@@ -985,11 +986,12 @@ bool QProcessPrivate::startDetached(qint64 *pid) + + 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); +- } +- if (tmp.isEmpty()) ++ tmp = QFile::encodeName(exeFilePath); ++ } else + tmp = QFile::encodeName(program); + argv[0] = tmp.data(); + +diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp +index bc9df3f1f3..33051d3803 100644 +--- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp ++++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp +@@ -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 test suite of the Qt Toolkit. +@@ -149,6 +149,8 @@ private slots: + void startStopStartStopBuffers(); + void processEventsInAReadyReadSlot_data(); + void processEventsInAReadyReadSlot(); ++ void startFromCurrentWorkingDir_data(); ++ void startFromCurrentWorkingDir(); + + // keep these at the end, since they use lots of processes and sometimes + // caused obscure failures to occur in tests that followed them (esp. on the Mac) +@@ -2732,5 +2734,94 @@ void tst_QProcess::finishProcessBeforeReadingDone_deprecated() + QVERIFY(process.waitForFinished()); + } + ++enum class ChdirMode { ++ None = 0, ++ InParent, ++ InChild ++}; ++Q_DECLARE_METATYPE(ChdirMode) ++ ++void tst_QProcess::startFromCurrentWorkingDir_data() ++{ ++ qRegisterMetaType(); ++ QTest::addColumn("programPrefix"); ++ QTest::addColumn("chdirMode"); ++ QTest::addColumn("success"); ++ ++ constexpr bool IsWindows = true ++#ifdef Q_OS_UNIX ++ && false ++#endif ++ ; ++ ++ // baseline: trying to execute the directory, this can't possibly succeed! ++ QTest::newRow("plain-same-cwd") << QString() << ChdirMode::None << false; ++ ++ // cross-platform behavior: neither OS searches the setWorkingDirectory() ++ // dir without "./" ++ QTest::newRow("plain-child-chdir") << QString() << ChdirMode::InChild << false; ++ ++ // cross-platform behavior: both OSes search the parent's CWD with "./" ++ QTest::newRow("prefixed-parent-chdir") << "./" << ChdirMode::InParent << true; ++ ++ // opposite behaviors: Windows searches the parent's CWD and Unix searches ++ // the child's with "./" ++ QTest::newRow("prefixed-child-chdir") << "./" << ChdirMode::InChild << !IsWindows; ++ ++ // Windows searches the parent's CWD without "./" ++ QTest::newRow("plain-parent-chdir") << QString() << ChdirMode::InParent << IsWindows; ++} ++ ++void tst_QProcess::startFromCurrentWorkingDir() ++{ ++ QFETCH(QString, programPrefix); ++ QFETCH(ChdirMode, chdirMode); ++ QFETCH(bool, success); ++ ++ QProcess process; ++ qRegisterMetaType(); ++ QSignalSpy errorSpy(&process, &QProcess::errorOccurred); ++ QVERIFY(errorSpy.isValid()); ++ ++ // both the dir name and the executable name ++ const QString target = QStringLiteral("testProcessNormal"); ++ process.setProgram(programPrefix + target); ++ ++#ifdef Q_OS_UNIX ++ // Reset PATH, to be sure it doesn't contain . or the empty path. ++ // We can't do this on Windows because DLLs are searched in PATH ++ // and Windows always searches "." anyway. ++ auto restoreEnv = qScopeGuard([old = qgetenv("PATH")] { ++ qputenv("PATH", old); ++ }); ++ qputenv("PATH", "/"); ++#endif ++ ++ switch (chdirMode) { ++ case ChdirMode::InParent: { ++ auto restoreCwd = qScopeGuard([old = QDir::currentPath()] { ++ QDir::setCurrent(old); ++ }); ++ QVERIFY(QDir::setCurrent(target)); ++ process.start(); ++ break; ++ } ++ case ChdirMode::InChild: ++ process.setWorkingDirectory(target); ++ Q_FALLTHROUGH(); ++ case ChdirMode::None: ++ process.start(); ++ break; ++ } ++ ++ QCOMPARE(process.waitForStarted(), success); ++ QCOMPARE(errorSpy.count(), int(!success)); ++ if (success) { ++ QVERIFY(process.waitForFinished()); ++ } else { ++ QCOMPARE(process.error(), QProcess::FailedToStart); ++ } ++} ++ + QTEST_MAIN(tst_QProcess) + #include "tst_qprocess.moc" +-- +2.34.0 + diff --git a/qt5-qtbase.spec b/qt5-qtbase.spec index e8090a8..266d928 100644 --- a/qt5-qtbase.spec +++ b/qt5-qtbase.spec @@ -13,7 +13,7 @@ Name: qt5-qtbase Summary: Core component of Qt toolkit Version: 5.11.1 -Release: 12 +Release: 13 License: LGPLv2 with exceptions or GPLv3 with exceptions Url: http://qt-project.org/ Source0: https://download.qt.io/new_archive/qt/5.11/%{version}/submodules/qtbase-everywhere-src-%{version}.tar.xz @@ -37,6 +37,10 @@ Patch0011: qt5-qtbase-glibc.patch Patch6000: CVE-2018-15518.patch Patch6001: CVE-2015-9541.patch Patch6002: CVE-2019-18281.patch +# https://launchpad.net/ubuntu/+source/qtbase-opensource-src/5.12.8+dfsg-0ubuntu2.1 +Patch6003: CVE-2021-38593.patch +# https://build.opensuse.org/package/view_file/SUSE:SLE-15-SP3:Update/libqt5-qtbase/0001-QProcess-Unix-ensure-we-don-t-accidentally-execute-s.patch?expand=1 +Patch6004: CVE-2022-25255.patch BuildRequires: pkgconfig(libsystemd) cups-devel desktop-file-utils findutils BuildRequires: libjpeg-devel libmng-devel libtiff-devel pkgconfig(alsa) @@ -403,6 +407,9 @@ fi %changelog +* Tue Jul 26 2022 wangkai - 5.11.1-13 +- Fix CVE-2021-38593 and CVE-2022-25255 + * Thu Apr 22 2021 wangyue - 5.11.1-12 - fix CVE-2019-18281 -- Gitee