OSDN Git Service

do not use QTextCodec::codecForLocale() for converting QString to locale-encoded...
authorIvailo Monev <xakepa10@gmail.com>
Mon, 31 Oct 2022 19:48:55 +0000 (21:48 +0200)
committerIvailo Monev <xakepa10@gmail.com>
Mon, 31 Oct 2022 19:48:55 +0000 (21:48 +0200)
if QString::toLocal8Bit() is called from global static destructor it may
be too late to cache a QTextCodec, in fact it may cause a crash.
QTextCodec::codecForLocale() also has to lock a global mutex

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
src/core/codecs/qtextcodec.cpp
src/core/codecs/qtextcodec_p.h
src/core/tools/qstring.cpp

index 6c5b063..49856f0 100644 (file)
@@ -940,6 +940,8 @@ static const qint16 MIBTblSize = sizeof(MIBTbl) / sizeof(MIBTblData);
 static const UChar nullchar[2] = { 0x5c, 0x30 };
 static const UChar questionmarkchar[1] = { 0x3f };
 
+static QByteArray localecodec;
+
 static inline bool nameMatch(const QByteArray &name, const QByteArray &name2)
 {
     if (ucnv_compareNames(name.constData(), name2.constData()) == 0) {
@@ -953,6 +955,25 @@ static inline bool nameMatch(const QByteArray &name, const QByteArray &name2)
     return (ucnv_compareNames(iana, name2.constData()) == 0);
 }
 
+static QByteArray checkForCodec(const QByteArray &name)
+{
+    foreach(const QByteArray &codec, QTextCodecPrivate::allCodecs()) {
+        if (nameMatch(name, codec)) {
+            return name;
+        }
+    }
+    const int index = name.indexOf('@');
+    if (index != -1) {
+        const QByteArray namepart = name.left(index);
+        foreach(const QByteArray &codec, QTextCodecPrivate::allCodecs()) {
+            if (nameMatch(namepart, codec)) {
+                return namepart;
+            }
+        }
+    }
+    return QByteArray();
+}
+
 QTextCodecPrivate::QTextCodecPrivate(const QByteArray &aname)
     : name(aname)
 {
@@ -1016,6 +1037,47 @@ QList<int> QTextCodecPrivate::allMibs()
     return allmibs;
 }
 
+QByteArray QTextCodecPrivate::localeCodec()
+{
+    if (!localecodec.isEmpty()) {
+        return localecodec;
+    }
+
+    QByteArray result;
+    // Get the first non-empty value from $LC_ALL, $LC_CTYPE, and $LANG
+    // environment variables.
+    QByteArray lang = qgetenv("LC_ALL");
+    if (lang.isEmpty()) {
+        lang = qgetenv("LC_CTYPE");
+    }
+    if (lang.isEmpty()) {
+        lang = qgetenv("LANG");
+    }
+
+    const int indexOfDot = lang.indexOf('.');
+    if (indexOfDot != -1) {
+        result = checkForCodec(lang.mid(indexOfDot + 1));
+    }
+
+    if (result.isEmpty() && !lang.isEmpty()) {
+        result = checkForCodec(lang);
+    }
+
+    // Fallback to implementation-defined default locale
+    if (result.isEmpty()) {
+        const QByteArray charset = ::nl_langinfo(CODESET); // deep copy
+        result = checkForCodec(charset);
+    }
+
+    // nl_langinfo() is documented to return empty string only if its argument
+    // is not valid
+    if (Q_UNLIKELY(result.isEmpty())) {
+        qFatal("Could not detect codec for locale");
+    }
+
+    return result;
+}
+
 QString QTextCodecPrivate::convertTo(const char *data, int length, const char* const codec)
 {
     UErrorCode error = U_ZERO_ERROR;
@@ -1163,7 +1225,6 @@ void QTextConverterPrivate::invalidChars(int length) const
 }
 
 
-static QByteArray localecodec;
 #ifndef QT_NO_THREAD
 Q_GLOBAL_STATIC(QMutex, textCodecsMutex)
 #endif
@@ -1191,64 +1252,6 @@ QTextCodecCleanup::~QTextCodecCleanup()
 }
 Q_GLOBAL_STATIC(QTextCodecCleanup, qGlobalQTextCodec)
 
