From 5983d6c56406268f2f2b7ee5029b2e3ba7d99189 Mon Sep 17 00:00:00 2001 From: Debian Qt/KDE Maintainers Date: Thu, 11 Dec 2025 13:02:24 +0300 Subject: [PATCH] rich text: limit size of text object Origin: upstream, https://code.qt.io/cgit/qt/qtdeclarative.git/commit/?id=144ce34e846b3f73 Backported to 5.15 by Dmitry Shachnev: validate allocation manually instead of using QImageIOHandler::allocateImage(). Last-Update: 2025-12-11 When we draw a text object, we need to store this in RAM since the QTextObjectInterface is QPainter-based. This could lead to over-allocation if the text object size was set to be very large. We use the existing image IO infrastructure for making sure allocations are within reasonable (and configurable) limits. Gbp-Pq: Name CVE-2025-12385-part2.patch --- src/quick/items/qquicktextnodeengine.cpp | 17 ++-- .../auto/quick/qquicktext/tst_qquicktext.cpp | 81 +++++++++++++++++++ 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp index 7e522406a..c6e06fd16 100644 --- a/src/quick/items/qquicktextnodeengine.cpp +++ b/src/quick/items/qquicktextnodeengine.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -464,12 +465,16 @@ void QQuickTextNodeEngine::addTextObject(const QTextBlock &block, const QPointF } } - if (image.isNull()) { - image = QImage(size.toSize(), QImage::Format_ARGB32_Premultiplied); - image.fill(Qt::transparent); - { - QPainter painter(&image); - handler->drawObject(&painter, image.rect(), textDocument, pos, format); + if (image.isNull() && !size.isEmpty()) { + QImageData::ImageSizeParameters szp = + QImageData::calculateImageParameters(size.width(), size.height(), 32); + if (szp.isValid() && szp.totalSize <= 268435456L /* 256 MB */) { + image = QImage(size.toSize(), QImage::Format_ARGB32_Premultiplied); + image.fill(Qt::transparent); + { + QPainter painter(&image); + handler->drawObject(&painter, image.rect(), textDocument, pos, format); + } } } diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index 03570b43c..e37dc4b9a 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -133,6 +133,8 @@ private slots: void imgTagsElide(); void imgTagsUpdates(); void imgTagsError(); + void imgSize_data(); + void imgSize(); void fontSizeMode_data(); void fontSizeMode(); void fontSizeModeMultiline_data(); @@ -3237,6 +3239,85 @@ void tst_qquicktext::imgTagsError() delete textObject; } +void tst_qquicktext::imgSize_data() +{ + QTest::addColumn("url"); + QTest::addColumn("width"); + QTest::addColumn("height"); + QTest::addColumn("format"); + + QTest::newRow("negative (styled text)") << QStringLiteral("images/starfish_2.png") + << qint64(-0x7FFFFF) + << qint64(-0x7FFFFF) + << QQuickText::StyledText; + QTest::newRow("negative (rich text)") << QStringLiteral("images/starfish_2.png") + << qint64(-0x7FFFFF) + << qint64(-0x7FFFFF) + << QQuickText::RichText; + QTest::newRow("large (styled text)") << QStringLiteral("images/starfish_2.png") + << qint64(0x7FFFFF) + << qint64(0x7FFFFF) + << QQuickText::StyledText; + QTest::newRow("large (right text)") << QStringLiteral("images/starfish_2.png") + << qint64(0x7FFFFF) + << qint64(0x7FFFFF) + << QQuickText::RichText; + QTest::newRow("medium (styled text)") << QStringLiteral("images/starfish_2.png") + << qint64(0x10000) + << qint64(0x10000) + << QQuickText::StyledText; + QTest::newRow("medium (right text)") << QStringLiteral("images/starfish_2.png") + << qint64(0x10000) + << qint64(0x10000) + << QQuickText::RichText; + QTest::newRow("large non-existent (styled text)") << QStringLiteral("a") + << qint64(0x7FFFFF) + << qint64(0x7FFFFF) + << QQuickText::StyledText; + QTest::newRow("medium non-existent (styled text)") << QStringLiteral("a") + << qint64(0x10000) + << qint64(0x10000) + << QQuickText::StyledText; + QTest::newRow("out-of-bounds non-existent (styled text)") << QStringLiteral("a") + << (qint64(INT_MAX) + 1) + << (qint64(INT_MAX) + 1) + << QQuickText::StyledText; + QTest::newRow("large non-existent (rich text)") << QStringLiteral("a") + << qint64(0x7FFFFF) + << qint64(0x7FFFFF) + << QQuickText::RichText; + QTest::newRow("medium non-existent (rich text)") << QStringLiteral("a") + << qint64(0x10000) + << qint64(0x10000) + << QQuickText::RichText; +} + +void tst_qquicktext::imgSize() +{ + QFETCH(QString, url); + QFETCH(qint64, width); + QFETCH(qint64, height); + QFETCH(QQuickText::TextFormat, format); + + // Reusing imgTagsUpdates.qml here, since it is just an empty Text component + QScopedPointer window(createView(testFile("imgTagsUpdates.qml"))); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QScopedPointer myText(window->rootObject()->findChild("myText")); + QVERIFY(myText); + + myText->setTextFormat(format); + + QString imgStr = QStringLiteral("") + .arg(url) + .arg(width) + .arg(height); + myText->setText(imgStr); + + QVERIFY(QQuickTest::qWaitForItemPolished(myText.data())); +} + void tst_qquicktext::fontSizeMode_data() { QTest::addColumn("text"); -- 2.30.2