From 9b009208be7914f5673c4311ba702d22c8d62f65 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 18 Dec 2024 10:10:08 +0100 Subject: [PATCH] QTextCodec: avoid read-past-buffer in codecForName() The old code passed a QByteArray to a function taking const char*, invoking QByteArray::operator const char*() implicitly. The callee expects the argument to be NUL-terminated, but if the QByteArray was created fromRawData(), that is not guaranteed. In newer Qt versions we have nullTerminated(), but this needs to be picked furthr back, so use a std::string to do the null-termination. [ChangeLog][QTextCodec] Fixed an out-of-bounds read in codecForName(). which could read past the end of the QByteArray argument if the argument was created fromRawData() with no NUL-termination. Amends the start of the public history even in qtbase (where nameMatch() took QByteArrays, yes, but then proceeded to qstricmp() them, invoking said operator). Manual conflict resolution for 6.11: - removed the auto-pick-to 5.15: the dashboard anyway doesn't track picks cross repos and the 5.15 pick-to causes sanity bot to -2 each step of the way. Pick-to: 6.10 6.8 6.5 Fixes: QTBUG-145180 Change-Id: If82adb1309993d125d807ffa47afc47229039343 Reviewed-by: Thiago Macieira (cherry picked from commit 894079b4932dc878b505056cab9887cca67a011a) --- diff --git a/src/core5/codecs/qtextcodec.cpp b/src/core5/codecs/qtextcodec.cpp index 0d0894c..c574ad0 100644 --- a/src/core5/codecs/qtextcodec.cpp +++ b/src/core5/codecs/qtextcodec.cpp @@ -494,6 +494,10 @@ if (name.isEmpty()) return nullptr; + // ensure NUL-termination: + const std::string name0(name.data(), size_t(name.size())); + // (do it outside the critical section, even if we may not need it) + const TextCodecsMutexLocker locker; QTextCodecData *globalData = QTextCodecData::instance(); @@ -510,14 +514,14 @@ for (TextCodecListConstIt it = globalData->allCodecs.constBegin(), cend = globalData->allCodecs.constEnd(); it != cend; ++it) { QTextCodec *cursor = *it; - if (qTextCodecNameMatch(cursor->name(), name)) { + if (qTextCodecNameMatch(cursor->name().constData(), name0.data())) { if (cache) cache->insert(name, cursor); return cursor; } QList aliases = cursor->aliases(); for (ByteArrayListConstIt ait = aliases.constBegin(), acend = aliases.constEnd(); ait != acend; ++ait) { - if (qTextCodecNameMatch(*ait, name)) { + if (qTextCodecNameMatch(ait->constData(), name0.data())) { cache->insert(name, cursor); return cursor; } @@ -526,7 +530,7 @@ return nullptr; #else - return QIcuCodec::codecForNameUnlocked(name); + return QIcuCodec::codecForNameUnlocked(name0.data()); #endif } diff --git a/tests/auto/core5/codecs/qtextcodec/tst_qtextcodec.cpp b/tests/auto/core5/codecs/qtextcodec/tst_qtextcodec.cpp index 91edc71..086cf07 100644 --- a/tests/auto/core5/codecs/qtextcodec/tst_qtextcodec.cpp +++ b/tests/auto/core5/codecs/qtextcodec/tst_qtextcodec.cpp @@ -2337,7 +2337,6 @@ const QByteArray ISO_8859_1 = QByteArray::fromRawData(ISO_8859_15, sizeof ISO_8859_15 - 2); const auto codec = QTextCodec::codecForName(ISO_8859_1); QVERIFY(codec); - QEXPECT_FAIL("", "QTBUG-145180", Continue); QCOMPARE(codec->name(), "ISO-8859-1"); } }