From 9b009208be7914f5673c4311ba702d22c8d62f65 Mon Sep 17 00:00:00 2001
From: Marc Mutz <marc.mutz@qt.io>
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 <thiago.macieira@intel.com>
(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<QByteArray> 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");
     }
 }
