Logging: fix crash when decoding a symbol that isn't a function

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<bool (void*, void*)>

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 <kai.koehne@qt.io>
This commit is contained in:
Thiago Macieira 2023-02-22 17:22:32 -08:00
parent c827b058dd
commit 644c06b48f
2 changed files with 71 additions and 0 deletions

View File

@ -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;

View File

@ -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<void (*)(int)>::~Polymorphic()"
<< "Polymorphic::~Polymorphic";
QTest::newRow("gcc_41")
<< "function<void (int*)>()::S::f()"
<< "function()::S::f";
QTest::newRow("msvc_41")
<< "void `void function<void __cdecl(int *)>(void)'::`2'::S::f(void)"
<< "function(void)'::`2'::S::f";
QTest::newRow("gcc_42")
<< "function<Polymorphic<void (int*)> >()::S::f(Polymorphic<void (int*)>*)"
<< "function()::S::f";
QTest::newRow("msvc_42")
<< "void `void function<Polymorphic<void __cdecl(int *)> >(void)'::`2'::S::f(Polymorphic<void __cdecl(int *)> *)"
<< "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<char, std::char_traits<char> >::~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<QByteArray>("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<bool (void*, void*)>");
addBadFrame("vtable for BezierEase");
addBadFrame("vtable for Polymorphic<void ()>");
addBadFrame("vtable for Polymorphic<void (*)(int)>");
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<void, std::integral_constant<bool, true> > >");
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()