-static QByteArray checkForCodec(const QByteArray &name)
-{
-    foreach(const QByteArray &codec, QTextCodec::availableCodecs()) {
-        if (nameMatch(name, codec)) {
-            return name;
-        }
-    }
-    const int index = name.indexOf('@');
-    if (index != -1) {
-        const QByteArray namepart = name.left(index);
-        foreach(const QByteArray &codec, QTextCodec::availableCodecs()) {
-            if (nameMatch(namepart, codec)) {
-                return namepart;
-            }
-        }
-    }
-    return QByteArray();
-}
-
-static QByteArray getLocaleCodec()
-{
-    Q_ASSERT(localecodec.isEmpty());
-
-    QByteArray result;
-    // Get the first non-empty value from $LC_ALL, $LC_CTYPE, and $LANG
-    // environment variables.
-    QByteArray lang = qgetenv("LC_ALL");
-    if (lang.isEmpty()) {
-        lang = qgetenv("LC_CTYPE");
-    }
-    if (lang.isEmpty()) {
-        lang = qgetenv("LANG");
-    }
-
-    const int indexOfDot = lang.indexOf('.');
-    if (indexOfDot != -1) {
-        result = checkForCodec(lang.mid(indexOfDot + 1));
-    }
-
-    if (result.isEmpty() && !lang.isEmpty()) {
-        result = checkForCodec(lang);
-    }
-
-    // Fallback to implementation-defined default locale
-    if (result.isEmpty()) {
-        const QByteArray charset = ::nl_langinfo(CODESET); // deep copy
-        result = checkForCodec(charset);
-    }
-
-    // nl_langinfo() is documented to return empty string only if its argument
-    // is not valid
-    if (Q_UNLIKELY(result.isEmpty())) {
-        qFatal("Could not detect codec for locale");
-    }
-
-    return result;
-}
-
 /*!
     \enum QTextConverter::ConversionFlag
 
@@ -1540,7 +1543,7 @@ void QTextCodec::setCodecForLocale(QTextCodec *c)
     if (c) {
         localecodec = c->name();
     } else {
-        localecodec = getLocaleCodec();
+        localecodec = QTextCodecPrivate::localeCodec();
     }
 }
 
@@ -1551,7 +1554,7 @@ void QTextCodec::setCodecForLocale(QTextCodec *c)
 QTextCodec* QTextCodec::codecForLocale()
 {
     if (localecodec.isEmpty()) {
-        localecodec = getLocaleCodec();
+        localecodec = QTextCodecPrivate::localeCodec();
     }
     return QTextCodec::codecForName(localecodec);
 }
index 39c2f81..70f5488 100644 (file)
@@ -51,6 +51,8 @@ public:
     static QList<QByteArray> allCodecs();
     static QList<int> allMibs();
 
+    static QByteArray localeCodec();
+
     static QString convertTo(const char *data, int len, const char* const codec);
     static QByteArray convertFrom(const QChar *unicode, int len, const char* const codec);
 
index 5c5f06a..f28d0ab 100644 (file)
@@ -3181,11 +3181,8 @@ QByteArray QString::toAscii() const
 
 static QByteArray toLocal8Bit_helper(const QChar *data, int length)
 {
-#ifndef QT_NO_TEXTCODEC
-    return QTextCodec::codecForLocale()->fromUnicode(data, length);
-#else
-    return toLatin1_helper(data, length);
-#endif // QT_NO_TEXTCODEC
+    const QByteArray localecodec = QTextCodecPrivate::localeCodec();
+    return QTextCodecPrivate::convertFrom(data, length, localecodec.constData());
 }
 
 /*!
@@ -8129,11 +8126,7 @@ QByteArray QStringRef::toAscii() const
 */
 QByteArray QStringRef::toLocal8Bit() const
 {
-#ifndef QT_NO_TEXTCODEC
-    return QTextCodec::codecForLocale()->fromUnicode(unicode(), length());
-#else
-    return toLatin1();
-#endif // QT_NO_TEXTCODEC
+    return toLocal8Bit_helper(unicode(), length());
 }
 
 /*!