Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package qt6-qt5compat for openSUSE:Factory 
checked in at 2026-11-05 18:27:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/qt6-qt5compat (Old)
 and      /work/SRC/openSUSE:Factory/.qt6-qt5compat.new.3759290 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "qt6-qt5compat"

Thu Nov  5 18:27:47 2026 rev:46 rq:1394760 version:6.12.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/qt6-qt5compat/qt6-qt5compat.changes      
2026-10-18 19:21:41.579703133 +0200
+++ /work/SRC/openSUSE:Factory/.qt6-qt5compat.new.3759290/qt6-qt5compat.changes 
2026-11-05 18:27:49.385479744 +0100
@@ -1,0 +2,9 @@
+Tue Nov  3 17:58:43 UTC 2026 - Christophe Marin <[email protected]>
+
+- Add upstream changes (CVE-2026-100140, boo#1289130)
+  * CVE-2026-100140-qt5compat-6.12-0001.diff
+  * CVE-2026-100140-qt5compat-6.12-0002.diff
+  * CVE-2026-100140-qt5compat-6.12-0003.diff
+  * CVE-2026-100140-qt5compat-6.12-0004.diff
+
+-------------------------------------------------------------------

New:
----
  CVE-2026-100140-qt5compat-6.12-0001.diff
  CVE-2026-100140-qt5compat-6.12-0002.diff
  CVE-2026-100140-qt5compat-6.12-0003.diff
  CVE-2026-100140-qt5compat-6.12-0004.diff

----------(New B)----------
  New:- Add upstream changes (CVE-2026-100140, boo#1289130)
  * CVE-2026-100140-qt5compat-6.12-0001.diff
  * CVE-2026-100140-qt5compat-6.12-0002.diff
  New:  * CVE-2026-100140-qt5compat-6.12-0001.diff
  * CVE-2026-100140-qt5compat-6.12-0002.diff
  * CVE-2026-100140-qt5compat-6.12-0003.diff
  New:  * CVE-2026-100140-qt5compat-6.12-0002.diff
  * CVE-2026-100140-qt5compat-6.12-0003.diff
  * CVE-2026-100140-qt5compat-6.12-0004.diff
  New:  * CVE-2026-100140-qt5compat-6.12-0003.diff
  * CVE-2026-100140-qt5compat-6.12-0004.diff
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ qt6-qt5compat.spec ++++++
--- /var/tmp/diff_new_pack.AULRnt/_old  2026-11-05 18:27:57.268808019 +0100
+++ /var/tmp/diff_new_pack.AULRnt/_new  2026-11-05 18:27:57.270808102 +0100
@@ -34,6 +34,11 @@
 URL:            https://www.qt.io
 Source0:        
https://download.qt.io/official_releases/qt/%{short_version}/%{real_version}%{tar_suffix}/submodules/%{tar_name}-%{real_version}%{tar_suffix}.tar.xz
 Source99:       qt6-qt5compat-rpmlintrc
+# PATCH-FIX-UPSTREAM -- CVE-2026-100140
+Patch0:         CVE-2026-100140-qt5compat-6.12-0001.diff
+Patch1:         CVE-2026-100140-qt5compat-6.12-0002.diff
+Patch2:         CVE-2026-100140-qt5compat-6.12-0003.diff
+Patch3:         CVE-2026-100140-qt5compat-6.12-0004.diff
 BuildRequires:  fdupes
 BuildRequires:  pkgconfig
 BuildRequires:  cmake(Qt6Core) = %{real_version}

++++++ CVE-2026-100140-qt5compat-6.12-0001.diff ++++++
>From 2a41143bbae0f3a11f6f949aba164854d29dd4de Mon Sep 17 00:00:00 2001
From: Ivan Solovev <[email protected]>
Date: Wed, 09 Sep 2026 13:39:22 +0200
Subject: [PATCH] SAX: use ranged-for loops

In places where we do not need the index.
It is the part of porting the code from int to qsizetype.

Task-number: QTBUG-149566
Pick-to: 6.12.0 6.11 6.11.3 6.8 6.5
Change-Id: Ia24e05b4b54f0e4d3298b2067381eb48c3a68630
Reviewed-by: Marc Mutz <[email protected]>
(cherry picked from commit 6cae8a4aebc82fb59116d55a551c168b2cd9e053)
Reviewed-by: Qt Cherry-pick Bot <[email protected]>
---

diff --git a/src/core5/sax/qxml.cpp b/src/core5/sax/qxml.cpp
index cb7a953..4e5d472 100644
--- a/src/core5/sax/qxml.cpp
+++ b/src/core5/sax/qxml.cpp
@@ -7515,9 +7515,8 @@
                     {
                         // Bypassed
                         stringAddC(QLatin1Char('&'));
-                        for (int i=0; i<(int)reference.size(); i++) {
-                            stringAddC(reference[i]);
-                        }
+                        for (const QChar c : std::as_const(reference))
+                            stringAddC(c);
                         stringAddC(QLatin1Char(';'));
                         parseReference_charDataRead = true;
                     }
@@ -7537,9 +7536,8 @@
                 if (parseReference_context == InEntityValue) {
                     // Bypassed
                     stringAddC(QLatin1Char('&'));
-                    for (int i=0; i<(int)reference.size(); i++) {
-                        stringAddC(reference[i]);
-                    }
+                    for (const QChar c : std::as_const(reference))
+                        stringAddC(c);
                     stringAddC(QLatin1Char(';'));
                     parseReference_charDataRead = true;
                 } else {
@@ -7620,9 +7618,8 @@
                         {
                             // Bypassed
                             stringAddC(QLatin1Char('&'));
-                            for (int i=0; i<(int)reference.size(); i++) {
-                                stringAddC(reference[i]);
-                            }
+                            for (const QChar c : std::as_const(reference))
+                                stringAddC(c);
                             stringAddC(QLatin1Char(';'));
                             parseReference_charDataRead = true;
                         }

++++++ CVE-2026-100140-qt5compat-6.12-0002.diff ++++++
>From 308bb243a3b0319e5ab0d788cfd01b0cc9be8c7c Mon Sep 17 00:00:00 2001
From: Ivan Solovev <[email protected]>
Date: Wed, 9 Sep 2026 13:34:54 +0200
Subject: [PATCH] SAX: port internal implementation to qsizetype

The implementation was using int when referring to QString/QByteArray
sizes and indices. Use qsizetype consistently. These were all
overlooked when moving the code from Qt 5 to Qt5Compat module of
Qt 6.

This patch fixes at least one significant issue: a write past the
end of the buffer in updateValue().

The static updateValue() method computes the new length of the
underlying buffer by doing an addition of two int values without an
overflow check.

As a result, for XML documents with large strings in names, values, or
references, the calculation can overflow to a negative value. This
negative value will be passed to QString::resize(), which will result
in an empty string.

After that, we immediately call memcpy() into that string, passing the
original offset and the size of the new chunk - this will cause a write
past the end of the (empty) buffer.

Note that in practice this is not a problem in Qt 5 and 32-bit builds
of Qt 6, because in that cases QString cannot really contain INT_MAX
QChars (it is limited to INT_MAX *bytes*). So, we'll get a memory
allocation error faster than an int overflow and write past the end
of the buffer.

Get rid of storing the string size in a separate variable, and use
QString::size() in the updateValue() method, which returns the size
as qsizetype. This means that the next resize() call also computes
the new value as qsizetype.

After switching to qsizetype, the code would fail to allocate earlier
than an overflow would happen. This is in line with how all Qt
containers behave.

The unit-test generates a string with over 2 Gi of charactes and
makes sure that they all are properly parsed.
Due to the fact that the parser builds the internal buffers in chunks
of 256 QChars, and also because of the fact that the test actually
allocates at least 4 GiB of memory, we do not run it on CI.

Fixes: QTBUG-149566
Pick-to: 6.12.0 6.11 6.11.3 6.8 6.5
Change-Id: Id777560920ad0c867ae2aa3d44e96a25deee43a6
Reviewed-by: Marc Mutz <[email protected]>
(cherry picked from commit 4b72520e50cd04f2c29524f28bf77eefc81956fe)
Reviewed-by: Qt Cherry-pick Bot <[email protected]>
---
 src/core5/sax/qxml.cpp                        |  73 ++++++------
 src/core5/sax/qxml_p.h                        |  17 ++-
 .../qxmlsimplereader/tst_qxmlsimplereader.cpp | 107 ++++++++++++++++++
 3 files changed, 150 insertions(+), 47 deletions(-)

diff --git a/src/core5/sax/qxml.cpp b/src/core5/sax/qxml.cpp
index aa385ac..4f203a8 100644
--- a/src/core5/sax/qxml.cpp
+++ b/src/core5/sax/qxml.cpp
@@ -202,8 +202,8 @@ public:
 
     QString str;
     const QChar *unicode;
-    int pos;
-    int length;
+    qsizetype pos;
+    qsizetype length;
     bool nextReturnedEndOfData;
 #if QT_CONFIG(textcodec)
     QTextDecoder *encMapper;
@@ -533,7 +533,7 @@ QString QXmlNamespaceSupport::uri(const QString& prefix) 
const
 void QXmlNamespaceSupport::splitName(const QString& qname, QString& prefix,
                                      QString& localname) const
 {
-    int pos = qname.indexOf(QLatin1Char(':'));
+    auto pos = qname.indexOf(u':');
     if (pos == -1)
         pos = qname.size();
 
@@ -562,14 +562,10 @@ void QXmlNamespaceSupport::processName(const QString& 
qname,
         bool isAttribute,
         QString& nsuri, QString& localname) const
 {
-    int len = qname.size();
-    const QChar *data = qname.constData();
-    for (int pos = 0; pos < len; ++pos) {
-        if (data[pos] == QLatin1Char(':')) {
-            nsuri = uri(qname.left(pos));
-            localname = qname.mid(pos + 1);
-            return;
-        }
+    if (const auto pos = qname.indexOf(u':'); pos >= 0) {
+        nsuri = uri(qname.left(pos));
+        localname = qname.mid(pos + 1);
+        return;
     }
 
     // there was no ':'
@@ -1194,7 +1190,7 @@ void QXmlInputSource::fetchData()
             if (d->inputStream && d->inputStream->string()) {
                 QString *s = d->inputStream->string();
                 rawData = QByteArray(reinterpret_cast<const char 
*>(s->constData()),
-                                     int(s->size() * sizeof(QChar)));
+                                     s->size() * qsizetype(sizeof(QChar)));
             }
         } else if (device->isOpen() || device->open(QIODevice::ReadOnly)) {
             rawData.resize(BufferSize);
@@ -1218,18 +1214,18 @@ static QString extractEncodingDecl(const QString &text, 
bool *needMoreText)
 {
     *needMoreText = false;
 
-    int l = text.size();
-    const QLatin1String snip("<?xml", std::min(l, 5));
+    qsizetype l = text.size();
+    const QLatin1String snip("<?xml", std::min(l, qsizetype(5)));
     if (l > 0 && !text.startsWith(snip))
         return QString();
 
-    int endPos = text.indexOf(QLatin1Char('>'));
+    qsizetype endPos = text.indexOf(QLatin1Char('>'));
     if (endPos == -1) {
         *needMoreText = l < 255; // we won't look forever
         return QString();
     }
 
-    int pos = text.indexOf(QLatin1String("encoding"));
+    qsizetype pos = text.indexOf(QLatin1String("encoding"));
     if (pos == -1 || pos >= endPos)
         return QString();
 
@@ -2426,16 +2422,19 @@ inline bool QXmlSimpleReaderPrivate::atEnd()
 
 inline void QXmlSimpleReaderPrivate::stringClear()
 {
-    stringValueLen = 0; stringArrayPos = 0;
+    stringValue.resize(0);
+    stringArrayPos = 0;
 }
 inline void QXmlSimpleReaderPrivate::nameClear()
 {
-    nameValueLen = 0; nameArrayPos = 0;
+    nameValue.resize(0);
+    nameArrayPos = 0;
 }
 
 inline void QXmlSimpleReaderPrivate::refClear()
 {
-    refValueLen = 0; refArrayPos = 0;
+    refValue.resize(0);
+    refArrayPos = 0;
 }
 
 QXmlSimpleReaderPrivate::QXmlSimpleReaderPrivate(QXmlSimpleReader *reader)
@@ -4314,7 +4313,7 @@ QXmlSimpleReaderPrivate::FrameParseResult 
QXmlSimpleReaderPrivate::parseContentF
 
 bool QXmlSimpleReaderPrivate::reportEndEntities()
 {
-    int count = (int)xmlRefStack.size();
+    qsizetype count = xmlRefStack.size();
     while (count != 0 && xmlRefStack.top().isEmpty()) {
         if (contentHnd) {
             if (reportWhitespaceCharData || !string().simplified().isEmpty()) {
@@ -6301,7 +6300,7 @@ bool 
QXmlSimpleReaderPrivate::isExpandedEntityValueTooLarge(QString *errorMessag
                  ++referencedIt) {
                 const QString &entityName = referencedIt.key();
 
-                for (int i = 0; i < leftOvers.size() && i != -1; ) {
+                for (qsizetype i = 0; i < leftOvers.size() && i != -1; ) {
                     entityNameBuffer = QLatin1Char('&') + entityName + 
QLatin1Char(';');
 
                     i = leftOvers.indexOf(entityNameBuffer, i);
@@ -6316,19 +6315,19 @@ bool 
QXmlSimpleReaderPrivate::isExpandedEntityValueTooLarge(QString *errorMessag
         }
     }
 
-    for (QHash<QString, QHash<QString, int> >::const_iterator entityIt = 
referencesToOtherEntities.constBegin();
+    for (QHash<QString, QHash<QString, qsizetype> >::const_iterator entityIt = 
referencesToOtherEntities.constBegin();
          entityIt != referencesToOtherEntities.constEnd();
          ++entityIt) {
         const QString &entity = entityIt.key();
 
-        QHash<QString, int>::iterator expandedIt = expandedSizes.find(entity);
+        QHash<QString, qsizetype>::iterator expandedIt = 
expandedSizes.find(entity);
         if (expandedIt == expandedSizes.end()) {
             expandedIt = expandedSizes.insert(entity, 
literalEntitySizes.value(entity));
-            for (QHash<QString, int>::const_iterator referenceIt = 
entityIt->constBegin();
+            for (QHash<QString, qsizetype>::const_iterator referenceIt = 
entityIt->constBegin();
                  referenceIt != entityIt->constEnd();
                  ++referenceIt) {
                 const QString &referenceTo = referenceIt.key();
-                const int references = 
referencesToOtherEntities.value(entity).value(referenceTo);
+                const qsizetype references = 
referencesToOtherEntities.value(entity).value(referenceTo);
                 // The total size of an entity's value is the expanded size of 
all of its referenced entities, plus its literal size.
                 *expandedIt += expandedSizes.value(referenceTo) * references + 
literalEntitySizes.value(referenceTo) * references;
             }
@@ -7487,7 +7486,7 @@ bool QXmlSimpleReaderPrivate::insertXmlRef(const QString 
&data, const QString &n
     } else {
         xmlRefStack.push(XmlRef(name, data));
     }
-    int n = qMax(parameterEntities.size(), entities.size());
+    qsizetype n = qMax(parameterEntities.size(), entities.size());
     if (xmlRefStack.size() > n+1) {
         // recursive entities
         reportParseError(QLatin1String(XMLERR_RECURSIVEENTITIES));
@@ -7507,7 +7506,7 @@ bool QXmlSimpleReaderPrivate::insertXmlRef(const QString 
&data, const QString &n
 */
 void QXmlSimpleReaderPrivate::next()
 {
-    int count = xmlRefStack.size();
+    qsizetype count = xmlRefStack.size();
     while (count != 0) {
         if (xmlRefStack.top().isEmpty()) {
             xmlRefStack.pop_back();
@@ -7685,47 +7684,47 @@ void 
QXmlSimpleReaderPrivate::pushParseState(ParseFunction function, int state)
     parseStack->push(ps);
 }
 
-inline static void updateValue(QString &value, const QChar *array, int 
&arrayPos, int &valueLen)
+inline static void updateValue(QString &value, const QChar *array, int 
&arrayPos)
 {
-    value.resize(valueLen + arrayPos);
-    memcpy(value.data() + valueLen, array, arrayPos * sizeof(QChar));
-    valueLen += arrayPos;
+    const auto sz = value.size();
+    value.resize(sz + arrayPos);
+    memcpy(value.data() + sz, array, arrayPos * sizeof(QChar));
     arrayPos = 0;
 }
 
 // use buffers instead of QString::operator+= when single characters are read
 const QString& QXmlSimpleReaderPrivate::string()
 {
-    updateValue(stringValue, stringArray, stringArrayPos, stringValueLen);
+    updateValue(stringValue, stringArray, stringArrayPos);
     return stringValue;
 }
 const QString& QXmlSimpleReaderPrivate::name()
 {
-    updateValue(nameValue, nameArray, nameArrayPos, nameValueLen);
+    updateValue(nameValue, nameArray, nameArrayPos);
     return nameValue;
 }
 const QString& QXmlSimpleReaderPrivate::ref()
 {
-    updateValue(refValue, refArray, refArrayPos, refValueLen);
+    updateValue(refValue, refArray, refArrayPos);
     return refValue;
 }
 
 void QXmlSimpleReaderPrivate::stringAddC(QChar ch)
 {
     if (stringArrayPos == 256)
-        updateValue(stringValue, stringArray, stringArrayPos, stringValueLen);
+        updateValue(stringValue, stringArray, stringArrayPos);
     stringArray[stringArrayPos++] = ch;
 }
 void QXmlSimpleReaderPrivate::nameAddC(QChar ch)
 {
     if (nameArrayPos == 256)
-        updateValue(nameValue, nameArray, nameArrayPos, nameValueLen);
+        updateValue(nameValue, nameArray, nameArrayPos);
     nameArray[nameArrayPos++] = ch;
 }
 void QXmlSimpleReaderPrivate::refAddC(QChar ch)
 {
     if (refArrayPos == 256)
-        updateValue(refValue, refArray, refArrayPos, refValueLen);
+        updateValue(refValue, refArray, refArrayPos);
     refArray[refArrayPos++] = ch;
 }
 
diff --git a/src/core5/sax/qxml_p.h b/src/core5/sax/qxml_p.h
index c9e18ae..b8c3809 100644
--- a/src/core5/sax/qxml_p.h
+++ b/src/core5/sax/qxml_p.h
@@ -79,7 +79,7 @@ private:
         QChar next() { return value.at(index++); }
         QString name;
         QString value;
-        int index;
+        qsizetype index;
     };
     friend class QTypeInfo<XmlRef>;
     QStack<XmlRef> xmlRefStack;
@@ -171,27 +171,24 @@ private:
     QXmlInputSource *inputSource;
 
     QChar c; // the character at reading position
-    int   lineNr; // number of line
-    int   columnNr; // position in line
+    qsizetype   lineNr; // number of line
+    qsizetype   columnNr; // position in line
 
     QChar   nameArray[256]; // only used for names
     QString nameValue; // only used for names
     int     nameArrayPos;
-    int     nameValueLen;
     QChar   refArray[256]; // only used for references
     QString refValue; // only used for references
     int     refArrayPos;
-    int     refValueLen;
     QChar   stringArray[256]; // used for any other strings that are parsed
     QString stringValue; // used for any other strings that are parsed
     int     stringArrayPos;
-    int     stringValueLen;
     QString emptyStr;
 
-    QHash<QString, int> literalEntitySizes;
-    // The entity at (QMap<QString,) referenced the entities at 
(QMap<QString,) (int>) times.
-    QHash<QString, QHash<QString, int> > referencesToOtherEntities;
-    QHash<QString, int> expandedSizes;
+    QHash<QString, qsizetype> literalEntitySizes;
+    // The entity at (QMap<QString,) referenced the entities at 
(QMap<QString,) (qsizetype>) times.
+    QHash<QString, QHash<QString, qsizetype> > referencesToOtherEntities;
+    QHash<QString, qsizetype> expandedSizes;
     // The limit to the amount of times the DTD parsing functions can be called
     // for the DTD currently being parsed.
     static const int dtdRecursionLimit = 2;
diff --git a/tests/auto/core5/sax/qxmlsimplereader/tst_qxmlsimplereader.cpp 
b/tests/auto/core5/sax/qxmlsimplereader/tst_qxmlsimplereader.cpp
index aac1f8c..0c8a05e 100644
--- a/tests/auto/core5/sax/qxmlsimplereader/tst_qxmlsimplereader.cpp
+++ b/tests/auto/core5/sax/qxmlsimplereader/tst_qxmlsimplereader.cpp
@@ -14,6 +14,8 @@
 #include <qbuffer.h>
 #include "parser/parser.h"
 
+#include <limits>
+
 static const char *const inputString = "<!DOCTYPE inferno [<!ELEMENT inferno 
(circle+)><!ELEMENT circle (#PCDATA)>]><inferno><circle /><circle /></inferno>";
 static const char *const refString = 
"setDocumentLocator(locator={columnNumber=1, 
lineNumber=1})\nstartDocument()\nstartDTD(name=\"inferno\", publicId=\"\", 
systemId=\"\")\nendDTD()\nstartElement(namespaceURI=\"\", 
localName=\"inferno\", qName=\"inferno\", 
atts=[])\nstartElement(namespaceURI=\"\", localName=\"circle\", 
qName=\"circle\", atts=[])\nendElement(namespaceURI=\"\", localName=\"circle\", 
qName=\"circle\")\nstartElement(namespaceURI=\"\", localName=\"circle\", 
qName=\"circle\", atts=[])\nendElement(namespaceURI=\"\", localName=\"circle\", 
qName=\"circle\")\nendElement(namespaceURI=\"\", localName=\"inferno\", 
qName=\"inferno\")\nendDocument()\n";
 
@@ -132,6 +134,8 @@ class tst_QXmlSimpleReader : public QObject
         void deepElementNestingIncremental_data();
         void deepElementNestingIncremental();
 
+        void valueLongerThanIntMaxDoesNotCrash();
+
     private:
         static QDomDocument fromByteArray(const QString &title, const 
QByteArray &ba, bool *ok);
         XmlServer *server;
@@ -963,5 +967,108 @@ void tst_QXmlSimpleReader::deepElementNestingIncremental()
     QCOMPARE(handler.depth, 0);
 }
 
+void tst_QXmlSimpleReader::valueLongerThanIntMaxDoesNotCrash()
+{
+    if constexpr (QT_POINTER_SIZE == 4)
+        QSKIP("An allocated QString does not fit into a 32 bit address 
space.");
+
+    if (qgetenv("QTEST_ENVIRONMENT").split(' ').contains("ci"))
+        QSKIP("Too slow and consumes at least 4 GiB of memory - skipping on 
CI.");
+
+
+    // Serves prefix + runLength copies of runChar + suffix in chunks to avoid
+    // storing a multi-Gb input
+    class RepeatedRunDevice : public QIODevice
+    {
+    public:
+        RepeatedRunDevice(const QByteArray &prefix, qint64 runLength, const 
QByteArray &suffix,
+                          char runChar = 'a')
+            : m_prefix(prefix), m_suffix(suffix), m_runLength(runLength), 
m_runChar(runChar)
+        {
+        }
+
+        qint64 size() const override { return m_prefix.size() + m_runLength + 
m_suffix.size(); }
+
+    protected:
+        qint64 readData(char *data, qint64 maxSize) override
+        {
+            const qint64 runStart = m_prefix.size();
+            const qint64 suffixStart = runStart + m_runLength;
+            qint64 offset = pos();
+            qint64 written = 0;
+
+            while (written < maxSize && offset < size()) {
+                const char *source = nullptr;
+                qint64 available = 0;
+                if (offset < runStart) {
+                    source = m_prefix.constData() + offset;
+                    available = runStart - offset;
+                } else if (offset < suffixStart) {
+                    available = suffixStart - offset;
+                } else {
+                    source = m_suffix.constData() + (offset - suffixStart);
+                    available = size() - offset;
+                }
+
+                const qint64 chunk = qMin(available, maxSize - written);
+                if (source)
+                    memcpy(data + written, source, chunk);
+                else
+                    memset(data + written, m_runChar, chunk);
+                written += chunk;
+                offset += chunk;
+            }
+
+            return written;
+        }
+
+        qint64 writeData(const char *, qint64) override { return -1; }
+
+    private:
+        QByteArray m_prefix;
+        QByteArray m_suffix;
+        qint64 m_runLength;
+        char m_runChar;
+    };
+
+    class CharacterCountingHandler : public QXmlDefaultHandler
+    {
+    public:
+        bool characters(const QString &ch) override
+        {
+            characterCount += ch.size();
+            return true;
+        }
+
+        bool fatalError(const QXmlParseException &exception) override
+        {
+            errorMessage = exception.message();
+            return false;
+        }
+
+        qsizetype characterCount = 0;
+        QString errorMessage;
+    };
+
+    // The buffer is flushed every 256 characters, so a handful of characters
+    // past INT_MAX is enough to be sure the boundary is crossed.
+    constexpr auto runLength = std::numeric_limits<int>::max() + qint64{10};
+
+    CharacterCountingHandler handler;
+    QXmlSimpleReader reader;
+    reader.setContentHandler(&handler);
+    reader.setErrorHandler(&handler);
+
+    QT_TRY {
+        RepeatedRunDevice device("<d>", runLength, "</d>");
+        QVERIFY(device.open(QIODevice::ReadOnly));
+        QXmlInputSource source(&device);
+        QVERIFY2(reader.parse(source), qPrintable(handler.errorMessage));
+        QCOMPARE(handler.characterCount, runLength);
+    } QT_CATCH (const std::bad_alloc &) {
+        QSKIP("Not enough memory to accumulate INT_MAX characters.");
+    }
+}
+
 QTEST_MAIN(tst_QXmlSimpleReader)
 #include "tst_qxmlsimplereader.moc"
-- 
2.56.0


++++++ CVE-2026-100140-qt5compat-6.12-0003.diff ++++++
>From 9c60ad33f410ad657a6da39f812522c1af860b57 Mon Sep 17 00:00:00 2001
From: Ivan Solovev <[email protected]>
Date: Wed, 9 Sep 2026 13:48:08 +0200
Subject: [PATCH] SAX: avoid integer truncation when reporting column and line

We ported the internal implementation to qsizetype, but the public API
of QXmlLocator still reports the column and line as int.

To avoid integer truncation when reporting results, explicitly check
if the reported value is larger than INT_MAX and report -1 if it is.
This is in line with the documentation, which explicitly states that -1
is returned when no column/line number is available.

Task-number: QTBUG-149566
Pick-to: 6.12.0 6.11 6.11.3 6.8 6.5
Change-Id: I277ab4ed390d63348d426619a7c01a079d2a057f
Reviewed-by: Marc Mutz <[email protected]>
(cherry picked from commit 0d2c086cb8cfa6833b82abb200b53a51a0321bdf)
Reviewed-by: Qt Cherry-pick Bot <[email protected]>
---
 src/core5/sax/qxml.cpp | 29 +++++++++++++++++++++++------
 src/core5/sax/qxml_p.h |  1 +
 2 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/src/core5/sax/qxml.cpp b/src/core5/sax/qxml.cpp
index 4f203a8..dd1880a 100644
--- a/src/core5/sax/qxml.cpp
+++ b/src/core5/sax/qxml.cpp
@@ -17,6 +17,8 @@
 #include <QtCore/qvarlengtharray.h>
 #include <qdebug.h>
 
+#include <climits>
+
 #ifdef Q_CC_BOR // borland 6 finds bogus warnings when building this file in 
uic3
 #    pragma warn -8080
 #endif
@@ -390,6 +392,21 @@ QXmlLocator::~QXmlLocator()
     number available.
 */
 
+/*
+    The public API returns position as int.
+    Returns the value + 1, since human-readable line and column numbers start
+    from 1.
+
+    Report -1 if the value does not fit into the int range, in line with the
+    columnNumber() and lineNumber() docs.
+*/
+static int reportedPosition(qsizetype position)
+{
+    if (position >= INT_MAX - 1)
+        return -1;
+    return int(position + 1);
+}
+
 class QXmlSimpleReaderLocator : public QXmlLocator
 {
 public:
@@ -403,11 +420,11 @@ public:
 
     int columnNumber() const override
     {
-        return (reader->d_ptr->columnNr == -1 ? -1 : reader->d_ptr->columnNr + 
1);
+        return (reader->d_ptr->columnNr == -1 ? -1 : 
reportedPosition(reader->d_ptr->columnNr));
     }
     int lineNumber() const override
     {
-        return (reader->d_ptr->lineNr == -1 ? -1 : reader->d_ptr->lineNr + 1);
+        return (reader->d_ptr->lineNr == -1 ? -1 : 
reportedPosition(reader->d_ptr->lineNr));
     }
 
 private:
@@ -7625,12 +7642,12 @@ void QXmlSimpleReaderPrivate::reportParseError(const 
QString& error)
     this->error = error;
     if (errorHnd) {
         if (this->error.isNull()) {
-            const QXmlParseException ex(QLatin1String(XMLERR_OK), columnNr+1, 
lineNr+1,
-                                        thisPublicId, thisSystemId);
+            const QXmlParseException ex(QLatin1String(XMLERR_OK), 
reportedPosition(columnNr),
+                                        reportedPosition(lineNr), 
thisPublicId, thisSystemId);
             errorHnd->fatalError(ex);
         } else {
-            const QXmlParseException ex(this->error, columnNr+1, lineNr+1,
-                                        thisPublicId, thisSystemId);
+            const QXmlParseException ex(this->error, 
reportedPosition(columnNr),
+                                        reportedPosition(lineNr), 
thisPublicId, thisSystemId);
             errorHnd->fatalError(ex);
         }
     }
diff --git a/src/core5/sax/qxml_p.h b/src/core5/sax/qxml_p.h
index b8c3809..e0fad65 100644
--- a/src/core5/sax/qxml_p.h
+++ b/src/core5/sax/qxml_p.h
@@ -171,6 +171,7 @@ private:
     QXmlInputSource *inputSource;
 
     QChar c; // the character at reading position
+    // The public API is int-based, range-checked in reportedPosition().
     qsizetype   lineNr; // number of line
     qsizetype   columnNr; // position in line
 
-- 
2.56.0


++++++ CVE-2026-100140-qt5compat-6.12-0004.diff ++++++
>From f6fd26cde72f2948d760acf8ffda86587aa6eea7 Mon Sep 17 00:00:00 2001
From: Ivan Solovev <[email protected]>
Date: Wed, 9 Sep 2026 13:54:33 +0200
Subject: [PATCH] QXmlAttributes: do not allow more than INT_MAX attributes

The implementation stores the attributes in a QList, meaning that its
size is represented as qsizetype.

However, the public API, like index() and length() only returns int.
This may lead to a (very unlikely in practice) situation when some of
the parsed attributes are not accessible to the user.

To avoid such situations, this patch limits the amount of attributes
for a single element to INT_MAX. If a document contains an element with
more than INT_MAX attibutes, it is considered malformed, and an error
is reported.

Task-number: QTBUG-149566
Pick-to: 6.12.0 6.11 6.11.3 6.8 6.5
Change-Id: I4d930a4c8917154d895311b96bbb3904d79d6495
Reviewed-by: Marc Mutz <[email protected]>
(cherry picked from commit a8def6219491537fd9dbdb74d98b8f67946ad82d)
Reviewed-by: Qt Cherry-pick Bot <[email protected]>
---
 src/core5/sax/qxml.cpp | 36 +++++++++++++++++++++++++++++++++---
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/src/core5/sax/qxml.cpp b/src/core5/sax/qxml.cpp
index dd1880a..2c36fb9 100644
--- a/src/core5/sax/qxml.cpp
+++ b/src/core5/sax/qxml.cpp
@@ -745,6 +745,9 @@ QXmlAttributes::~QXmlAttributes()
 */
 int QXmlAttributes::index(const QString& qName) const
 {
+#if QT_POINTER_SIZE != 4
+    Q_PRE(attList.size() <= INT_MAX);
+#endif
     for (int i = 0; i < attList.size(); ++i) {
         if (attList.at(i).qname == qName)
             return i;
@@ -756,6 +759,9 @@ int QXmlAttributes::index(const QString& qName) const
   */
 int QXmlAttributes::index(QLatin1String qName) const
 {
+#if QT_POINTER_SIZE != 4
+    Q_PRE(attList.size() <= INT_MAX);
+#endif
     for (int i = 0; i < attList.size(); ++i) {
         if (attList.at(i).qname == qName)
             return i;
@@ -776,6 +782,9 @@ int QXmlAttributes::index(QLatin1String qName) const
 */
 int QXmlAttributes::index(const QString& uri, const QString& localPart) const
 {
+#if QT_POINTER_SIZE != 4
+    Q_PRE(attList.size() <= INT_MAX);
+#endif
     for (int i = 0; i < attList.size(); ++i) {
         const Attribute &att = attList.at(i);
         if (att.uri == uri && att.localname == localPart)
@@ -944,6 +953,8 @@ void QXmlAttributes::clear()
 */
 void QXmlAttributes::append(const QString &qName, const QString &uri, const 
QString &localPart, const QString &value)
 {
+    Q_PRE(attList.size() < INT_MAX);
+
     Attribute att;
     att.qname = qName;
     att.uri = uri;
@@ -3983,6 +3994,15 @@ bool QXmlSimpleReaderPrivate::processElementAttribute()
     const QString &name = QXmlSimpleReaderPrivate::name();
     const QString &string = QXmlSimpleReaderPrivate::string();
 
+    auto tryAppend = [this](const QString &qName, const QString &uri,
+                            const QString &localPart, const QString &value) {
+        if (attList.length() < INT_MAX) {
+            attList.append(qName, uri, localPart, value);
+            return true;
+        }
+        return false;
+    };
+
     // add the attribute to the list
     if (useNamespaces) {
         // is it a namespace declaration?
@@ -3994,7 +4014,11 @@ bool QXmlSimpleReaderPrivate::processElementAttribute()
                 // according to http://www.w3.org/2000/xmlns/, the "prefix"
                 // xmlns maps to the namespace name
                 // http://www.w3.org/2000/xmlns/
-                attList.append(name, 
QLatin1String("http://www.w3.org/2000/xmlns/";), lname, string);
+                if (!tryAppend(name, 
QLatin1String("http://www.w3.org/2000/xmlns/";),
+                               lname, string)) {
+                    
reportParseError(QLatin1String(XMLERR_ERRORPARSINGELEMENT));
+                    return false;
+                }
             }
             // call the handler for prefix mapping
             if (contentHnd) {
@@ -4006,11 +4030,17 @@ bool QXmlSimpleReaderPrivate::processElementAttribute()
         } else {
             // no namespace delcaration
             namespaceSupport.processName(name, true, uri, lname);
-            attList.append(name, uri, lname, string);
+            if (!tryAppend(name, uri, lname, string)) {
+                reportParseError(QLatin1String(XMLERR_ERRORPARSINGELEMENT));
+                return false;
+            }
         }
     } else {
         // no namespace support
-        attList.append(name, uri, lname, string);
+        if (!tryAppend(name, uri, lname, string)) {
+            reportParseError(QLatin1String(XMLERR_ERRORPARSINGELEMENT));
+            return false;
+        }
     }
     return true;
 }
-- 
2.56.0

Reply via email to