commit dbff41a50b990598b3f21158122c516ad4ee1c95 Author: Danny Rawlins <monster.romster@gmail.com> Date: Sun May 17 04:59:26 2015 +1000 [notify] qt5: security fix CVE-2015-1858, CVE-2015-1859, CVE-2015-1860, CVE-2015-0295 and a few bugfixes diff --git a/qt5/.md5sum b/qt5/.md5sum index 12551c3..c7b2157 100644 --- a/qt5/.md5sum +++ b/qt5/.md5sum @@ -1 +1,10 @@ +ac8fa252868161c676d876b614e24436 0001-Require-fPIC-instead-of-just-fPIE-for-reduce-relocat.patch +fb1459f325a15b15b18afeac51173a1d 0001-Revert-Rotate-images-according-to-Exif-orientation.patch +871ab111d03a640b4d0250388a4307cc CVE-2015-0295.patch +b799130014294cb3c73fc46e7e8889db CVE-2015-1858_1859.patch +64bc4f7d5097438eb6c6f8042378b3a3 CVE-2015-1860.patch +bf756a3061e1b30b28df85dcf0c90df3 glib-2.43.patch +665439088fc7de52a97455c5eaf87889 keypad-shortcuts.patch +30d219401f77e536d215addc420b634c qlockfile-deadlock.patch +d0b070d6f211948ef4842b46542b9e4f qnam-corruption.patch 7afb5f9235d8d42b5b6e832442a32a5d qt-everywhere-opensource-src-5.4.1.tar.xz diff --git a/qt5/0001-Require-fPIC-instead-of-just-fPIE-for-reduce-relocat.patch b/qt5/0001-Require-fPIC-instead-of-just-fPIE-for-reduce-relocat.patch new file mode 100644 index 0000000..b0acd03 --- /dev/null +++ b/qt5/0001-Require-fPIC-instead-of-just-fPIE-for-reduce-relocat.patch @@ -0,0 +1,130 @@ +From 36d6eb721e7d5997ade75e289d4088dc48678d0d Mon Sep 17 00:00:00 2001 +From: Thiago Macieira <thiago.macieira@intel.com> +Date: Tue, 5 May 2015 08:43:42 -0700 +Subject: [PATCH] Require -fPIC instead of just -fPIE for -reduce-relocations + +GCC 5 combined with a recent binutils have a new optimization that +allows them to generate copy relocations even in -fPIE code. Clang has +the same functionality when compiling an executable with -flto. We need +to let the compilers know that they cannot use copy relocations, so they +need to use really position-independent code. + +Position independent code throughout is not really required. We just +need the compilers to use position-independent access to symbols coming +from the Qt libraries, but there's currently no other way of doing that. + +Task-number: QTBUG-45755 +Change-Id: I0d4913955e3745b69672ffff13db5df7377398c5 +Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com> +Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> +--- + mkspecs/common/gcc-base.conf | 2 +- + mkspecs/common/qcc-base.conf | 2 +- + mkspecs/linux-icc/qmake.conf | 2 +- + src/corelib/Qt5CoreConfigExtras.cmake.in | 2 +- + src/corelib/global/qglobal.h | 4 ++-- + tests/auto/tools/moc/tst_moc.cpp | 6 +++--- + 6 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/mkspecs/common/gcc-base.conf b/mkspecs/common/gcc-base.conf +index a149f4d..e4ccbd7 100644 +--- a/mkspecs/common/gcc-base.conf ++++ b/mkspecs/common/gcc-base.conf +@@ -42,7 +42,7 @@ QMAKE_CFLAGS_RELEASE += $$QMAKE_CFLAGS_OPTIMIZE + QMAKE_CFLAGS_DEBUG += -g + QMAKE_CFLAGS_SHLIB += -fPIC + QMAKE_CFLAGS_STATIC_LIB += -fPIC +-QMAKE_CFLAGS_APP += -fPIE ++QMAKE_CFLAGS_APP += -fPIC + QMAKE_CFLAGS_ISYSTEM = -isystem + QMAKE_CFLAGS_YACC += -Wno-unused -Wno-parentheses + QMAKE_CFLAGS_HIDESYMS += -fvisibility=hidden +diff --git a/mkspecs/common/qcc-base.conf b/mkspecs/common/qcc-base.conf +index f529d7f..8276316 100644 +--- a/mkspecs/common/qcc-base.conf ++++ b/mkspecs/common/qcc-base.conf +@@ -23,7 +23,7 @@ QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += $$QMAKE_CFLAGS_OPTIMIZE -g + QMAKE_CFLAGS_DEBUG += -g + QMAKE_CFLAGS_SHLIB += -fPIC -shared + QMAKE_CFLAGS_STATIC_LIB += -fPIC +-QMAKE_CFLAGS_APP += -fPIE ++QMAKE_CFLAGS_APP += -fPIC + QMAKE_CFLAGS_YACC += -Wno-unused -Wno-parentheses + QMAKE_CFLAGS_HIDESYMS += -fvisibility=hidden + QMAKE_CFLAGS_SSE2 += -msse2 +diff --git a/mkspecs/linux-icc/qmake.conf b/mkspecs/linux-icc/qmake.conf +index 8119c8a..9190aa9 100644 +--- a/mkspecs/linux-icc/qmake.conf ++++ b/mkspecs/linux-icc/qmake.conf +@@ -12,7 +12,7 @@ QMAKE_LEXFLAGS = + QMAKE_YACC = yacc + QMAKE_YACCFLAGS = -d + QMAKE_CFLAGS = +-QMAKE_CFLAGS_APP = -fPIE ++QMAKE_CFLAGS_APP = -fPIC + QMAKE_CFLAGS_DEPS = -M + QMAKE_CFLAGS_WARN_ON = -w1 -Wall -Wcheck -wd1572,873,2259,2261 + QMAKE_CFLAGS_WARN_OFF = -w +diff --git a/src/corelib/Qt5CoreConfigExtras.cmake.in b/src/corelib/Qt5CoreConfigExtras.cmake.in +index 7213a84..48d5f21 100644 +--- a/src/corelib/Qt5CoreConfigExtras.cmake.in ++++ b/src/corelib/Qt5CoreConfigExtras.cmake.in +@@ -71,7 +71,7 @@ set(_qt5_corelib_extra_includes) + # macro to add it. + set(Qt5_POSITION_INDEPENDENT_CODE True) + set_property(TARGET Qt5::Core PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE \"ON\") +-set(Qt5Core_EXECUTABLE_COMPILE_FLAGS \"-fPIE\") ++set(Qt5Core_EXECUTABLE_COMPILE_FLAGS \"-fPIC\") + !!ENDIF + + !!IF !isEmpty(QT_NAMESPACE) +diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h +index 455582e..ef84662 100644 +--- a/src/corelib/global/qglobal.h ++++ b/src/corelib/global/qglobal.h +@@ -1047,9 +1047,9 @@ Q_CORE_EXPORT int qrand(); + # define QT_NO_SHAREDMEMORY + #endif + +-#if !defined(QT_BOOTSTRAPPED) && defined(QT_REDUCE_RELOCATIONS) && defined(__ELF__) && !defined(__PIC__) && !defined(__PIE__) ++#if !defined(QT_BOOTSTRAPPED) && defined(QT_REDUCE_RELOCATIONS) && defined(__ELF__) && !defined(__PIC__) + # error "You must build your code with position independent code if Qt was built with -reduce-relocations. "\ +- "Compile your code with -fPIC or -fPIE." ++ "Compile your code with -fPIC." + #endif + + namespace QtPrivate { +diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp +index edb6488..748cb82 100644 +--- a/tests/auto/tools/moc/tst_moc.cpp ++++ b/tests/auto/tools/moc/tst_moc.cpp +@@ -662,7 +662,7 @@ void tst_Moc::oldStyleCasts() + + QStringList args; + args << "-c" << "-x" << "c++" << "-Wold-style-cast" << "-I" << "." +- << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIE" << "-"; ++ << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-"; + proc.start("gcc", args); + QVERIFY(proc.waitForStarted()); + proc.write(mocOut); +@@ -732,7 +732,7 @@ void tst_Moc::inputFileNameWithDotsButNoExtension() + + QStringList args; + args << "-c" << "-x" << "c++" << "-I" << ".." +- << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIE" << "-"; ++ << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-"; + proc.start("gcc", args); + QVERIFY(proc.waitForStarted()); + proc.write(mocOut); +@@ -1011,7 +1011,7 @@ void tst_Moc::ignoreOptionClashes() + // If -pthread wasn't ignored, it was parsed as a prefix of "thread/", which breaks compilation. + QStringList gccArgs; + gccArgs << "-c" << "-x" << "c++" << "-I" << ".." +- << "-I" << qtIncludePath << "-I" << includeDir << "-o" << "/dev/null" << "-fPIE" << "-"; ++ << "-I" << qtIncludePath << "-I" << includeDir << "-o" << "/dev/null" << "-fPIC" << "-"; + proc.start("gcc", gccArgs); + QVERIFY(proc.waitForStarted()); + proc.write(mocOut); +-- +2.3.6 + diff --git a/qt5/0001-Revert-Rotate-images-according-to-Exif-orientation.patch b/qt5/0001-Revert-Rotate-images-according-to-Exif-orientation.patch new file mode 100644 index 0000000..0b1756d --- /dev/null +++ b/qt5/0001-Revert-Rotate-images-according-to-Exif-orientation.patch @@ -0,0 +1,290 @@ +From 518f886b6128331ce47932edd637471d58d0d877 Mon Sep 17 00:00:00 2001 +From: Rainer Keller <rainer.keller@theqtcompany.com> +Date: Fri, 17 Apr 2015 10:43:19 +0200 +Subject: [PATCH] Revert "Rotate images according to Exif orientation" + +Due to a behavior change. + +This reverts commit 9157087334186ff3ef811f2ec234a3bf5d4a4889. +This reverts commit 16c32c6dfbca03a46d1a2bb87b6c1c365e6179d5. + +Task-number: QTBUG-37946 +Task-number: QTBUG-45552 +Task-number: QTBUG-43563 +Change-Id: Idf8df7d8f22465e8f6b51acb68993ac97208b184 +Reviewed-by: Konstantin Ritt <ritt.ks@gmail.com> +Reviewed-by: Gunnar Sletta <gunnar@sletta.org> +--- + src/gui/image/qjpeghandler.cpp | 150 +---------------------------- + tests/auto/gui/image/qimage/tst_qimage.cpp | 33 ------- + 2 files changed, 1 insertion(+), 182 deletions(-) + +diff --git a/src/gui/image/qjpeghandler.cpp b/src/gui/image/qjpeghandler.cpp +index 9cf9947..14c8b4c 100644 +--- a/src/gui/image/qjpeghandler.cpp ++++ b/src/gui/image/qjpeghandler.cpp +@@ -726,7 +726,7 @@ public: + }; + + QJpegHandlerPrivate(QJpegHandler *qq) +- : quality(75), exifOrientation(1), iod_src(0), state(Ready), q(qq) ++ : quality(75), iod_src(0), state(Ready), q(qq) + {} + + ~QJpegHandlerPrivate() +@@ -741,10 +741,8 @@ public: + + bool readJpegHeader(QIODevice*); + bool read(QImage *image); +- void applyExifOrientation(QImage *image); + + int quality; +- int exifOrientation; + QVariant size; + QImage::Format format; + QSize scaledSize; +@@ -762,97 +760,6 @@ public: + QJpegHandler *q; + }; + +-static bool readExifHeader(QDataStream &stream) +-{ +- char prefix[6]; +- if (stream.readRawData(prefix, sizeof(prefix)) != sizeof(prefix)) +- return false; +- if (prefix[0] != 'E' || prefix[1] != 'x' || prefix[2] != 'i' || prefix[3] != 'f' || prefix[4] != 0 || prefix[5] != 0) +- return false; +- return true; +-} +- +-/* +- * Returns -1 on error +- * Returns 0 if no Exif orientation was found +- * Returns 1 orientation is horizontal (normal) +- * Returns 2 mirror horizontal +- * Returns 3 rotate 180 +- * Returns 4 mirror vertical +- * Returns 5 mirror horizontal and rotate 270 CCW +- * Returns 6 rotate 90 CW +- * Returns 7 mirror horizontal and rotate 90 CW +- * Returns 8 rotate 270 CW +- */ +-static int getExifOrientation(QByteArray &exifData) +-{ +- QDataStream stream(&exifData, QIODevice::ReadOnly); +- +- if (!readExifHeader(stream)) +- return -1; +- +- quint16 val; +- quint32 offset; +- +- // read byte order marker +- stream >> val; +- if (val == 0x4949) // 'II' == Intel +- stream.setByteOrder(QDataStream::LittleEndian); +- else if (val == 0x4d4d) // 'MM' == Motorola +- stream.setByteOrder(QDataStream::BigEndian); +- else +- return -1; // unknown byte order +- +- // read size +- stream >> val; +- if (val != 0x2a) +- return -1; +- +- stream >> offset; +- // we have already used 8 bytes of TIFF header +- offset -= 8; +- +- // read IFD +- while (!stream.atEnd()) { +- quint16 numEntries; +- +- // skip offset bytes to get the next IFD +- if (stream.skipRawData(offset) != (qint32)offset) +- return -1; +- +- stream >> numEntries; +- +- for (;numEntries > 0; --numEntries) { +- quint16 tag; +- quint16 type; +- quint32 components; +- quint16 value; +- quint16 dummy; +- +- stream >> tag >> type >> components >> value >> dummy; +- if (tag == 0x0112) { // Tag Exif.Image.Orientation +- if (components !=1) +- return -1; +- if (type != 3) // we are expecting it to be an unsigned short +- return -1; +- if (value < 1 || value > 8) // check for valid range +- return -1; +- +- // It is possible to include the orientation multiple times. +- // Right now the first value is returned. +- return value; +- } +- } +- +- // read offset to next IFD +- stream >> offset; +- if (offset == 0) // this is the last IFD +- break; +- } +- +- // No Exif orientation was found +- return 0; +-} + /*! + \internal + */ +@@ -872,7 +779,6 @@ bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device) + + if (!setjmp(err.setjmp_buffer)) { + jpeg_save_markers(&info, JPEG_COM, 0xFFFF); +- jpeg_save_markers(&info, JPEG_APP0+1, 0xFFFF); // Exif uses APP1 marker + + (void) jpeg_read_header(&info, TRUE); + +@@ -884,8 +790,6 @@ bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device) + format = QImage::Format_Invalid; + read_jpeg_format(format, &info); + +- QByteArray exifData; +- + for (jpeg_saved_marker_ptr marker = info.marker_list; marker != NULL; marker = marker->next) { + if (marker->marker == JPEG_COM) { + QString key, value; +@@ -903,18 +807,9 @@ bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device) + description += key + QLatin1String(": ") + value.simplified(); + readTexts.append(key); + readTexts.append(value); +- } else if (marker->marker == JPEG_APP0+1) { +- exifData.append((const char*)marker->data, marker->data_length); + } + } + +- if (exifData.size()) { +- // Exif data present +- int orientation = getExifOrientation(exifData); +- if (orientation > 0) +- exifOrientation = orientation; +- } +- + state = ReadHeader; + return true; + } +@@ -928,48 +823,6 @@ bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device) + return true; + } + +-void QJpegHandlerPrivate::applyExifOrientation(QImage *image) +-{ +- // This is not an optimized implementation, but easiest to maintain +- QTransform transform; +- +- switch (exifOrientation) { +- case 1: // normal +- break; +- case 2: // mirror horizontal +- *image = image->mirrored(true, false); +- break; +- case 3: // rotate 180 +- transform.rotate(180); +- *image = image->transformed(transform); +- break; +- case 4: // mirror vertical +- *image = image->mirrored(false, true); +- break; +- case 5: // mirror horizontal and rotate 270 CCW +- *image = image->mirrored(true, false); +- transform.rotate(270); +- *image = image->transformed(transform); +- break; +- case 6: // rotate 90 CW +- transform.rotate(90); +- *image = image->transformed(transform); +- break; +- case 7: // mirror horizontal and rotate 90 CW +- *image = image->mirrored(true, false); +- transform.rotate(90); +- *image = image->transformed(transform); +- break; +- case 8: // rotate 270 CW +- transform.rotate(-90); +- *image = image->transformed(transform); +- break; +- default: +- qWarning("This should never happen"); +- } +- exifOrientation = 1; +-} +- + bool QJpegHandlerPrivate::read(QImage *image) + { + if(state == Ready) +@@ -981,7 +834,6 @@ bool QJpegHandlerPrivate::read(QImage *image) + if (success) { + for (int i = 0; i < readTexts.size()-1; i+=2) + image->setText(readTexts.at(i), readTexts.at(i+1)); +- applyExifOrientation(image); + + state = Ready; + return true; +diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp +index 309d3a8..33302ed 100644 +--- a/tests/auto/gui/image/qimage/tst_qimage.cpp ++++ b/tests/auto/gui/image/qimage/tst_qimage.cpp +@@ -173,9 +173,6 @@ private slots: + void invertPixelsRGB_data(); + void invertPixelsRGB(); + +- void exifOrientation_data(); +- void exifOrientation(); +- + void cleanupFunctions(); + + private: +@@ -2649,36 +2646,6 @@ void tst_QImage::invertPixelsRGB() + QCOMPARE(qBlue(pixel) >> 4, (255 - 96) >> 4); + } + +-void tst_QImage::exifOrientation_data() +-{ +- QTest::addColumn<QString>("fileName"); +- QTest::newRow("Orientation 1, Intel format") << m_prefix + "jpeg_exif_orientation_value_1.jpg"; +- QTest::newRow("Orientation 2, Intel format") << m_prefix + "jpeg_exif_orientation_value_2.jpg"; +- QTest::newRow("Orientation 3, Intel format") << m_prefix + "jpeg_exif_orientation_value_3.jpg"; +- QTest::newRow("Orientation 4, Intel format") << m_prefix + "jpeg_exif_orientation_value_4.jpg"; +- QTest::newRow("Orientation 5, Intel format") << m_prefix + "jpeg_exif_orientation_value_5.jpg"; +- QTest::newRow("Orientation 6, Intel format") << m_prefix + "jpeg_exif_orientation_value_6.jpg"; +- QTest::newRow("Orientation 6, Motorola format") << m_prefix + "jpeg_exif_orientation_value_6_motorola.jpg"; +- QTest::newRow("Orientation 7, Intel format") << m_prefix + "jpeg_exif_orientation_value_7.jpg"; +- QTest::newRow("Orientation 8, Intel format") << m_prefix + "jpeg_exif_orientation_value_8.jpg"; +-} +- +-void tst_QImage::exifOrientation() +-{ +- QFETCH(QString, fileName); +- +- QImage img; +- QRgb px; +- +- QVERIFY(img.load(fileName)); +- +- px = img.pixel(0, 0); +- QVERIFY(qRed(px) > 250 && qGreen(px) < 5 && qBlue(px) < 5); +- +- px = img.pixel(img.width() - 1, 0); +- QVERIFY(qRed(px) < 5 && qGreen(px) < 5 && qBlue(px) > 250); +-} +- + static void cleanupFunction(void* info) + { + bool *called = static_cast<bool*>(info); +-- +2.3.6 + diff --git a/qt5/CVE-2015-0295.patch b/qt5/CVE-2015-0295.patch new file mode 100644 index 0000000..e852736 --- /dev/null +++ b/qt5/CVE-2015-0295.patch @@ -0,0 +1,44 @@ +From 661f6bfd032dacc62841037732816a583640e187 Mon Sep 17 00:00:00 2001 +From: "Richard J. Moore" <rich@kde.org> +Date: Sat, 21 Feb 2015 17:43:21 +0000 +Subject: Fix a division by zero when processing malformed BMP files. + +This fixes a division by 0 when processing a maliciously crafted BMP +file. No impact beyond DoS. + +Task-number: QTBUG-44547 +Change-Id: Ifcded2c0aa712e90d23e6b3969af0ec3add53973 +Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> +Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> +--- + src/gui/image/qbmphandler.cpp | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp +index 21c1a2f..df66499 100644 +--- a/src/gui/image/qbmphandler.cpp ++++ b/src/gui/image/qbmphandler.cpp +@@ -314,12 +314,20 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int + } + } else if (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32)) { + red_shift = calc_shift(red_mask); ++ if (((red_mask >> red_shift) + 1) == 0) ++ return false; + red_scale = 256 / ((red_mask >> red_shift) + 1); + green_shift = calc_shift(green_mask); ++ if (((green_mask >> green_shift) + 1) == 0) ++ return false; + green_scale = 256 / ((green_mask >> green_shift) + 1); + blue_shift = calc_shift(blue_mask); ++ if (((blue_mask >> blue_shift) + 1) == 0) ++ return false; + blue_scale = 256 / ((blue_mask >> blue_shift) + 1); + alpha_shift = calc_shift(alpha_mask); ++ if (((alpha_mask >> alpha_shift) + 1) == 0) ++ return false; + alpha_scale = 256 / ((alpha_mask >> alpha_shift) + 1); + } else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) { + blue_mask = 0x000000ff; +-- +cgit v0.11.0 + diff --git a/qt5/CVE-2015-1858_1859.patch b/qt5/CVE-2015-1858_1859.patch new file mode 100644 index 0000000..c0ea6c2 --- /dev/null +++ b/qt5/CVE-2015-1858_1859.patch @@ -0,0 +1,62 @@ +From 51ec7ebfe5f45d1c0a03d992e97053cac66e25fe Mon Sep 17 00:00:00 2001 +From: Eirik Aavitsland <eirik.aavitsland@theqtcompany.com> +Date: Wed, 11 Mar 2015 13:34:01 +0100 +Subject: Fixes crash in bmp and ico image decoding + +Fuzzing test revealed that for certain malformed bmp and ico files, +the handler would segfault. + +Change-Id: I19d45145f31e7f808f7f6a1a1610270ea4159cbe +Reviewed-by: Lars Knoll <lars.knoll@digia.com> +--- + src/gui/image/qbmphandler.cpp | 13 +++++++------ + src/plugins/imageformats/ico/qicohandler.cpp | 2 +- + 2 files changed, 8 insertions(+), 7 deletions(-) + +diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp +index df66499..8acc593 100644 +--- a/src/gui/image/qbmphandler.cpp ++++ b/src/gui/image/qbmphandler.cpp +@@ -484,12 +484,6 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int + p = data + (h-y-1)*bpl; + break; + case 2: // delta (jump) +- // Protection +- if ((uint)x >= (uint)w) +- x = w-1; +- if ((uint)y >= (uint)h) +- y = h-1; +- + { + quint8 tmp; + d->getChar((char *)&tmp); +@@ -497,6 +491,13 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int + d->getChar((char *)&tmp); + y += tmp; + } ++ ++ // Protection ++ if ((uint)x >= (uint)w) ++ x = w-1; ++ if ((uint)y >= (uint)h) ++ y = h-1; ++ + p = data + (h-y-1)*bpl + x; + break; + default: // absolute mode +diff --git a/src/plugins/imageformats/ico/qicohandler.cpp b/src/plugins/imageformats/ico/qicohandler.cpp +index 00de0c8..ec1654e 100644 +--- a/src/plugins/imageformats/ico/qicohandler.cpp ++++ b/src/plugins/imageformats/ico/qicohandler.cpp +@@ -567,7 +567,7 @@ QImage ICOReader::iconAt(int index) + QImage::Format format = QImage::Format_ARGB32; + if (icoAttrib.nbits == 24) + format = QImage::Format_RGB32; +- else if (icoAttrib.ncolors == 2) ++ else if (icoAttrib.ncolors == 2 && icoAttrib.depth == 1) + format = QImage::Format_Mono; + else if (icoAttrib.ncolors > 0) + format = QImage::Format_Indexed8; +-- +cgit v0.11.0 + diff --git a/qt5/CVE-2015-1860.patch b/qt5/CVE-2015-1860.patch new file mode 100644 index 0000000..3b9daa5 --- /dev/null +++ b/qt5/CVE-2015-1860.patch @@ -0,0 +1,30 @@ +From d3048a29797ee2d80d84bbda26bb3c954584f332 Mon Sep 17 00:00:00 2001 +From: Eirik Aavitsland <eirik.aavitsland@theqtcompany.com> +Date: Wed, 11 Mar 2015 09:00:41 +0100 +Subject: Fixes crash in gif image decoder + +Fuzzing test revealed that for certain malformed gif files, +qgifhandler would segfault. + +Change-Id: I5bb6f60e1c61849e0d8c735edc3869945e5331c1 +Reviewed-by: Richard J. Moore <rich@kde.org> +--- + src/gui/image/qgifhandler.cpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/gui/image/qgifhandler.cpp b/src/gui/image/qgifhandler.cpp +index 03e46ab..8d8c4ae 100644 +--- a/src/gui/image/qgifhandler.cpp ++++ b/src/gui/image/qgifhandler.cpp +@@ -936,6 +936,8 @@ void QGIFFormat::fillRect(QImage *image, int col, int row, int w, int h, QRgb co + + void QGIFFormat::nextY(unsigned char *bits, int bpl) + { ++ if (out_of_bounds) ++ return; + int my; + switch (interlace) { + case 0: // Non-interlaced +-- +cgit v0.11.0 + diff --git a/qt5/Pkgfile b/qt5/Pkgfile index b58a457..4daa7e8 100644 --- a/qt5/Pkgfile +++ b/qt5/Pkgfile @@ -6,12 +6,46 @@ name=qt5 version=5.4.1 -release=1 -source=(http://download.qt-project.org/official_releases/qt/${version%.*}/$version/single/qt-everywhere-opensource-src-$version.tar.xz) +release=2 +source=(http://download.qt-project.org/official_releases/qt/${version%.*}/$version/single/qt-everywhere-opensource-src-$version.tar.xz + 0001-Revert-Rotate-images-according-to-Exif-orientation.patch + 0001-Require-fPIC-instead-of-just-fPIE-for-reduce-relocat.patch + glib-2.43.patch + qlockfile-deadlock.patch + qnam-corruption.patch + keypad-shortcuts.patch + CVE-2015-0295.patch + CVE-2015-1858_1859.patch + CVE-2015-1860.patch) build() { cd qt-everywhere-opensource-src-$version + # https://bugreports.qt.io/browse/QTBUG-37946 + patch -d qtbase -p1 -i $SRC/0001-Revert-Rotate-images-according-to-Exif-orientation.patch + + # https://bugreports.qt.io/browse/QTBUG-45755 + patch -d qtbase -p1 -i $SRC/0001-Require-fPIC-instead-of-just-fPIE-for-reduce-relocat.patch + + # for glib 2.43+ https://bugreports.qt.io/browse/QTBUG-44714 + patch -d qtwebkit -p1 -i $SRC/glib-2.43.patch + + # https://bugs.kde.org/show_bug.cgi?id=345901 + patch -d qtbase -p1 -i $SRC/qlockfile-deadlock.patch + + # https://codereview.qt-project.org/#/c/110150/ + patch -d qtbase -p1 -i $SRC/qnam-corruption.patch + + # https://bugs.archlinux.org/task/44676 + patch -d qtbase -p1 -i $SRC/keypad-shortcuts.patch + + # http://lists.qt-project.org/pipermail/announce/2015-February/000059.html + patch -d qtbase -p1 -i $SRC/CVE-2015-0295.patch + + # http://lists.qt-project.org/pipermail/announce/2015-April/000067.html + patch -d qtbase -p1 -i $SRC/CVE-2015-1858_1859.patch + patch -d qtbase -p1 -i $SRC/CVE-2015-1860.patch + # Respect system CXX [ "$CXX" ] || CXX=g++ sed -i "/^QMAKE_CXX\s/s|=.*|= $CXX|" qtbase/mkspecs/common/g++-base.conf @@ -20,11 +54,13 @@ build() { sed -i 's|X11R6/||g' qtbase/mkspecs/*/*.conf # Respect system CXXFLAGS - sed -i "s|-O2|$CXXFLAGS|" qtbase/mkspecs/common/g++-unix.conf - sed -i "s|-O2|${CXXFLAGS}|" qtbase/mkspecs/common/{g++,gcc}-base.conf + sed -i "s|^\(QMAKE_CFLAGS_RELEASE.*\)|\1 ${CXXFLAGS}|" qtbase/mkspecs/common/gcc-base.conf + #sed -i "s|-O2|$CXXFLAGS|" qtbase/mkspecs/common/g++-unix.conf + #sed -i "s|-O2|${CXXFLAGS}|" qtbase/mkspecs/common/{g++,gcc}-base.conf # Respect system LDFLAGS - sed -i "/^QMAKE_LFLAGS\s/s|+=|+= ${LDFLAGS}|g" qtbase/mkspecs/common/gcc-base.conf + sed -i "s|^\(QMAKE_LFLAGS_RELEASE.*\)|\1 ${LDFLAGS}|" qtbase/mkspecs/common/g++-unix.conf + #sed -i "/^QMAKE_LFLAGS\s/s|+=|+= ${LDFLAGS}|g" qtbase/mkspecs/common/gcc-base.conf # Fix quoting bug sed -i 's|"$COMPILER" -c|$COMPILER -c|' qtbase/config.tests/unix/fvisibility.test diff --git a/qt5/glib-2.43.patch b/qt5/glib-2.43.patch new file mode 100644 index 0000000..248dc45 --- /dev/null +++ b/qt5/glib-2.43.patch @@ -0,0 +1,238 @@ +From ed5d6c93f7cf902e58aaf3f0abd54b3b258172ed Mon Sep 17 00:00:00 2001 +From: Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> +Date: Fri, 27 Feb 2015 14:14:44 +0100 +Subject: Fix building with glib 2.43+ + +The newest version of glib have introduced their own GMutexLocker +conflicting with the one defined in WebCore. + +Task-number: QTBUG-44714 +Change-Id: Ibdd1646e129eaed84c65dc0d96bebe80f2a61f4b +Reviewed-by: Andras Becsi <andras.becsi@theqtcompany.com> +--- + .../gstreamer/WebKitWebSourceGStreamer.cpp | 48 +++++++++++----------- + 1 file changed, 24 insertions(+), 24 deletions(-) + +diff --git a/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp +index 5625873..a6d961f 100644 +--- a/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp ++++ b/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp +@@ -354,7 +354,7 @@ static void webKitWebSrcSetProperty(GObject* object, guint propID, const GValue* + + switch (propID) { + case PROP_IRADIO_MODE: { +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + priv->iradioMode = g_value_get_boolean(value); + break; + } +@@ -376,7 +376,7 @@ static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value + WebKitWebSrc* src = WEBKIT_WEB_SRC(object); + WebKitWebSrcPrivate* priv = src->priv; + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + switch (propID) { + case PROP_IRADIO_MODE: + g_value_set_boolean(value, priv->iradioMode); +@@ -429,7 +429,7 @@ static gboolean webKitWebSrcStop(WebKitWebSrc* src) + + ASSERT(isMainThread()); + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + + bool seeking = priv->seekID; + +@@ -493,7 +493,7 @@ static gboolean webKitWebSrcStart(WebKitWebSrc* src) + + ASSERT(isMainThread()); + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + + priv->startID = 0; + +@@ -584,7 +584,7 @@ static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStat + return ret; + } + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_DEBUG_OBJECT(src, "READY->PAUSED"); +@@ -615,7 +615,7 @@ static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQ + gst_query_parse_duration(query, &format, NULL); + + GST_DEBUG_OBJECT(src, "duration query in format %s", gst_format_get_name(format)); +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + if (format == GST_FORMAT_BYTES && src->priv->size > 0) { + gst_query_set_duration(query, format, src->priv->size); + result = TRUE; +@@ -623,7 +623,7 @@ static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQ + break; + } + case GST_QUERY_URI: { +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + gst_query_set_uri(query, src->priv->uri); + result = TRUE; + break; +@@ -668,7 +668,7 @@ static gchar* webKitWebSrcGetUri(GstURIHandler* handler) + WebKitWebSrc* src = WEBKIT_WEB_SRC(handler); + gchar* ret; + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + ret = g_strdup(src->priv->uri); + return ret; + } +@@ -683,7 +683,7 @@ static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GEr + return FALSE; + } + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + + g_free(priv->uri); + priv->uri = 0; +@@ -719,7 +719,7 @@ static const gchar* webKitWebSrcGetUri(GstURIHandler* handler) + WebKitWebSrc* src = WEBKIT_WEB_SRC(handler); + gchar* ret; + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + ret = g_strdup(src->priv->uri); + return ret; + } +@@ -734,7 +734,7 @@ static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri) + return FALSE; + } + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + + g_free(priv->uri); + priv->uri = 0; +@@ -772,7 +772,7 @@ static gboolean webKitWebSrcNeedDataMainCb(WebKitWebSrc* src) + + ASSERT(isMainThread()); + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + // already stopped + if (!priv->needDataID) + return FALSE; +@@ -793,7 +793,7 @@ static void webKitWebSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData) + + GST_DEBUG_OBJECT(src, "Need more data: %u", length); + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + if (priv->needDataID || !priv->paused) { + return; + } +@@ -807,7 +807,7 @@ static gboolean webKitWebSrcEnoughDataMainCb(WebKitWebSrc* src) + + ASSERT(isMainThread()); + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + // already stopped + if (!priv->enoughDataID) + return FALSE; +@@ -828,7 +828,7 @@ static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData) + + GST_DEBUG_OBJECT(src, "Have enough data"); + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + if (priv->enoughDataID || priv->paused) { + return; + } +@@ -842,7 +842,7 @@ static gboolean webKitWebSrcSeekMainCb(WebKitWebSrc* src) + + ASSERT(isMainThread()); + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + // already stopped + if (!priv->seekID) + return FALSE; +@@ -860,7 +860,7 @@ static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer user + WebKitWebSrcPrivate* priv = src->priv; + + GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset); +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + if (offset == priv->offset && priv->requestedOffset == priv->offset) + return TRUE; + +@@ -879,7 +879,7 @@ static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer user + void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player) + { + ASSERT(player); +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + src->priv->player = player; + s_cachedResourceLoader = player->cachedResourceLoader(); + } +@@ -906,7 +906,7 @@ char* StreamingClient::createReadBuffer(size_t requestedSize, size_t& actualSize + mapGstBuffer(buffer); + #endif + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + priv->buffer = adoptGRef(buffer); + locker.unlock(); + +@@ -921,7 +921,7 @@ void StreamingClient::handleResponseReceived(const ResourceResponse& response) + + GST_DEBUG_OBJECT(src, "Received response: %d", response.httpStatusCode()); + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + + // If we seeked we need 206 == PARTIAL_CONTENT + if (priv->requestedOffset && response.httpStatusCode() != 206) { +@@ -1020,7 +1020,7 @@ void StreamingClient::handleDataReceived(const char* data, int length) + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); + WebKitWebSrcPrivate* priv = src->priv; + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + + GST_LOG_OBJECT(src, "Have %d bytes of data", priv->buffer ? getGstBufferSize(priv->buffer.get()) : length); + +@@ -1074,7 +1074,7 @@ void StreamingClient::handleNotifyFinished() + + GST_DEBUG_OBJECT(src, "Have EOS"); + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + if (!priv->seekID) { + locker.unlock(); + gst_app_src_end_of_stream(priv->appsrc); +@@ -1210,7 +1210,7 @@ void ResourceHandleStreamingClient::wasBlocked(ResourceHandle*) + + GST_ERROR_OBJECT(src, "Request was blocked"); + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + uri.set(g_strdup(src->priv->uri)); + locker.unlock(); + +@@ -1224,7 +1224,7 @@ void ResourceHandleStreamingClient::cannotShowURL(ResourceHandle*) + + GST_ERROR_OBJECT(src, "Cannot show URL"); + +- GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); ++ WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + uri.set(g_strdup(src->priv->uri)); + locker.unlock(); + +-- +cgit v0.11.0 + diff --git a/qt5/keypad-shortcuts.patch b/qt5/keypad-shortcuts.patch new file mode 100644 index 0000000..2961b9d --- /dev/null +++ b/qt5/keypad-shortcuts.patch @@ -0,0 +1,94 @@ +From c137502c7fd7550c780c9531ec414098d8101757 Mon Sep 17 00:00:00 2001 +From: Alexander Volkov <a.volkov@rusbitech.ru> +Date: Thu, 18 Sep 2014 16:16:26 +0400 +Subject: Fix shortcuts with keypad keys + +The way of searching a shortcut match for a key without the keypad +modifier introduced in 547a1bea492954d828aa0798be93384669812489 is +not correct. QKeyEvent::setModifiers() doesn't change native scan code +so we get the incorrect QKeyEvent object which is eventually passed to +the implementation of QPlatformIntegration::possibleKeys(). +And then QPlatformIntegration::possibleKeys() returns the same result +as for the original QKeyEvent object. + +So to fix it we have to remove Qt::KeypadModifier from keys after +calling the implementation of QPlatformIntegration::possibleKeys(), +as it was before 547a1bea492954d828aa0798be93384669812489. + +Task-number: QTBUG-33093 +Task-number: QTBUG-20191 +Change-Id: I5b33c9b6cf2c06b133166a31eba9aff9181c9483 +--- + src/gui/kernel/qshortcutmap.cpp | 12 +++++------- + src/gui/kernel/qshortcutmap_p.h | 4 ++-- + 2 files changed, 7 insertions(+), 9 deletions(-) + +diff --git a/src/gui/kernel/qshortcutmap.cpp b/src/gui/kernel/qshortcutmap.cpp +index c915ed39..c13c82c 100644 +--- a/src/gui/kernel/qshortcutmap.cpp ++++ b/src/gui/kernel/qshortcutmap.cpp +@@ -388,9 +388,7 @@ QKeySequence::SequenceMatch QShortcutMap::nextState(QKeyEvent *e) + result = find(e); + if (result == QKeySequence::NoMatch && (e->modifiers() & Qt::KeypadModifier)) { + // Try to find a match without keypad modifier +- QKeyEvent event = *e; +- event.setModifiers(e->modifiers() & ~Qt::KeypadModifier); +- result = find(&event); ++ result = find(e, Qt::KeypadModifier); + } + if (result == QKeySequence::NoMatch && e->modifiers() & Qt::ShiftModifier) { + // If Shift + Key_Backtab, also try Shift + Qt::Key_Tab +@@ -443,13 +441,13 @@ bool QShortcutMap::hasShortcutForKeySequence(const QKeySequence &seq) const + which can be access through matches(). + \sa matches + */ +-QKeySequence::SequenceMatch QShortcutMap::find(QKeyEvent *e) ++QKeySequence::SequenceMatch QShortcutMap::find(QKeyEvent *e, int ignoredModifiers) + { + Q_D(QShortcutMap); + if (!d->sequences.count()) + return QKeySequence::NoMatch; + +- createNewSequences(e, d->newEntries); ++ createNewSequences(e, d->newEntries, ignoredModifiers); + #if defined(DEBUG_QSHORTCUTMAP) + qDebug() << "Possible shortcut key sequences:" << d->newEntries; + #endif +@@ -551,7 +549,7 @@ void QShortcutMap::clearSequence(QVector<QKeySequence> &ksl) + Alters \a seq to the new sequence state, based on the + current sequence state, and the new key event \a e. + */ +-void QShortcutMap::createNewSequences(QKeyEvent *e, QVector<QKeySequence> &ksl) ++void QShortcutMap::createNewSequences(QKeyEvent *e, QVector<QKeySequence> &ksl, int ignoredModifiers) + { + Q_D(QShortcutMap); + QList<int> possibleKeys = QKeyMapper::possibleKeys(e); +@@ -581,7 +579,7 @@ void QShortcutMap::createNewSequences(QKeyEvent *e, QVector<QKeySequence> &ksl) + curKsl.setKey(0, 2); + curKsl.setKey(0, 3); + } +- curKsl.setKey(possibleKeys.at(pkNum), index); ++ curKsl.setKey(possibleKeys.at(pkNum) & ~ignoredModifiers, index); + } + } + } +diff --git a/src/gui/kernel/qshortcutmap_p.h b/src/gui/kernel/qshortcutmap_p.h +index 3959c2c..39a8eac 100644 +--- a/src/gui/kernel/qshortcutmap_p.h ++++ b/src/gui/kernel/qshortcutmap_p.h +@@ -96,10 +96,10 @@ private: + QKeySequence::SequenceMatch state(); + void dispatchEvent(QKeyEvent *e); + +- QKeySequence::SequenceMatch find(QKeyEvent *e); ++ QKeySequence::SequenceMatch find(QKeyEvent *e, int ignoredModifiers = 0); + QKeySequence::SequenceMatch matches(const QKeySequence &seq1, const QKeySequence &seq2) const; + QVector<const QShortcutEntry *> matches() const; +- void createNewSequences(QKeyEvent *e, QVector<QKeySequence> &ksl); ++ void createNewSequences(QKeyEvent *e, QVector<QKeySequence> &ksl, int ignoredModifiers); + void clearSequence(QVector<QKeySequence> &ksl); + int translateModifiers(Qt::KeyboardModifiers modifiers); + +-- +cgit v0.11.0 + diff --git a/qt5/qlockfile-deadlock.patch b/qt5/qlockfile-deadlock.patch new file mode 100644 index 0000000..5c59127 --- /dev/null +++ b/qt5/qlockfile-deadlock.patch @@ -0,0 +1,112 @@ +From f58e882b7594c59b6050d3c87562fcf836d10f60 Mon Sep 17 00:00:00 2001 +From: Olivier Goffart <ogoffart@woboq.com> +Date: Tue, 14 Apr 2015 10:58:26 +0200 +Subject: QLockFile: fix deadlock when the lock file is corrupted + +[ChangeLog][QtCore][QLockFile] Fixed a deadlock when the lock file +is corrupted. + +Task-number: QTBUG-44771 +Change-Id: Ic490b09d70ff1cc1733b64949889a73720b2d0f3 +Reviewed-by: David Faure <david.faure@kdab.com> +--- + src/corelib/io/qlockfile_unix.cpp | 10 +++++----- + src/corelib/io/qlockfile_win.cpp | 22 +++++++++++----------- + tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp | 17 +++++++++++++++++ + 3 files changed, 33 insertions(+), 16 deletions(-) + +diff --git a/src/corelib/io/qlockfile_unix.cpp b/src/corelib/io/qlockfile_unix.cpp +index bf1015a..dc9f8f7 100644 +--- a/src/corelib/io/qlockfile_unix.cpp ++++ b/src/corelib/io/qlockfile_unix.cpp +@@ -181,11 +181,11 @@ bool QLockFilePrivate::isApparentlyStale() const + { + qint64 pid; + QString hostname, appname; +- if (!getLockInfo(&pid, &hostname, &appname)) +- return false; +- if (hostname.isEmpty() || hostname == QString::fromLocal8Bit(localHostName())) { +- if (::kill(pid, 0) == -1 && errno == ESRCH) +- return true; // PID doesn't exist anymore ++ if (getLockInfo(&pid, &hostname, &appname)) { ++ if (hostname.isEmpty() || hostname == QString::fromLocal8Bit(localHostName())) { ++ if (::kill(pid, 0) == -1 && errno == ESRCH) ++ return true; // PID doesn't exist anymore ++ } + } + const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime()); + return staleLockTime > 0 && age > staleLockTime; +diff --git a/src/corelib/io/qlockfile_win.cpp b/src/corelib/io/qlockfile_win.cpp +index f9f2909..3587c7b 100644 +--- a/src/corelib/io/qlockfile_win.cpp ++++ b/src/corelib/io/qlockfile_win.cpp +@@ -115,21 +115,21 @@ bool QLockFilePrivate::isApparentlyStale() const + { + qint64 pid; + QString hostname, appname; +- if (!getLockInfo(&pid, &hostname, &appname)) +- return false; + + // On WinRT there seems to be no way of obtaining information about other + // processes due to sandboxing + #ifndef Q_OS_WINRT +- if (hostname == QString::fromLocal8Bit(localHostName())) { +- HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); +- if (!procHandle) +- return true; +- // We got a handle but check if process is still alive +- DWORD dwR = ::WaitForSingleObject(procHandle, 0); +- ::CloseHandle(procHandle); +- if (dwR == WAIT_TIMEOUT) +- return true; ++ if (getLockInfo(&pid, &hostname, &appname)) { ++ if (hostname == QString::fromLocal8Bit(localHostName())) { ++ HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); ++ if (!procHandle) ++ return true; ++ // We got a handle but check if process is still alive ++ DWORD dwR = ::WaitForSingleObject(procHandle, 0); ++ ::CloseHandle(procHandle); ++ if (dwR == WAIT_TIMEOUT) ++ return true; ++ } + } + #endif // !Q_OS_WINRT + const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime()); +diff --git a/tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp b/tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp +index 77bef94..12bea67 100644 +--- a/tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp ++++ b/tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp +@@ -58,6 +58,7 @@ private slots: + void staleLongLockFromBusyProcess(); + void staleLockRace(); + void noPermissions(); ++ void corruptedLockFile(); + + public: + QString m_helperApp; +@@ -415,5 +416,21 @@ void tst_QLockFile::noPermissions() + QCOMPARE(int(lockFile.error()), int(QLockFile::PermissionError)); + } + ++void tst_QLockFile::corruptedLockFile() ++{ ++ const QString fileName = dir.path() + "/corruptedLockFile"; ++ ++ { ++ // Create a empty file. Typically the result of a computer crash or hard disk full. ++ QFile file(fileName); ++ QVERIFY(file.open(QFile::WriteOnly)); ++ } ++ ++ QLockFile secondLock(fileName); ++ secondLock.setStaleLockTime(100); ++ QVERIFY(secondLock.tryLock(10000)); ++ QCOMPARE(int(secondLock.error()), int(QLockFile::NoError)); ++} ++ + QTEST_MAIN(tst_QLockFile) + #include "tst_qlockfile.moc" +-- +cgit v0.11.0 + diff --git a/qt5/qnam-corruption.patch b/qt5/qnam-corruption.patch new file mode 100644 index 0000000..51649f2 --- /dev/null +++ b/qt5/qnam-corruption.patch @@ -0,0 +1,691 @@ +From cff39fba10ffc10ee4dcfdc66ff6528eb26462d3 Mon Sep 17 00:00:00 2001 +From: Markus Goetz <markus@woboq.com> +Date: Fri, 10 Apr 2015 14:09:53 +0200 +Subject: QNAM: Fix upload corruptions when server closes connection + +This patch fixes several upload corruptions if the server closes the connection +while/before we send data into it. They happen inside multiple places in the HTTP +layer and are explained in the comments. +Corruptions are: +* The upload byte device has an in-flight signal with pending upload data, if +it gets reset (because server closes the connection) then the re-send of the +request was sometimes taking this stale in-flight pending upload data. +* Because some signals were DirectConnection and some were QueuedConnection, there +was a chance that a direct signal overtakes a queued signal. The state machine +then sent data down the socket which was buffered there (and sent later) although +it did not match the current state of the state machine when it was actually sent. +* A socket was seen as being able to have requests sent even though it was not +encrypted yet. This relates to the previous corruption where data is stored inside +the socket's buffer and then sent later. + +The included auto test produces all fixed corruptions, I detected no regressions +via the other tests. +This code also adds a bit of sanity checking to protect from possible further +problems. + +[ChangeLog][QtNetwork] Fix HTTP(s) upload corruption when server closes connection + +Change-Id: I54c883925ec897050941498f139c4b523030432e +Reviewed-by: Peter Hartmann <peter-qt@hartmann.tk> +--- + src/corelib/io/qnoncontiguousbytedevice.cpp | 18 +++ + src/corelib/io/qnoncontiguousbytedevice_p.h | 4 + + .../access/qhttpnetworkconnectionchannel.cpp | 35 ++++- + .../access/qhttpnetworkconnectionchannel_p.h | 2 + + src/network/access/qhttpprotocolhandler.cpp | 7 + + src/network/access/qhttpthreaddelegate_p.h | 36 ++++- + src/network/access/qnetworkreplyhttpimpl.cpp | 25 ++- + src/network/access/qnetworkreplyhttpimpl_p.h | 7 +- + .../access/qnetworkreply/tst_qnetworkreply.cpp | 175 ++++++++++++++++++++- + 9 files changed, 279 insertions(+), 30 deletions(-) + +diff --git a/src/corelib/io/qnoncontiguousbytedevice.cpp b/src/corelib/io/qnoncontiguousbytedevice.cpp +index 11510a8..760ca3d 100644 +--- a/src/corelib/io/qnoncontiguousbytedevice.cpp ++++ b/src/corelib/io/qnoncontiguousbytedevice.cpp +@@ -236,6 +236,11 @@ qint64 QNonContiguousByteDeviceByteArrayImpl::size() + return byteArray->size(); + } + ++qint64 QNonContiguousByteDeviceByteArrayImpl::pos() ++{ ++ return currentPosition; ++} ++ + QNonContiguousByteDeviceRingBufferImpl::QNonContiguousByteDeviceRingBufferImpl(QSharedPointer<QRingBuffer> rb) + : QNonContiguousByteDevice(), currentPosition(0) + { +@@ -273,6 +278,11 @@ bool QNonContiguousByteDeviceRingBufferImpl::atEnd() + return currentPosition >= size(); + } + ++qint64 QNonContiguousByteDeviceRingBufferImpl::pos() ++{ ++ return currentPosition; ++} ++ + bool QNonContiguousByteDeviceRingBufferImpl::reset() + { + if (resetDisabled) +@@ -406,6 +416,14 @@ qint64 QNonContiguousByteDeviceIoDeviceImpl::size() + return device->size() - initialPosition; + } + ++qint64 QNonContiguousByteDeviceIoDeviceImpl::pos() ++{ ++ if (device->isSequential()) ++ return -1; ++ ++ return device->pos(); ++} ++ + QByteDeviceWrappingIoDevice::QByteDeviceWrappingIoDevice(QNonContiguousByteDevice *bd) : QIODevice((QObject*)0) + { + byteDevice = bd; +diff --git a/src/corelib/io/qnoncontiguousbytedevice_p.h b/src/corelib/io/qnoncontiguousbytedevice_p.h +index c05ae11..4d7b7b0 100644 +--- a/src/corelib/io/qnoncontiguousbytedevice_p.h ++++ b/src/corelib/io/qnoncontiguousbytedevice_p.h +@@ -61,6 +61,7 @@ public: + virtual const char* readPointer(qint64 maximumLength, qint64 &len) = 0; + virtual bool advanceReadPointer(qint64 amount) = 0; + virtual bool atEnd() = 0; ++ virtual qint64 pos() { return -1; } + virtual bool reset() = 0; + void disableReset(); + bool isResetDisabled() { return resetDisabled; } +@@ -106,6 +107,7 @@ public: + bool atEnd(); + bool reset(); + qint64 size(); ++ qint64 pos() Q_DECL_OVERRIDE; + protected: + QByteArray* byteArray; + qint64 currentPosition; +@@ -121,6 +123,7 @@ public: + bool atEnd(); + bool reset(); + qint64 size(); ++ qint64 pos() Q_DECL_OVERRIDE; + protected: + QSharedPointer<QRingBuffer> ringBuffer; + qint64 currentPosition; +@@ -138,6 +141,7 @@ public: + bool atEnd(); + bool reset(); + qint64 size(); ++ qint64 pos() Q_DECL_OVERRIDE; + protected: + QIODevice* device; + QByteArray* currentReadBuffer; +diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp +index 9f63280..49c6793 100644 +--- a/src/network/access/qhttpnetworkconnectionchannel.cpp ++++ b/src/network/access/qhttpnetworkconnectionchannel.cpp +@@ -106,15 +106,19 @@ void QHttpNetworkConnectionChannel::init() + socket->setProxy(QNetworkProxy::NoProxy); + #endif + ++ // We want all signals (except the interactive ones) be connected as QueuedConnection ++ // because else we're falling into cases where we recurse back into the socket code ++ // and mess up the state. Always going to the event loop (and expecting that when reading/writing) ++ // is safer. + QObject::connect(socket, SIGNAL(bytesWritten(qint64)), + this, SLOT(_q_bytesWritten(qint64)), +- Qt::DirectConnection); ++ Qt::QueuedConnection); + QObject::connect(socket, SIGNAL(connected()), + this, SLOT(_q_connected()), +- Qt::DirectConnection); ++ Qt::QueuedConnection); + QObject::connect(socket, SIGNAL(readyRead()), + this, SLOT(_q_readyRead()), +- Qt::DirectConnection); ++ Qt::QueuedConnection); + + // The disconnected() and error() signals may already come + // while calling connectToHost(). +@@ -143,13 +147,13 @@ void QHttpNetworkConnectionChannel::init() + // won't be a sslSocket if encrypt is false + QObject::connect(sslSocket, SIGNAL(encrypted()), + this, SLOT(_q_encrypted()), +- Qt::DirectConnection); ++ Qt::QueuedConnection); + QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)), + this, SLOT(_q_sslErrors(QList<QSslError>)), + Qt::DirectConnection); + QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)), + this, SLOT(_q_encryptedBytesWritten(qint64)), +- Qt::DirectConnection); ++ Qt::QueuedConnection); + + if (ignoreAllSslErrors) + sslSocket->ignoreSslErrors(); +@@ -186,8 +190,11 @@ void QHttpNetworkConnectionChannel::close() + // pendingEncrypt must only be true in between connected and encrypted states + pendingEncrypt = false; + +- if (socket) ++ if (socket) { ++ // socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while ++ // there is no socket yet. + socket->close(); ++ } + } + + +@@ -353,6 +360,14 @@ bool QHttpNetworkConnectionChannel::ensureConnection() + } + return false; + } ++ ++ // This code path for ConnectedState ++ if (pendingEncrypt) { ++ // Let's only be really connected when we have received the encrypted() signal. Else the state machine seems to mess up ++ // and corrupt the things sent to the server. ++ return false; ++ } ++ + return true; + } + +@@ -659,6 +674,12 @@ bool QHttpNetworkConnectionChannel::isSocketReading() const + void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes) + { + Q_UNUSED(bytes); ++ if (ssl) { ++ // In the SSL case we want to send data from encryptedBytesWritten signal since that one ++ // is the one going down to the actual network, not only into some SSL buffer. ++ return; ++ } ++ + // bytes have been written to the socket. write even more of them :) + if (isSocketWriting()) + sendRequest(); +@@ -734,7 +755,7 @@ void QHttpNetworkConnectionChannel::_q_connected() + + // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! + //channels[i].reconnectAttempts = 2; +- if (pendingEncrypt) { ++ if (ssl || pendingEncrypt) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState + #ifndef QT_NO_SSL + if (connection->sslContext().isNull()) { + // this socket is making the 1st handshake for this connection, +diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h +index 692c0e6..231fe11 100644 +--- a/src/network/access/qhttpnetworkconnectionchannel_p.h ++++ b/src/network/access/qhttpnetworkconnectionchannel_p.h +@@ -83,6 +83,8 @@ typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair; + class QHttpNetworkConnectionChannel : public QObject { + Q_OBJECT + public: ++ // TODO: Refactor this to add an EncryptingState (and remove pendingEncrypt). ++ // Also add an Unconnected state so IdleState does not have double meaning. + enum ChannelState { + IdleState = 0, // ready to send request + ConnectingState = 1, // connecting to host +diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp +index 28e10f7..3357948 100644 +--- a/src/network/access/qhttpprotocolhandler.cpp ++++ b/src/network/access/qhttpprotocolhandler.cpp +@@ -368,6 +368,13 @@ bool QHttpProtocolHandler::sendRequest() + // nothing to read currently, break the loop + break; + } else { ++ if (m_channel->written != uploadByteDevice->pos()) { ++ // Sanity check. This was useful in tracking down an upload corruption. ++ qWarning() << "QHttpProtocolHandler: Internal error in sendRequest. Expected to write at position" << m_channel->written << "but read device is at" << uploadByteDevice->pos(); ++ Q_ASSERT(m_channel->written == uploadByteDevice->pos()); ++ m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::ProtocolFailure); ++ return false; ++ } + qint64 currentWriteSize = m_socket->write(readPointer, currentReadSize); + if (currentWriteSize == -1 || currentWriteSize != currentReadSize) { + // socket broke down +diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h +index 1661082..b553409 100644 +--- a/src/network/access/qhttpthreaddelegate_p.h ++++ b/src/network/access/qhttpthreaddelegate_p.h +@@ -187,6 +187,7 @@ protected: + QByteArray m_dataArray; + bool m_atEnd; + qint64 m_size; ++ qint64 m_pos; // to match calls of haveDataSlot with the expected position + public: + QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s) + : QNonContiguousByteDevice(), +@@ -194,7 +195,8 @@ public: + m_amount(0), + m_data(0), + m_atEnd(aE), +- m_size(s) ++ m_size(s), ++ m_pos(0) + { + } + +@@ -202,6 +204,11 @@ public: + { + } + ++ qint64 pos() Q_DECL_OVERRIDE ++ { ++ return m_pos; ++ } ++ + const char* readPointer(qint64 maximumLength, qint64 &len) + { + if (m_amount > 0) { +@@ -229,11 +236,10 @@ public: + + m_amount -= a; + m_data += a; ++ m_pos += a; + +- // To main thread to inform about our state +- emit processedData(a); +- +- // FIXME possible optimization, already ask user thread for some data ++ // To main thread to inform about our state. The m_pos will be sent as a sanity check. ++ emit processedData(m_pos, a); + + return true; + } +@@ -250,10 +256,21 @@ public: + { + m_amount = 0; + m_data = 0; ++ m_dataArray.clear(); ++ ++ if (wantDataPending) { ++ // had requested the user thread to send some data (only 1 in-flight at any moment) ++ wantDataPending = false; ++ } + + // Communicate as BlockingQueuedConnection + bool b = false; + emit resetData(&b); ++ if (b) { ++ // the reset succeeded, we're at pos 0 again ++ m_pos = 0; ++ // the HTTP code will anyway abort the request if !b. ++ } + return b; + } + +@@ -264,8 +281,13 @@ public: + + public slots: + // From user thread: +- void haveDataSlot(QByteArray dataArray, bool dataAtEnd, qint64 dataSize) ++ void haveDataSlot(qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize) + { ++ if (pos != m_pos) { ++ // Sometimes when re-sending a request in the qhttpnetwork* layer there is a pending haveData from the ++ // user thread on the way to us. We need to ignore it since it is the data for the wrong(later) chunk. ++ return; ++ } + wantDataPending = false; + + m_dataArray = dataArray; +@@ -285,7 +307,7 @@ signals: + + // to main thread: + void wantData(qint64); +- void processedData(qint64); ++ void processedData(qint64 pos, qint64 amount); + void resetData(bool *b); + }; + +diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp +index 4ce7303..974a101 100644 +--- a/src/network/access/qnetworkreplyhttpimpl.cpp ++++ b/src/network/access/qnetworkreplyhttpimpl.cpp +@@ -424,6 +424,7 @@ QNetworkReplyHttpImplPrivate::QNetworkReplyHttpImplPrivate() + , synchronous(false) + , state(Idle) + , statusCode(0) ++ , uploadByteDevicePosition(false) + , uploadDeviceChoking(false) + , outgoingData(0) + , bytesUploaded(-1) +@@ -863,9 +864,9 @@ void QNetworkReplyHttpImplPrivate::postRequest() + q, SLOT(uploadByteDeviceReadyReadSlot()), + Qt::QueuedConnection); + +- // From main thread to user thread: +- QObject::connect(q, SIGNAL(haveUploadData(QByteArray,bool,qint64)), +- forwardUploadDevice, SLOT(haveDataSlot(QByteArray,bool,qint64)), Qt::QueuedConnection); ++ // From user thread to http thread: ++ QObject::connect(q, SIGNAL(haveUploadData(qint64,QByteArray,bool,qint64)), ++ forwardUploadDevice, SLOT(haveDataSlot(qint64,QByteArray,bool,qint64)), Qt::QueuedConnection); + QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()), + forwardUploadDevice, SIGNAL(readyRead()), + Qt::QueuedConnection); +@@ -873,8 +874,8 @@ void QNetworkReplyHttpImplPrivate::postRequest() + // From http thread to user thread: + QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)), + q, SLOT(wantUploadDataSlot(qint64))); +- QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)), +- q, SLOT(sentUploadDataSlot(qint64))); ++ QObject::connect(forwardUploadDevice,SIGNAL(processedData(qint64, qint64)), ++ q, SLOT(sentUploadDataSlot(qint64,qint64))); + QObject::connect(forwardUploadDevice, SIGNAL(resetData(bool*)), + q, SLOT(resetUploadDataSlot(bool*)), + Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued! +@@ -1268,12 +1269,22 @@ void QNetworkReplyHttpImplPrivate::replySslConfigurationChanged(const QSslConfig + void QNetworkReplyHttpImplPrivate::resetUploadDataSlot(bool *r) + { + *r = uploadByteDevice->reset(); ++ if (*r) { ++ // reset our own position which is used for the inter-thread communication ++ uploadByteDevicePosition = 0; ++ } + } + + // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread +-void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 amount) ++void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 pos, qint64 amount) + { ++ if (uploadByteDevicePosition + amount != pos) { ++ // Sanity check, should not happen. ++ error(QNetworkReply::UnknownNetworkError, ""); ++ return; ++ } + uploadByteDevice->advanceReadPointer(amount); ++ uploadByteDevicePosition += amount; + } + + // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread +@@ -1298,7 +1309,7 @@ void QNetworkReplyHttpImplPrivate::wantUploadDataSlot(qint64 maxSize) + QByteArray dataArray(data, currentUploadDataLength); + + // Communicate back to HTTP thread +- emit q->haveUploadData(dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size()); ++ emit q->haveUploadData(uploadByteDevicePosition, dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size()); + } + + void QNetworkReplyHttpImplPrivate::uploadByteDeviceReadyReadSlot() +diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h +index 77d9c5a..1940922 100644 +--- a/src/network/access/qnetworkreplyhttpimpl_p.h ++++ b/src/network/access/qnetworkreplyhttpimpl_p.h +@@ -120,7 +120,7 @@ public: + + Q_PRIVATE_SLOT(d_func(), void resetUploadDataSlot(bool *r)) + Q_PRIVATE_SLOT(d_func(), void wantUploadDataSlot(qint64)) +- Q_PRIVATE_SLOT(d_func(), void sentUploadDataSlot(qint64)) ++ Q_PRIVATE_SLOT(d_func(), void sentUploadDataSlot(qint64,qint64)) + Q_PRIVATE_SLOT(d_func(), void uploadByteDeviceReadyReadSlot()) + Q_PRIVATE_SLOT(d_func(), void emitReplyUploadProgress(qint64, qint64)) + Q_PRIVATE_SLOT(d_func(), void _q_cacheSaveDeviceAboutToClose()) +@@ -144,7 +144,7 @@ signals: + + void startHttpRequestSynchronously(); + +- void haveUploadData(QByteArray dataArray, bool dataAtEnd, qint64 dataSize); ++ void haveUploadData(const qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize); + }; + + class QNetworkReplyHttpImplPrivate: public QNetworkReplyPrivate +@@ -195,6 +195,7 @@ public: + // upload + QNonContiguousByteDevice* createUploadByteDevice(); + QSharedPointer<QNonContiguousByteDevice> uploadByteDevice; ++ qint64 uploadByteDevicePosition; + bool uploadDeviceChoking; // if we couldn't readPointer() any data at the moment + QIODevice *outgoingData; + QSharedPointer<QRingBuffer> outgoingDataBuffer; +@@ -283,7 +284,7 @@ public: + // From QNonContiguousByteDeviceThreadForwardImpl in HTTP thread: + void resetUploadDataSlot(bool *r); + void wantUploadDataSlot(qint64); +- void sentUploadDataSlot(qint64); ++ void sentUploadDataSlot(qint64, qint64); + + // From user's QNonContiguousByteDevice + void uploadByteDeviceReadyReadSlot(); +diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +index 3ccedf6..d2edf67 100644 +--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp ++++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +@@ -457,6 +457,10 @@ private Q_SLOTS: + + void putWithRateLimiting(); + ++#ifndef QT_NO_SSL ++ void putWithServerClosingConnectionImmediately(); ++#endif ++ + // NOTE: This test must be last! + void parentingRepliesToTheApp(); + private: +@@ -4718,18 +4722,22 @@ void tst_QNetworkReply::ioPostToHttpNoBufferFlag() + class SslServer : public QTcpServer { + Q_OBJECT + public: +- SslServer() : socket(0) {}; ++ SslServer() : socket(0), m_ssl(true) {} + void incomingConnection(qintptr socketDescriptor) { + QSslSocket *serverSocket = new QSslSocket; + serverSocket->setParent(this); + + if (serverSocket->setSocketDescriptor(socketDescriptor)) { ++ connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); ++ if (!m_ssl) { ++ emit newPlainConnection(serverSocket); ++ return; ++ } + QString testDataDir = QFileInfo(QFINDTESTDATA("rfc3252.txt")).absolutePath(); + if (testDataDir.isEmpty()) + testDataDir = QCoreApplication::applicationDirPath(); + + connect(serverSocket, SIGNAL(encrypted()), this, SLOT(encryptedSlot())); +- connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + serverSocket->setProtocol(QSsl::AnyProtocol); + connect(serverSocket, SIGNAL(sslErrors(QList<QSslError>)), serverSocket, SLOT(ignoreSslErrors())); + serverSocket->setLocalCertificate(testDataDir + "/certs/server.pem"); +@@ -4740,11 +4748,12 @@ public: + } + } + signals: +- void newEncryptedConnection(); ++ void newEncryptedConnection(QSslSocket *s); ++ void newPlainConnection(QSslSocket *s); + public slots: + void encryptedSlot() { + socket = (QSslSocket*) sender(); +- emit newEncryptedConnection(); ++ emit newEncryptedConnection(socket); + } + void readyReadSlot() { + // for the incoming sockets, not the server socket +@@ -4753,6 +4762,7 @@ public slots: + + public: + QSslSocket *socket; ++ bool m_ssl; + }; + + // very similar to ioPostToHttpUploadProgress but for SSL +@@ -4780,7 +4790,7 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress() + QNetworkReplyPtr reply(manager.post(request, sourceFile)); + + QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64))); +- connect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); ++ connect(&server, SIGNAL(newEncryptedConnection(QSslSocket*)), &QTestEventLoop::instance(), SLOT(exitLoop())); + connect(reply, SIGNAL(sslErrors(QList<QSslError>)), reply.data(), SLOT(ignoreSslErrors())); + + // get the request started and the incoming socket connected +@@ -4788,7 +4798,7 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress() + QVERIFY(!QTestEventLoop::instance().timeout()); + QTcpSocket *incomingSocket = server.socket; + QVERIFY(incomingSocket); +- disconnect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); ++ disconnect(&server, SIGNAL(newEncryptedConnection(QSslSocket*)), &QTestEventLoop::instance(), SLOT(exitLoop())); + + + incomingSocket->setReadBufferSize(1*1024); +@@ -7905,6 +7915,159 @@ void tst_QNetworkReply::putWithRateLimiting() + } + + ++#ifndef QT_NO_SSL ++ ++class PutWithServerClosingConnectionImmediatelyHandler: public QObject ++{ ++ Q_OBJECT ++public: ++ bool m_parsedHeaders; ++ QByteArray m_receivedData; ++ QByteArray m_expectedData; ++ QSslSocket *m_socket; ++ PutWithServerClosingConnectionImmediatelyHandler(QSslSocket *s, QByteArray expected) :m_parsedHeaders(false), m_expectedData(expected), m_socket(s) ++ { ++ m_socket->setParent(this); ++ connect(m_socket, SIGNAL(readyRead()), SLOT(readyReadSlot())); ++ connect(m_socket, SIGNAL(disconnected()), SLOT(disconnectedSlot())); ++ } ++signals: ++ void correctFileUploadReceived(); ++ void corruptFileUploadReceived(); ++ ++public slots: ++ void closeDelayed() { ++ m_socket->close(); ++ } ++ ++ void readyReadSlot() ++ { ++ QByteArray data = m_socket->readAll(); ++ m_receivedData += data; ++ if (!m_parsedHeaders && m_receivedData.contains("\r\n\r\n")) { ++ m_parsedHeaders = true; ++ QTimer::singleShot(qrand()%10, this, SLOT(closeDelayed())); // simulate random network latency ++ // This server simulates a web server connection closing, e.g. because of Apaches MaxKeepAliveRequests or KeepAliveTimeout ++ // In this case QNAM needs to re-send the upload data but it had a bug which then corrupts the upload ++ // This test catches that. ++ } ++ ++ } ++ void disconnectedSlot() ++ { ++ if (m_parsedHeaders) { ++ //qDebug() << m_receivedData.left(m_receivedData.indexOf("\r\n\r\n")); ++ m_receivedData = m_receivedData.mid(m_receivedData.indexOf("\r\n\r\n")+4); // check only actual data ++ } ++ if (m_receivedData.length() > 0 && !m_expectedData.startsWith(m_receivedData)) { ++ // We had received some data but it is corrupt! ++ qDebug() << "CORRUPT" << m_receivedData.count(); ++ ++ // Use this to track down the pattern of the corruption and conclude the source ++// QFile a("/tmp/corrupt"); ++// a.open(QIODevice::WriteOnly); ++// a.write(m_receivedData); ++// a.close(); ++ ++// QFile b("/tmp/correct"); ++// b.open(QIODevice::WriteOnly); ++// b.write(m_expectedData); ++// b.close(); ++ //exit(1); ++ emit corruptFileUploadReceived(); ++ } else { ++ emit correctFileUploadReceived(); ++ } ++ } ++}; ++ ++class PutWithServerClosingConnectionImmediatelyServer: public SslServer ++{ ++ Q_OBJECT ++public: ++ int m_correctUploads; ++ int m_corruptUploads; ++ int m_repliesFinished; ++ int m_expectedReplies; ++ QByteArray m_expectedData; ++ PutWithServerClosingConnectionImmediatelyServer() : SslServer(), m_correctUploads(0), m_corruptUploads(0), m_repliesFinished(0), m_expectedReplies(0) ++ { ++ QObject::connect(this, SIGNAL(newEncryptedConnection(QSslSocket*)), this, SLOT(createHandlerForConnection(QSslSocket*))); ++ QObject::connect(this, SIGNAL(newPlainConnection(QSslSocket*)), this, SLOT(createHandlerForConnection(QSslSocket*))); ++ } ++ ++public slots: ++ void createHandlerForConnection(QSslSocket* s) { ++ PutWithServerClosingConnectionImmediatelyHandler *handler = new PutWithServerClosingConnectionImmediatelyHandler(s, m_expectedData); ++ handler->setParent(this); ++ QObject::connect(handler, SIGNAL(correctFileUploadReceived()), this, SLOT(increaseCorrect())); ++ QObject::connect(handler, SIGNAL(corruptFileUploadReceived()), this, SLOT(increaseCorrupt())); ++ } ++ void increaseCorrect() { ++ m_correctUploads++; ++ } ++ void increaseCorrupt() { ++ m_corruptUploads++; ++ } ++ void replyFinished() { ++ m_repliesFinished++; ++ if (m_repliesFinished == m_expectedReplies) { ++ QTestEventLoop::instance().exitLoop(); ++ } ++ } ++}; ++ ++ ++ ++void tst_QNetworkReply::putWithServerClosingConnectionImmediately() ++{ ++ const int numUploads = 40; ++ qint64 wantedSize = 512*1024; // 512 kB ++ QByteArray sourceFile; ++ for (int i = 0; i < wantedSize; ++i) { ++ sourceFile += (char)'a' +(i%26); ++ } ++ bool withSsl = false; ++ ++ for (int s = 0; s <= 1; s++) { ++ withSsl = (s == 1); ++ // Test also needs to run several times because of 9c2ecf89 ++ for (int j = 0; j < 20; j++) { ++ // emulate a minimal https server ++ PutWithServerClosingConnectionImmediatelyServer server; ++ server.m_ssl = withSsl; ++ server.m_expectedData = sourceFile; ++ server.m_expectedReplies = numUploads; ++ server.listen(QHostAddress(QHostAddress::LocalHost), 0); ++ ++ for (int i = 0; i < numUploads; i++) { ++ // create the request ++ QUrl url = QUrl(QString("http%1://127.0.0.1:%2/file=%3").arg(withSsl ? "s" : "").arg(server.serverPort()).arg(i)); ++ QNetworkRequest request(url); ++ QNetworkReply *reply = manager.put(request, sourceFile); ++ connect(reply, SIGNAL(sslErrors(QList<QSslError>)), reply, SLOT(ignoreSslErrors())); ++ connect(reply, SIGNAL(finished()), &server, SLOT(replyFinished())); ++ reply->setParent(&server); ++ } ++ ++ // get the request started and the incoming socket connected ++ QTestEventLoop::instance().enterLoop(10); ++ ++ //qDebug() << "correct=" << server.m_correctUploads << "corrupt=" << server.m_corruptUploads << "expected=" <<numUploads; ++ ++ // Sanity check because ecause of 9c2ecf89 most replies will error out but we want to make sure at least some of them worked ++ QVERIFY(server.m_correctUploads > 5); ++ // Because actually important is that we don't get any corruption: ++ QCOMPARE(server.m_corruptUploads, 0); ++ ++ server.close(); ++ } ++ } ++ ++ ++} ++ ++#endif + + // NOTE: This test must be last testcase in tst_qnetworkreply! + void tst_QNetworkReply::parentingRepliesToTheApp() +-- +cgit v0.11.0 +