diff options
-rwxr-xr-x | configure | 5 | ||||
-rw-r--r-- | dist/changes-5.12.8 | 70 | ||||
-rw-r--r-- | mkspecs/features/mac/default_post.prf | 4 | ||||
-rw-r--r-- | src/android/jar/src/org/qtproject/qt5/android/QtNative.java | 128 | ||||
-rw-r--r-- | src/corelib/serialization/qxmlstream.g | 14 | ||||
-rw-r--r-- | src/corelib/serialization/qxmlstream_p.h | 14 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoabackingstore.h | 10 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoabackingstore.mm | 83 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qioscontext.mm | 7 | ||||
-rw-r--r-- | tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp | 23 |
10 files changed, 268 insertions, 90 deletions
@@ -271,12 +271,9 @@ macSDKify() val=$(echo $sdk_val $(echo $val | cut -s -d ' ' -f 2-)) echo "$var=$val" ;; - QMAKE_CFLAGS=*|QMAKE_CXXFLAGS=*) + QMAKE_CFLAGS=*|QMAKE_CXXFLAGS=*|QMAKE_LFLAGS=*) echo "$line -isysroot $sysroot $version_min_flag" ;; - QMAKE_LFLAGS=*) - echo "$line -Wl,-syslibroot,$sysroot $version_min_flag" - ;; *) echo "$line" ;; diff --git a/dist/changes-5.12.8 b/dist/changes-5.12.8 new file mode 100644 index 0000000000..0f75d167cf --- /dev/null +++ b/dist/changes-5.12.8 @@ -0,0 +1,70 @@ +Qt 5.12.8 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.7. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qt-5/index.html + +The Qt version 5.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* QtCore * +**************************************************************************** + + - QLockFile: + * Suppressed the warning on QNX that said 'setNativeLocks failed: + "Function not implemented"'. There is no difference in behavior: Qt + will continue not to be able to apply an OS- level file lock, which + means the lock could be accidentally stolen by buggy software. Correct + software using QLockFile should not be affected. + + - QXmlStream: + * QTBUG-47417: QXmlStreamReader does now limit the expansion of + entities to 4096 characters. Documents where a single entity + expands to more characters than the limit are not considered well + formed. The limit is there to avoid DoS attacks through + recursively expanding entities when loading untrusted content. Qt + 5.15 will add methods that allow changing that limit. + +**************************************************************************** +* QtSQL * +**************************************************************************** + + - sqlite: + * Updated to v3.31.1 + * [QTBUG-82533] Fixed CVE-2020-9327 + +**************************************************************************** +* QtNetwork * +**************************************************************************** + + - QSslCertificate: + * Fix a potential heap buffer overflow when parsing certificates + - QTBUG-81762: Fix SSL symbol resolving for OPENSSL_NO_NEXPROTONEG + +**************************************************************************** +* Platform specific changes * +**************************************************************************** + + - macOS: + * QTBUG-82986: Improve performance when flushing sublayers + * QTBUG-79139: Avoid repainting OpenGL layers when resizing the window + + - xcb/X11: + * QTBUG-76147, QTBUG-76354, QTBUG-68864: Fix handling of minimized state + + - QNX: + * QTBUG-81701: QLockFile: Disable flock() on QNX + + + diff --git a/mkspecs/features/mac/default_post.prf b/mkspecs/features/mac/default_post.prf index 993f4d56a9..d052808c14 100644 --- a/mkspecs/features/mac/default_post.prf +++ b/mkspecs/features/mac/default_post.prf @@ -197,7 +197,7 @@ macx-xcode { -isysroot$$xcodeSDKInfo(Path, $$sdk) QMAKE_XARCH_LFLAGS_$${arch} = $$version_min_flags \ -Xarch_$${arch} \ - -Wl,-syslibroot,$$xcodeSDKInfo(Path, $$sdk) + -isysroot$$xcodeSDKInfo(Path, $$sdk) QMAKE_XARCH_CFLAGS += $(EXPORT_QMAKE_XARCH_CFLAGS_$${arch}) QMAKE_XARCH_LFLAGS += $(EXPORT_QMAKE_XARCH_LFLAGS_$${arch}) @@ -218,7 +218,7 @@ macx-xcode { version_min_flag = -m$${version_identifier}-version-min=$$deployment_target QMAKE_CFLAGS += -isysroot $$QMAKE_MAC_SDK_PATH $$version_min_flag QMAKE_CXXFLAGS += -isysroot $$QMAKE_MAC_SDK_PATH $$version_min_flag - QMAKE_LFLAGS += -Wl,-syslibroot,$$QMAKE_MAC_SDK_PATH $$version_min_flag + QMAKE_LFLAGS += -isysroot $$QMAKE_MAC_SDK_PATH $$version_min_flag } # Enable precompiled headers for multiple architectures diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java index 1d2b70ab5f..a13c233712 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java @@ -724,54 +724,66 @@ public class QtNative public static boolean hasClipboardText() { - if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { - ClipData primaryClip = m_clipboardManager.getPrimaryClip(); - for (int i = 0; i < primaryClip.getItemCount(); ++i) - if (primaryClip.getItemAt(i).getText() != null) - return true; + try { + if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { + ClipData primaryClip = m_clipboardManager.getPrimaryClip(); + for (int i = 0; i < primaryClip.getItemCount(); ++i) + if (primaryClip.getItemAt(i).getText() != null) + return true; + } + } catch (Exception e) { + Log.e(QtTAG, "Failed to get clipboard data", e); } return false; } private static String getClipboardText() { - if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { - ClipData primaryClip = m_clipboardManager.getPrimaryClip(); - for (int i = 0; i < primaryClip.getItemCount(); ++i) - if (primaryClip.getItemAt(i).getText() != null) - return primaryClip.getItemAt(i).getText().toString(); + try { + if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { + ClipData primaryClip = m_clipboardManager.getPrimaryClip(); + for (int i = 0; i < primaryClip.getItemCount(); ++i) + if (primaryClip.getItemAt(i).getText() != null) + return primaryClip.getItemAt(i).getText().toString(); + } + } catch (Exception e) { + Log.e(QtTAG, "Failed to get clipboard data", e); } return ""; } private static void updatePrimaryClip(ClipData clipData) { - if (m_usePrimaryClip) { - ClipData clip = m_clipboardManager.getPrimaryClip(); - if (Build.VERSION.SDK_INT >= 26) { - if (m_addItemMethod == null) { - Class[] cArg = new Class[2]; - cArg[0] = ContentResolver.class; - cArg[1] = ClipData.Item.class; + try { + if (m_usePrimaryClip) { + ClipData clip = m_clipboardManager.getPrimaryClip(); + if (Build.VERSION.SDK_INT >= 26) { + if (m_addItemMethod == null) { + Class[] cArg = new Class[2]; + cArg[0] = ContentResolver.class; + cArg[1] = ClipData.Item.class; + try { + m_addItemMethod = m_clipboardManager.getClass().getMethod("addItem", cArg); + } catch (Exception e) { + } + } + } + if (m_addItemMethod != null) { try { - m_addItemMethod = m_clipboardManager.getClass().getMethod("addItem", cArg); + m_addItemMethod.invoke(m_activity.getContentResolver(), clipData.getItemAt(0)); } catch (Exception e) { + e.printStackTrace(); } + } else { + clip.addItem(clipData.getItemAt(0)); } - } - if (m_addItemMethod != null) { - try { - m_addItemMethod.invoke(m_activity.getContentResolver(), clipData.getItemAt(0)); - } catch (Exception e) { - e.printStackTrace(); - } + m_clipboardManager.setPrimaryClip(clip); } else { - clip.addItem(clipData.getItemAt(0)); + m_clipboardManager.setPrimaryClip(clipData); + m_usePrimaryClip = true; } - m_clipboardManager.setPrimaryClip(clip); - } else { - m_clipboardManager.setPrimaryClip(clipData); - m_usePrimaryClip = true; + } catch (Exception e) { + Log.e(QtTAG, "Failed to set clipboard data", e); } } @@ -785,22 +797,30 @@ public class QtNative public static boolean hasClipboardHtml() { - if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { - ClipData primaryClip = m_clipboardManager.getPrimaryClip(); - for (int i = 0; i < primaryClip.getItemCount(); ++i) - if (primaryClip.getItemAt(i).getHtmlText() != null) - return true; + try { + if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { + ClipData primaryClip = m_clipboardManager.getPrimaryClip(); + for (int i = 0; i < primaryClip.getItemCount(); ++i) + if (primaryClip.getItemAt(i).getHtmlText() != null) + return true; + } + } catch (Exception e) { + Log.e(QtTAG, "Failed to get clipboard data", e); } return false; } private static String getClipboardHtml() { - if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { - ClipData primaryClip = m_clipboardManager.getPrimaryClip(); - for (int i = 0; i < primaryClip.getItemCount(); ++i) - if (primaryClip.getItemAt(i).getHtmlText() != null) - return primaryClip.getItemAt(i).getHtmlText().toString(); + try { + if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { + ClipData primaryClip = m_clipboardManager.getPrimaryClip(); + for (int i = 0; i < primaryClip.getItemCount(); ++i) + if (primaryClip.getItemAt(i).getHtmlText() != null) + return primaryClip.getItemAt(i).getHtmlText().toString(); + } + } catch (Exception e) { + Log.e(QtTAG, "Failed to get clipboard data", e); } return ""; } @@ -816,11 +836,15 @@ public class QtNative public static boolean hasClipboardUri() { - if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { - ClipData primaryClip = m_clipboardManager.getPrimaryClip(); - for (int i = 0; i < primaryClip.getItemCount(); ++i) - if (primaryClip.getItemAt(i).getUri() != null) - return true; + try { + if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { + ClipData primaryClip = m_clipboardManager.getPrimaryClip(); + for (int i = 0; i < primaryClip.getItemCount(); ++i) + if (primaryClip.getItemAt(i).getUri() != null) + return true; + } + } catch (Exception e) { + Log.e(QtTAG, "Failed to get clipboard data", e); } return false; } @@ -828,11 +852,15 @@ public class QtNative private static String[] getClipboardUris() { ArrayList<String> uris = new ArrayList<String>(); - if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { - ClipData primaryClip = m_clipboardManager.getPrimaryClip(); - for (int i = 0; i < primaryClip.getItemCount(); ++i) - if (primaryClip.getItemAt(i).getUri() != null) - uris.add(primaryClip.getItemAt(i).getUri().toString()); + try { + if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { + ClipData primaryClip = m_clipboardManager.getPrimaryClip(); + for (int i = 0; i < primaryClip.getItemCount(); ++i) + if (primaryClip.getItemAt(i).getUri() != null) + uris.add(primaryClip.getItemAt(i).getUri().toString()); + } + } catch (Exception e) { + Log.e(QtTAG, "Failed to get clipboard data", e); } String[] strings = new String[uris.size()]; strings = uris.toArray(strings); diff --git a/src/corelib/serialization/qxmlstream.g b/src/corelib/serialization/qxmlstream.g index 10bfcd491c..5726bafb26 100644 --- a/src/corelib/serialization/qxmlstream.g +++ b/src/corelib/serialization/qxmlstream.g @@ -277,9 +277,19 @@ public: QHash<QStringView, Entity> entityHash; QHash<QStringView, Entity> parameterEntityHash; QXmlStreamSimpleStack<Entity *>entityReferenceStack; + int entityExpansionLimit = 4096; + int entityLength = 0; inline bool referenceEntity(Entity &entity) { if (entity.isCurrentlyReferenced) { - raiseWellFormedError(QXmlStream::tr("Recursive entity detected.")); + raiseWellFormedError(QXmlStream::tr("Self-referencing entity detected.")); + return false; + } + // entityLength represents the amount of additional characters the + // entity expands into (can be negative for e.g. &). It's used to + // avoid DoS attacks through recursive entity expansions + entityLength += entity.value.size() - entity.name.size() - 2; + if (entityLength > entityExpansionLimit) { + raiseWellFormedError(QXmlStream::tr("Entity expands to more characters than the entity expansion limit.")); return false; } entity.isCurrentlyReferenced = true; @@ -830,6 +840,8 @@ entity_done ::= ENTITY_DONE; /. case $rule_number: entityReferenceStack.pop()->isCurrentlyReferenced = false; + if (entityReferenceStack.isEmpty()) + entityLength = 0; clearSym(); break; ./ diff --git a/src/corelib/serialization/qxmlstream_p.h b/src/corelib/serialization/qxmlstream_p.h index 61f501f81b..31053f8e0b 100644 --- a/src/corelib/serialization/qxmlstream_p.h +++ b/src/corelib/serialization/qxmlstream_p.h @@ -774,9 +774,19 @@ public: QHash<QStringView, Entity> entityHash; QHash<QStringView, Entity> parameterEntityHash; QXmlStreamSimpleStack<Entity *>entityReferenceStack; + int entityExpansionLimit = 4096; + int entityLength = 0; inline bool referenceEntity(Entity &entity) { if (entity.isCurrentlyReferenced) { - raiseWellFormedError(QXmlStream::tr("Recursive entity detected.")); + raiseWellFormedError(QXmlStream::tr("Self-referencing entity detected.")); + return false; + } + // entityLength represents the amount of additional characters the + // entity expands into (can be negative for e.g. &). It's used to + // avoid DoS attacks through recursive entity expansions + entityLength += entity.value.size() - entity.name.size() - 2; + if (entityLength > entityExpansionLimit) { + raiseWellFormedError(QXmlStream::tr("Entity expands to more characters than the entity expansion limit.")); return false; } entity.isCurrentlyReferenced = true; @@ -1308,6 +1318,8 @@ bool QXmlStreamReaderPrivate::parse() case 10: entityReferenceStack.pop()->isCurrentlyReferenced = false; + if (entityReferenceStack.isEmpty()) + entityLength = 0; clearSym(); break; diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index b57deacb57..3d9dfd8359 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -47,6 +47,8 @@ #include <QScopedPointer> #include "qiosurfacegraphicsbuffer.h" +#include <unordered_map> + QT_BEGIN_NAMESPACE class QCocoaBackingStore : public QRasterBackingStore @@ -71,8 +73,9 @@ private: void redrawRoundedBottomCorners(CGRect) const; }; -class QCALayerBackingStore : public QCocoaBackingStore +class QCALayerBackingStore : public QObject, public QCocoaBackingStore { + Q_OBJECT public: QCALayerBackingStore(QWindow *window); ~QCALayerBackingStore(); @@ -119,6 +122,11 @@ private: QMacNotificationObserver m_backingPropertiesObserver; std::list<std::unique_ptr<GraphicsBuffer>> m_buffers; + + void flushSubWindow(QWindow *window); + std::unordered_map<QWindow*, std::unique_ptr<QCALayerBackingStore>> m_subWindowBackingstores; + void windowDestroyed(QObject *object); + bool m_clearSurfaceOnPaint = true; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 94eb471f5d..6aa0c0182f 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -394,7 +394,7 @@ void QCALayerBackingStore::beginPaint(const QRegion ®ion) // Although undocumented, QBackingStore::beginPaint expects the painted region // to be cleared before use if the window has a surface format with an alpha. // Fresh IOSurfaces are already cleared, so we don't need to clear those. - if (!bufferWasRecreated && window()->format().hasAlpha()) { + if (m_clearSurfaceOnPaint && !bufferWasRecreated && window()->format().hasAlpha()) { qCDebug(lcQpaBackingStore) << "Clearing" << region << "before use"; QPainter painter(m_buffers.back()->asImage()); painter.setCompositionMode(QPainter::CompositionMode_Source); @@ -523,9 +523,13 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, if (!prepareForFlush()) return; + if (flushedWindow != window()) { + flushSubWindow(flushedWindow); + return; + } + QMacAutoReleasePool pool; - NSView *backingStoreView = static_cast<QCocoaWindow *>(window()->handle())->view(); NSView *flushedView = static_cast<QCocoaWindow *>(flushedWindow->handle())->view(); // If the backingstore is just flushed, without being painted to first, then we may @@ -560,7 +564,7 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, // are committed as part of a display-cycle instead of on the next runloop pass. This // means CA won't try to throttle us if we flush too fast, and we'll coalesce our flush // with other pending view and layer updates. - backingStoreView.window.viewsNeedDisplay = YES; + flushedView.window.viewsNeedDisplay = YES; if (window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer) { // The private API [CALayer reloadValueForKeyPath:@"contents"] would be preferable, @@ -568,28 +572,10 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, flushedView.layer.contents = nil; } - if (flushedView == backingStoreView) { - qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface - << "to" << flushedView.layer << "of" << flushedView; - flushedView.layer.contents = backBufferSurface; - } else { - auto subviewRect = [flushedView convertRect:flushedView.bounds toView:backingStoreView]; - auto scale = flushedView.layer.contentsScale; - subviewRect = CGRectApplyAffineTransform(subviewRect, CGAffineTransformMakeScale(scale, scale)); - - // We make a copy of the image data up front, which means we don't - // need to mark the IOSurface as being in use. FIXME: Investigate - // if there's a cheaper way to get sub-image data to a layer. - m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess); - QImage subImage = m_buffers.back()->asImage()->copy(QRectF::fromCGRect(subviewRect).toRect()); - m_buffers.back()->unlock(); + qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface + << "to" << flushedView.layer << "of" << flushedView; - qCInfo(lcQpaBackingStore) << "Flushing" << subImage - << "to" << flushedView.layer << "of subview" << flushedView; - QCFType<CGImageRef> cgImage = CGImageCreateCopyWithColorSpace( - QCFType<CGImageRef>(subImage.toCGImage()), colorSpace()); - flushedView.layer.contents = (__bridge id)static_cast<CGImageRef>(cgImage); - } + flushedView.layer.contents = backBufferSurface; // Since we may receive multiple flushes before a new frame is started, we do not // swap any buffers just yet. Instead we check in the next beginPaint if the layer's @@ -601,6 +587,53 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, // the window server. } +void QCALayerBackingStore::flushSubWindow(QWindow *subWindow) +{ + qCInfo(lcQpaBackingStore) << "Flushing sub-window" << subWindow + << "via its own backingstore"; + + auto &subWindowBackingStore = m_subWindowBackingstores[subWindow]; + if (!subWindowBackingStore) { + subWindowBackingStore.reset(new QCALayerBackingStore(subWindow)); + QObject::connect(subWindow, &QObject::destroyed, this, &QCALayerBackingStore::windowDestroyed); + subWindowBackingStore->m_clearSurfaceOnPaint = false; + } + + auto subWindowSize = subWindow->size(); + static const auto kNoStaticContents = QRegion(); + subWindowBackingStore->resize(subWindowSize, kNoStaticContents); + + auto subWindowLocalRect = QRect(QPoint(), subWindowSize); + subWindowBackingStore->beginPaint(subWindowLocalRect); + + QPainter painter(subWindowBackingStore->m_buffers.back()->asImage()); + painter.setCompositionMode(QPainter::CompositionMode_Source); + + NSView *backingStoreView = static_cast<QCocoaWindow *>(window()->handle())->view(); + NSView *flushedView = static_cast<QCocoaWindow *>(subWindow->handle())->view(); + auto subviewRect = [flushedView convertRect:flushedView.bounds toView:backingStoreView]; + auto scale = flushedView.layer.contentsScale; + subviewRect = CGRectApplyAffineTransform(subviewRect, CGAffineTransformMakeScale(scale, scale)); + + m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess); + const QImage *backingStoreImage = m_buffers.back()->asImage(); + painter.drawImage(subWindowLocalRect, *backingStoreImage, QRectF::fromCGRect(subviewRect)); + m_buffers.back()->unlock(); + + painter.end(); + subWindowBackingStore->endPaint(); + subWindowBackingStore->flush(subWindow, subWindowLocalRect, QPoint()); + + qCInfo(lcQpaBackingStore) << "Done flushing sub-window" << subWindow; +} + +void QCALayerBackingStore::windowDestroyed(QObject *object) +{ + auto *window = static_cast<QWindow*>(object); + qCInfo(lcQpaBackingStore) << "Removing backingstore for sub-window" << window; + m_subWindowBackingstores.erase(window); +} + #ifndef QT_NO_OPENGL void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, QPlatformTextureList *textures, bool translucentBackground) @@ -734,4 +767,6 @@ QImage *QCALayerBackingStore::GraphicsBuffer::asImage() return &m_image; } +#include "moc_qcocoabackingstore.cpp" + QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qioscontext.mm b/src/plugins/platforms/ios/qioscontext.mm index 535e7d7aa6..cecbb17039 100644 --- a/src/plugins/platforms/ios/qioscontext.mm +++ b/src/plugins/platforms/ios/qioscontext.mm @@ -332,11 +332,8 @@ bool QIOSContext::verifyGraphicsHardwareAvailability() ); }); - if (applicationBackgrounded) { - static const char warning[] = "OpenGL ES calls are not allowed while an application is backgrounded"; - Q_ASSERT_X(!applicationBackgrounded, "QIOSContext", warning); - qCWarning(lcQpaGLContext, warning); - } + if (applicationBackgrounded) + qCWarning(lcQpaGLContext, "OpenGL ES calls are not allowed while an application is backgrounded"); return !applicationBackgrounded; } diff --git a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp index 8fdf91b090..1f9a0d575d 100644 --- a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp +++ b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp @@ -393,8 +393,6 @@ public: return true; } - QXmlStreamReader reader(&inputFile); - /* See testcases.dtd which reads: 'Nonvalidating parsers * must also accept "invalid" testcases, but validating ones must reject them.' */ if(type == QLatin1String("invalid") || type == QLatin1String("valid")) @@ -580,6 +578,8 @@ private slots: void roundTrip() const; void roundTrip_data() const; + void entityExpansionLimit() const; + private: static QByteArray readFile(const QString &filename); @@ -1756,6 +1756,25 @@ void tst_QXmlStream::roundTrip_data() const "</root>\n"; } +void tst_QXmlStream::entityExpansionLimit() const +{ + QString xml = QStringLiteral("<?xml version=\"1.0\"?>" + "<!DOCTYPE foo [" + "<!ENTITY a \"0123456789\" >" + "<!ENTITY b \"&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;\" >" + "<!ENTITY c \"&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;\" >" + "<!ENTITY d \"&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;\" >" + "]>" + "<foo>&d;&d;&d;</foo>"); + { + QXmlStreamReader reader(xml); + do { + reader.readNext(); + } while (!reader.atEnd()); + QCOMPARE(reader.error(), QXmlStreamReader::NotWellFormedError); + } +} + void tst_QXmlStream::roundTrip() const { QFETCH(QString, in); |