From b5be2434a0ff5962e5934c44a23c1b3f7ba8715c Mon Sep 17 00:00:00 2001 From: Ivan Solovev 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. Build fix for 6.8: * the Q_STATIC_LOGGING_CATEGORY macro is not available in this branch, so use Q_LOGGING_CATEGORY instead. Task-number: QTBUG-149424 Pick-to: 6.5 5.15 Change-Id: I25f16013ca35685ffb028a3bfbaa325b5b828f64 Reviewed-by: Marc Mutz (cherry picked from commit 8631aea33f3de13c23fcd740d8900c775c18ac00) Reviewed-by: Qt Cherry-pick Bot (cherry picked from commit aff163c0589bd3c089ab0fe5cfc8a8708319fdc9) (cherry picked from commit 299c11fd0020713a25863f71bbc04a4f66a22625) --- diff --git a/src/core5/codecs/qtextcodec.cpp b/src/core5/codecs/qtextcodec.cpp index 4a15146..ba8692e 100644 --- a/src/core5/codecs/qtextcodec.cpp +++ b/src/core5/codecs/qtextcodec.cpp @@ -13,6 +13,7 @@ #include "qfile.h" #include "qlist.h" #include +#include #include "qstringlist.h" #include "qvarlengtharray.h" @@ -59,6 +60,8 @@ QT_BEGIN_NAMESPACE +Q_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::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); }