瀏覽代碼

BUG: Prevent infinite recursion in Qt log handler

Slicer sometimes crashed on exit because in Qt log handler in
ctk::qtHandleToString (in ctkUtils) while executing line
"QTextStream s(&str)" a QDeviceClosedNotifier::QDeviceClosedNotifier()
object was  instantiated that tried to log a message, which eventually
got to ctk::qtHandleString, creating an infinite recursion.

To solve this, a counter is added that is incremented at each
recursion level and when it reaches a threshold then it does not try
to handle the message, thus preventing the crash.

Closes #543

Reviewed-by: Jean-Christophe Fillion-Robin <jchris.fillionr@kitware.com>
Andras Lasso 10 年之前
父節點
當前提交
849771c009
共有 1 個文件被更改,包括 27 次插入0 次删除
  1. 27 0
      Libs/Core/ctkErrorLogQtMessageHandler.cpp

+ 27 - 0
Libs/Core/ctkErrorLogQtMessageHandler.cpp

@@ -19,6 +19,7 @@
 =========================================================================*/
 
 // Qt includes
+#include <QAtomicInt>
 #include <QCoreApplication>
 #include <QThread>
 #include <QVariant>
@@ -31,6 +32,10 @@
 // STD includes
 #include <iostream>
 
+// Handling log messages may generate log messages, which would cause infinite loop.
+// We stop handling log messages if the maximum recursion depth is reached.
+static QAtomicInt ctkErrorLogQtMessageHandler_CurrentRecursionDepth;
+
 //------------------------------------------------------------------------------
 QString ctkErrorLogQtMessageHandler::HandlerName = QLatin1String("Qt");
 
@@ -59,11 +64,21 @@ namespace
 void ctkErrorLogModelQtMessageOutput(QtMsgType type, const QMessageLogContext& context,
                                      const QString& msg)
 {
+  ctkErrorLogQtMessageHandler_CurrentRecursionDepth.ref();
+  // Allow a couple of recursion levels to get a hint about where and why recursion occurs,
+  // so we stop processing the message if recursion depth is over 10.
+  if (ctkErrorLogQtMessageHandler_CurrentRecursionDepth > 10)
+    {
+    ctkErrorLogQtMessageHandler_CurrentRecursionDepth.deref();
+    return;
+    }
+
   //TODO: use context in the log message
   Q_UNUSED(context)
   // Warning: To avoid inifinite loop, do not use Q_ASSERT in this function.
   if (msg.isEmpty())
     {
+    ctkErrorLogQtMessageHandler_CurrentRecursionDepth.deref();
     return;
     }
   ctkErrorLogLevel::LogLevel level = ctkErrorLogLevel::Unknown;
@@ -99,13 +114,24 @@ void ctkErrorLogModelQtMessageOutput(QtMsgType type, const QMessageLogContext& c
           ctk::qtHandleToString(QThread::currentThreadId()),
           level, handler->handlerPrettyName(), ctkErrorLogContext(msg), msg);
     }
+  ctkErrorLogQtMessageHandler_CurrentRecursionDepth.deref();
 }
 #else
 void ctkErrorLogModelQtMessageOutput(QtMsgType type, const char *msg)
 {
+  ctkErrorLogQtMessageHandler_CurrentRecursionDepth.ref();
+  // Allow a couple of recursion levels to get a hint about where and why recursion occurs,
+  // so we stop processing the message if recursion depth is over 10.
+  if (ctkErrorLogQtMessageHandler_CurrentRecursionDepth > 10)
+    {
+    ctkErrorLogQtMessageHandler_CurrentRecursionDepth.deref();
+    return;
+    }
+
   // Warning: To avoid inifinite loop, do not use Q_ASSERT in this function.
   if (QString(msg).isEmpty())
     {
+    ctkErrorLogQtMessageHandler_CurrentRecursionDepth.deref();
     return;
     }
   ctkErrorLogLevel::LogLevel level = ctkErrorLogLevel::Unknown;
@@ -141,6 +167,7 @@ void ctkErrorLogModelQtMessageOutput(QtMsgType type, const char *msg)
           ctk::qtHandleToString(QThread::currentThreadId()),
           level, handler->handlerPrettyName(), ctkErrorLogContext(msg), msg);
     }
+  ctkErrorLogQtMessageHandler_CurrentRecursionDepth.deref();
 }
 #endif
 }