From aff163c0589bd3c089ab0fe5cfc8a8708319fdc9 Mon Sep 17 00:00:00 2001
From: Ivan Solovev <ivan.solovev@qt.io>
Date: Mon, 24 Aug 2026 12:48:02 +0200
Subject: [PATCH] QTextCodec: fix silent truncation of input data to int

The QTextCodec APIs did not account for the fact that QStringView,
QString, and QByteArray uses qsizetype, not int, to represent its
size.

As a result, the size is silently truncated when passing to the
underlying API, which can produce negative values for inputs
larger than INT_MAX. This can lead to all sorts of surprising
results, depending on the codec implementation.

Fix by explicitly checking the input size against INT_MAX. Raise
a warning and return early if the size is greater than this value.

The toUnicode(const char *) overload is special. It is using qstrlen()
to calculate the input size.
The qstrlen() method returns size_t (or uint in 5.15), which is then
truncated to int, and we get the same problem.

Since qstrlen() in 5.15 is broken (returning uint instead of size_t),
fix by using strlen() directly and comparing the result against
INT_MAX. Raise a warning and return early if the size is greater than
this value.

We could theoretically rewrite the code to do the conversion in a loop,
but since it is a compatibility code, the valid usecases are supposed
to use is with inputs less than INT_MAX anyway. If it is required to
deal with a larger input, the chunking can be implemented at the user
side.

[ChangeLog][QTextCodec] Fixed a bug in toUnicode(), fromUnicode(),
and canEncode() methods that could silently truncate the size of
input data if it exceeded INT_MAX. The updated code now raises a
warning and returns an empty QString/QByteArray immediately. To
process long inputs, implement chunking of the input data on the
client side, using the toUnicode() and fromUnicode() overloads that
take the ConverterState as the last parameter.

Task-number: QTBUG-149424
Pick-to: 6.11 6.8 6.5 5.15
Change-Id: I25f16013ca35685ffb028a3bfbaa325b5b828f64
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
(cherry picked from commit 8631aea33f3de13c23fcd740d8900c775c18ac00)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
---

diff --git a/src/core5/codecs/qtextcodec.cpp b/src/core5/codecs/qtextcodec.cpp
index c574ad0..0d7986e 100644
--- a/src/core5/codecs/qtextcodec.cpp
+++ b/src/core5/codecs/qtextcodec.cpp
@@ -13,6 +13,7 @@
 #include "qfile.h"
 #include "qlist.h"
 #include <private/qlocking_p.h>
+#include <QtCore/qloggingcategory.h>
 #include "qstringlist.h"
 #include "qvarlengtharray.h"
 
@@ -59,6 +60,8 @@
 
 QT_BEGIN_NAMESPACE
 
+Q_STATIC_LOGGING_CATEGORY(lcTextCodec, "qt.core5compat.qtextcodec");
+
 // in qstring.cpp:
 void qt_from_latin1(char16_t *dst, const char *str, size_t size) noexcept;
 
@@ -69,6 +72,16 @@
 
 Q_GLOBAL_STATIC(QTextCodecData, textCodecData)
 
+static bool inputFitsIntoInt(quint64 input)
+{
+    if (input > uint{(std::numeric_limits<int>::max)()}) {
+        qCWarning(lcTextCodec, "The input data is too long (%llu > INT_MAX). "
+                               "Pass it in smaller chunks instead.", qulonglong(input));
+        return false;
+    }
+    return true;
+}
+
 QTextCodecData::QTextCodecData()
     : codecForLocale(nullptr)
 {
@@ -783,12 +796,24 @@
     The \a state of the convertor used is updated.
 */
 
+/*
+//! [to-from-unicode-long-input]
+    \note if the input exceeds \c {INT_MAX}, this function does not perform
+    any conversion and returns an empty \1. For longer inputs, implement the
+    chunking on the calling side, using the overload that takes ConverterState.
+//! [to-from-unicode-long-input]
+*/
+
 /*!
     Converts \a str from Unicode to the encoding of this codec, and
     returns the result in a QByteArray.
+
+    \include qtextcodec.cpp {to-from-unicode-long-input} {QByteArray}
 */
 QByteArray QTextCodec::fromUnicode(const QString& str) const
 {
+    if (!inputFitsIntoInt(str.size()))
+        return QByteArray();
     ConverterState state = DefaultConversion | Flag::Stateless;
     return convertFromUnicode(str.constData(), str.size(), &state);
 }
@@ -799,9 +824,13 @@
 
     Converts \a str from Unicode to the encoding of this codec, and
     returns the result in a QByteArray.
+
+    \include qtextcodec.cpp {to-from-unicode-long-input} {QByteArray}
 */
 QByteArray QTextCodec::fromUnicode(QStringView str) const
 {
+    if (!inputFitsIntoInt(str.size()))
+        return QByteArray();
     ConverterState state = DefaultConversion | Flag::Stateless;
     return convertFromUnicode(str.data(), str.size(), &state);
 }
@@ -820,9 +849,13 @@
 /*!
     Converts \a a from the encoding of this codec to Unicode, and
     returns the result in a QString.
+
+    \include qtextcodec.cpp {to-from-unicode-long-input} {QString}
 */
 QString QTextCodec::toUnicode(const QByteArray& a) const
 {
+    if (!inputFitsIntoInt(a.size()))
+        return QString();
     ConverterState state = DefaultConversion | Flag::Stateless;
     return convertToUnicode(a.constData(), a.size(), &state);
 }
@@ -843,9 +876,16 @@
     \overload
 
     \a s contains the string being tested for encode-ability.
+
+//! [can-encode-long-input]
+    \note if the input size exceeds \c {INT_MAX}, this function does
+    not perform any checks and returns \c false.
+//! [can-encode-long-input]
 */
 bool QTextCodec::canEncode(const QString& s) const
 {
+    if (!inputFitsIntoInt(s.size()))
+        return false;
     ConverterState state;
     state.flags = ConvertInvalidToNull;
     convertFromUnicode(s.constData(), s.size(), &state);
@@ -858,9 +898,13 @@
 
     Returns \c true if the Unicode string \a s can be fully encoded
     with this codec; otherwise returns \c false.
+
+    \include qtextcodec.cpp can-encode-long-input
 */
 bool QTextCodec::canEncode(QStringView s) const
 {
+    if (!inputFitsIntoInt(s.size()))
+        return false;
     ConverterState state;
     state.flags = ConvertInvalidToNull;
     convertFromUnicode(s.data(), s.size(), &state);
@@ -870,11 +914,17 @@
     \overload
 
     \a chars contains the source characters.
+
+    \include qtextcodec.cpp {to-from-unicode-long-input} {QString}
 */
 QString QTextCodec::toUnicode(const char *chars) const
 {
-    const auto len = int(qstrlen(chars));
-    return convertToUnicode(chars, len, nullptr);
+    if (!chars)
+        return QString(); // trivially true for all encodings
+    const size_t len = strlen(chars);
+    if (!inputFitsIntoInt(len))
+        return QString();
+    return convertToUnicode(chars, int(len), nullptr);
 }
 
 
