105 lines
4.0 KiB
Diff
105 lines
4.0 KiB
Diff
From 81cbf0a59c311ec17cae4d2e4d40bd93635f6d8f Mon Sep 17 00:00:00 2001
|
|
From: Ivan Solovev <ivan.solovev@qt.io>
|
|
Date: Fri, 21 Aug 2026 16:15:15 +0200
|
|
Subject: [PATCH] QTextCodec: fix OOB write in ICONV codec
|
|
|
|
The ICONV codec has a size miscalculation error when encoding continues
|
|
with a high surrogate kept in the state after encoding of the previous
|
|
chunk. The code was requesting a resize based on the length in UTF-16
|
|
code points instead of the length in bytes.
|
|
As a result, we were requesting half of the bytes that we actually
|
|
needed.
|
|
Note that since resize() allocates more than requested, the code could
|
|
sometimes execute successfully.
|
|
|
|
The test specifically adds a large tail to the input, so that memcpy()
|
|
clearly triggers an out-of-bounds write. The test was triggering ASAN
|
|
before the fix.
|
|
|
|
Disclaimer: the tests were developed with the help of AI.
|
|
|
|
Fixes: QTBUG-149214
|
|
Pick-to: 6.8 6.5 5.15
|
|
Change-Id: I8d32b737630efc092d3f99eb45982f30106e29d3
|
|
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
|
|
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
|
|
(cherry picked from commit cf3f5fbc8bae37cd358b8157cff11b329cca02aa)
|
|
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
|
|
(cherry picked from commit 724025a7c05596961cc870ef703a2f2b14e55c88)
|
|
---
|
|
|
|
diff --git a/src/core5/codecs/qiconvcodec.cpp b/src/core5/codecs/qiconvcodec.cpp
|
|
index 2bfba5a..496a225 100644
|
|
--- a/src/core5/codecs/qiconvcodec.cpp
|
|
+++ b/src/core5/codecs/qiconvcodec.cpp
|
|
@@ -296,7 +296,7 @@
|
|
QByteArray in;
|
|
if (convState && convState->remainingChars) {
|
|
// we have one surrogate char to be prepended
|
|
- in.resize(sizeof(QChar) + len);
|
|
+ in.resize(sizeof(QChar) + inBytesLeft);
|
|
inBytes = in.data();
|
|
|
|
QChar remaining(convState->state_data[0]);
|
|
diff --git a/tests/auto/core5/codecs/qtextcodec/tst_qtextcodec.cpp b/tests/auto/core5/codecs/qtextcodec/tst_qtextcodec.cpp
|
|
index dcb5390..f2913f4 100644
|
|
--- a/tests/auto/core5/codecs/qtextcodec/tst_qtextcodec.cpp
|
|
+++ b/tests/auto/core5/codecs/qtextcodec/tst_qtextcodec.cpp
|
|
@@ -95,6 +95,8 @@
|
|
|
|
void tsciiEncoderDoesNotReadPastTheEnd_data();
|
|
void tsciiEncoderDoesNotReadPastTheEnd();
|
|
+
|
|
+ void iconvEncoderPendingSurrogateDoesNotOverflow();
|
|
};
|
|
|
|
void tst_QTextCodec::toUnicode_data()
|
|
@@ -2888,6 +2890,47 @@
|
|
QCOMPARE(stateless, expected);
|
|
}
|
|
|
|
+/*
|
|
+ This test catches a size miscalculation error in the Iconv codec
|
|
+ in the case when encoding continues with a high surrogate kept
|
|
+ in the state from the encoding of the previous chunk.
|
|
+*/
|
|
+void tst_QTextCodec::iconvEncoderPendingSurrogateDoesNotOverflow()
|
|
+{
|
|
+ constexpr char32_t Ucs4NonBmp = 0x10437; // DESERET SMALL LETTER YEE
|
|
+ // Before the fix such tail was leading to allocating 4096 bytes,
|
|
+ // while the full buffer required 4086 _QChars_ (so, almost twice more)
|
|
+ constexpr int IconvTailLength = 4064;
|
|
+
|
|
+ // QIconvCodec registers itself as "System", but it's only available
|
|
+ // when the configuration disables ICU and explicitly enables iconv.
|
|
+ QTextCodec *codec = QTextCodec::codecForName("System");
|
|
+ if (!codec)
|
|
+ QSKIP("QIconvCodec was not found (disable ICU and enable iconv explicitly)");
|
|
+
|
|
+ QString start(10, u'x');
|
|
+ start += QChar::highSurrogate(Ucs4NonBmp);
|
|
+
|
|
+ QString rest;
|
|
+ rest += QChar::lowSurrogate(Ucs4NonBmp);
|
|
+ rest += QString(IconvTailLength, u'x');
|
|
+
|
|
+ const QByteArray expected = codec->fromUnicode(start + rest);
|
|
+
|
|
+ // Encode the first block that ends with the high surrogate.
|
|
+ // It should be stored in the converter state.
|
|
+ QTextCodec::ConverterState state;
|
|
+ QByteArray continued = codec->fromUnicode(start.constData(), int(start.size()), &state);
|
|
+
|
|
+ // Encode the second block.
|
|
+ // Before the fix, this would cause an overrun in the QByteArray
|
|
+ // buffer. Detection left for an ASan or Valgrind-checked build.
|
|
+ continued += codec->fromUnicode(rest.constData(), int(rest.size()), &state);
|
|
+
|
|
+ QCOMPARE(continued, expected);
|
|
+ QCOMPARE(state.remainingChars, qsizetype(0));
|
|
+}
|
|
+
|
|
QT_END_NAMESPACE
|
|
|
|
QTEST_MAIN(tst_QTextCodec)
|