From 644c06b48f45aeabcd594df4dcf25a44ea02f132 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 22 Feb 2023 17:22:32 -0800 Subject: [PATCH] Logging: fix crash when decoding a symbol that isn't a function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Saw this on my FreeBSD VM. The backtrace() function thought the nearest symbol to something was "_ZTSNSt3__110__function6__baseIFbPvS2_EEE", which decoded to typeinfo name for std::__1::__function::__base The function pointer type inside parameter threw the decoder for a loop and caused it to crash with the failed assertion in qbytearray.h: inline char QByteArray::at(qsizetype i) const { Q_ASSERT(size_t(i) < size_t(size())); return d.data()[i]; } I noticed this - because tst_qtimer hung - because qFormatLogMessage deadlocked acquiring QMessagePattern::mutex - because the logging recursed - because qCleanupFuncinfo failed an assertion while formatting the backtrace (my QT_MESSAGE_PATTERN has %{backtrace}) - because QTimer::~QTimer -> QObject::killTimer printed a warning - because tst_QTimer::moveToThread produces warnings Pick-to: 5.15 6.2 6.4 6.5 Change-Id: Ieec322d73c1e40ad95c8fffd17464f86e9725991 Reviewed-by: Kai Köhne --- src/corelib/global/qlogging.cpp | 6 ++ .../corelib/global/qlogging/tst_qlogging.cpp | 65 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index e80e5b72d52..82e79e5e01c 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -1021,6 +1021,12 @@ Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info) // Don't know how to parse this function name return info; } + if (info.indexOf('>', pos) != -1 + || info.indexOf(':', pos) != -1) { + // that wasn't the function argument list. + pos = info.size(); + break; + } // find the beginning of the argument list --pos; diff --git a/tests/auto/corelib/global/qlogging/tst_qlogging.cpp b/tests/auto/corelib/global/qlogging/tst_qlogging.cpp index 0a43395ac91..7e2f9e3838c 100644 --- a/tests/auto/corelib/global/qlogging/tst_qlogging.cpp +++ b/tests/auto/corelib/global/qlogging/tst_qlogging.cpp @@ -30,6 +30,8 @@ private slots: #ifdef QT_BUILD_INTERNAL void cleanupFuncinfo_data(); void cleanupFuncinfo(); + void cleanupFuncinfoBad_data(); + void cleanupFuncinfoBad(); #endif void qMessagePattern_data(); @@ -599,6 +601,26 @@ void tst_qmessagehandler::cleanupFuncinfo_data() << "int TestClass1::operator>(int)" << "TestClass1::operator>"; + QTest::newRow("gcc_40") + << "Polymorphic::~Polymorphic()" + << "Polymorphic::~Polymorphic"; + + QTest::newRow("gcc_41") + << "function()::S::f()" + << "function()::S::f"; + + QTest::newRow("msvc_41") + << "void `void function(void)'::`2'::S::f(void)" + << "function(void)'::`2'::S::f"; + + QTest::newRow("gcc_42") + << "function >()::S::f(Polymorphic*)" + << "function()::S::f"; + + QTest::newRow("msvc_42") + << "void `void function >(void)'::`2'::S::f(Polymorphic *)" + << "function(void)'::`2'::S::f"; + QTest::newRow("objc_1") << "-[SomeClass someMethod:withArguments:]" << "-[SomeClass someMethod:withArguments:]"; @@ -614,6 +636,14 @@ void tst_qmessagehandler::cleanupFuncinfo_data() QTest::newRow("objc_4") << "__31-[SomeClass someMethodSchedulingBlock]_block_invoke" << "__31-[SomeClass someMethodSchedulingBlock]_block_invoke"; + + QTest::newRow("thunk-1") + << "non-virtual thunk to QFutureWatcherBasePrivate::postCallOutEvent(QFutureCallOutEvent const&)" + << "QFutureWatcherBasePrivate::postCallOutEvent"; + + QTest::newRow("thunk-2") + << "virtual thunk to std::basic_iostream >::~basic_iostream()" + << "std::basic_iostream::~basic_iostream"; } #endif @@ -634,6 +664,41 @@ void tst_qmessagehandler::cleanupFuncinfo() QEXPECT_FAIL("TestClass1::nested_struct_const", "Nested function processing is broken", Continue); QTEST(QString::fromLatin1(result), "expected"); } + +void tst_qmessagehandler::cleanupFuncinfoBad_data() +{ + QTest::addColumn("funcinfo"); + + auto addBadFrame = [i = 0](const char *symbol) mutable { + QTest::addRow("%d", ++i) << QByteArray(symbol); + }; + addBadFrame("typeinfo for QEventLoop"); + addBadFrame("typeinfo name for QtPrivate::ResultStoreBase"); + addBadFrame("typeinfo name for ._anon_476"); + addBadFrame("typeinfo name for std::__1::__function::__base"); + addBadFrame("vtable for BezierEase"); + addBadFrame("vtable for Polymorphic"); + addBadFrame("vtable for Polymorphic"); + addBadFrame("TLS wrapper function for (anonymous namespace)::jitStacks"); + addBadFrame("lcCheckIndex()::category"); + addBadFrame("guard variable for lcEPDetach()::category"); + addBadFrame("guard variable for QImageReader::read(QImage*)::disableNxImageLoading"); + addBadFrame("VTT for std::__1::ostrstream"); + addBadFrame("qIsRelocatable<(anonymous namespace)::Data>"); + addBadFrame("qt_incomplete_metaTypeArray<(anonymous namespace)::qt_meta_stringdata_CLASSQNonContiguousByteDeviceIoDeviceImplENDCLASS_t, QtPrivate::TypeAndForceComplete > >"); + addBadFrame("f()::i"); +} + +void tst_qmessagehandler::cleanupFuncinfoBad() +{ + QFETCH(QByteArray, funcinfo); + + // A corrupted stack trace may find non-sensical symbols that aren't + // functions. The result doesn't matter, so long as we don't crash or hang. + + QByteArray result = qCleanupFuncinfo(funcinfo); + qDebug() << "Decode of" << funcinfo << "produced" << result; +} #endif void tst_qmessagehandler::qMessagePattern_data()