[notify] qt5: security fix CVE-2015-1858, CVE-2015-1859, CVE-2015-1860, CVE-2015-0295 and a few bugfixes

This commit is contained in:
Danny Rawlins 2015-05-17 04:59:26 +10:00
parent 41c57e8c8a
commit dbff41a50b
11 changed files with 1741 additions and 5 deletions

View File

@ -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

View File

@ -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

View File

@ -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

44
qt5/CVE-2015-0295.patch Normal file
View File

@ -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

View File

@ -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

30
qt5/CVE-2015-1860.patch Normal file
View File

@ -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

View File

@ -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

238
qt5/glib-2.43.patch Normal file
View File

@ -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

View File

@ -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

View File

@ -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

691
qt5/qnam-corruption.patch Normal file
View File

@ -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