File indexing completed on 2024-11-10 03:37:52
0001 /* 0002 This file is part of the KDE Libraries 0003 SPDX-FileCopyrightText: 2000 Timo Hummel <timo.hummel@sap.com> 0004 SPDX-FileCopyrightText: 2000 Tom Braun <braunt@fh-konstanz.de> 0005 SPDX-FileCopyrightText: 2010 George Kiagiadakis <kiagiadakis.george@gmail.com> 0006 SPDX-FileCopyrightText: 2009 KDE e.V. <kde-ev-board@kde.org> 0007 SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org> 0008 SPDX-FileContributor: 2009 Adriaan de Groot <groot@kde.org> 0009 0010 SPDX-License-Identifier: LGPL-2.0-or-later 0011 */ 0012 0013 #include "kcrash.h" 0014 0015 #include "kcrash_debug.h" 0016 0017 #include <config-kcrash.h> 0018 0019 #include <csignal> 0020 #include <cstdio> 0021 #include <cstdlib> 0022 #include <cstring> 0023 0024 #include <qplatformdefs.h> 0025 #ifndef Q_OS_WIN 0026 #include <cerrno> 0027 #include <sys/resource.h> 0028 #include <sys/un.h> 0029 #else 0030 #include <qt_windows.h> 0031 #endif 0032 #ifdef Q_OS_LINUX 0033 #include <sys/poll.h> 0034 #include <sys/prctl.h> 0035 #endif 0036 0037 #include <KAboutData> 0038 0039 #include <algorithm> 0040 #include <array> 0041 #include <chrono> 0042 #include <memory> 0043 0044 #include <QDebug> 0045 #include <QDir> 0046 #include <QFile> 0047 #include <QGuiApplication> 0048 #include <QLibraryInfo> 0049 #include <QOffscreenSurface> 0050 #include <QOpenGLContext> 0051 #include <QOpenGLFunctions> 0052 #include <QStandardPaths> 0053 #include <QThread> 0054 0055 #if HAVE_X11 0056 #include <X11/Xlib.h> 0057 #endif 0058 0059 #include "coreconfig_p.h" 0060 #include "exception_p.h" 0061 #include "metadata_p.h" 0062 0063 // WARNING: do not use qGlobalStatics in here, they get destroyed too early on 0064 // shutdown and may inhibit crash handling in late-exit scenarios (e.g. when 0065 // a function local static gets destroyed by __cxa_finalize) 0066 #undef Q_GLOBAL_STATIC 0067 0068 using namespace std::chrono_literals; 0069 0070 #ifdef Q_OS_LINUX 0071 static QByteArray s_socketpath; 0072 #endif 0073 0074 struct Args { 0075 Args() = default; 0076 ~Args() 0077 { 0078 clear(); 0079 } 0080 Q_DISABLE_COPY_MOVE(Args) 0081 0082 void clear() 0083 { 0084 if (!argc) { 0085 return; 0086 } 0087 0088 for (int i = 0; i < argc; ++i) { 0089 delete[] argv[i]; 0090 } 0091 delete[] argv; 0092 0093 argv = nullptr; 0094 argc = 0; 0095 } 0096 0097 void resize(int size) 0098 { 0099 clear(); 0100 argc = size; 0101 argv = new char *[argc + 1]; 0102 for (int i = 0; i < argc + 1; ++i) { 0103 argv[i] = nullptr; 0104 } 0105 } 0106 0107 explicit operator bool() const 0108 { 0109 return argc > 0; 0110 } 0111 0112 int argc = 0; 0113 // null-terminated array of null-terminated strings 0114 char **argv = nullptr; 0115 }; 0116 0117 static KCrash::HandlerType s_emergencySaveFunction = nullptr; 0118 static KCrash::HandlerType s_crashHandler = nullptr; 0119 static std::unique_ptr<char[]> s_appFilePath; // this is the actual QCoreApplication::applicationFilePath 0120 static std::unique_ptr<char[]> s_appName; // the binary name (may be altered by the application) 0121 static std::unique_ptr<char[]> s_appPath; // the binary dir path (may be altered by the application) 0122 static Args s_autoRestartCommandLine; 0123 static std::unique_ptr<char[]> s_drkonqiPath; 0124 static KCrash::CrashFlags s_flags = KCrash::CrashFlags(); 0125 static int s_launchDrKonqi = -1; // -1=initial value 0=disabled 1=enabled 0126 static int s_originalSignal = -1; 0127 static QByteArray s_metadataPath; 0128 0129 static std::unique_ptr<char[]> s_kcrashErrorMessage; 0130 0131 namespace 0132 { 0133 const KCrash::CoreConfig s_coreConfig; 0134 0135 std::unique_ptr<char[]> s_glRenderer; // the GL_RENDERER 0136 0137 QString glRenderer() 0138 { 0139 QOpenGLContext context; 0140 QOffscreenSurface surface; 0141 surface.create(); 0142 0143 if (!context.create()) { 0144 return {}; 0145 } 0146 0147 if (!context.makeCurrent(&surface)) { 0148 return {}; 0149 } 0150 0151 auto done = qScopeGuard([&context] { 0152 context.doneCurrent(); 0153 }); 0154 return QString::fromUtf8(reinterpret_cast<const char *>(context.functions()->glGetString(GL_RENDERER))); 0155 } 0156 0157 QString bootId() 0158 { 0159 #ifdef Q_OS_LINUX 0160 QFile file(QStringLiteral("/proc/sys/kernel/random/boot_id")); 0161 if (!file.open(QFile::ReadOnly)) { 0162 qCWarning(LOG_KCRASH) << "Failed to read /proc/sys/kernel/random/boot_id" << file.errorString(); 0163 return {}; 0164 } 0165 return QString::fromUtf8(file.readAll().simplified().replace('-', QByteArrayView())); 0166 #else 0167 return {}; 0168 #endif 0169 } 0170 0171 } // namespace 0172 0173 static QStringList libexecPaths() 0174 { 0175 // Static since we only need to evaluate once. 0176 static QStringList list = QFile::decodeName(qgetenv("LIBEXEC_PATH")).split(QLatin1Char(':'), Qt::SkipEmptyParts) // env var is used first 0177 + QStringList{ 0178 QCoreApplication::applicationDirPath(), // then look where our application binary is located 0179 QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath), // look where libexec path is (can be set in qt.conf) 0180 QFile::decodeName(KDE_INSTALL_FULL_LIBEXECDIR) // look at our installation location 0181 }; 0182 return list; 0183 } 0184 0185 namespace KCrash 0186 { 0187 void setApplicationFilePath(const QString &filePath); 0188 void startProcess(int argc, const char *argv[], bool waitAndExit); 0189 0190 #if defined(Q_OS_WIN) 0191 LONG WINAPI win32UnhandledExceptionFilter(_EXCEPTION_POINTERS *exceptionInfo); 0192 #endif 0193 } 0194 0195 static bool shouldWriteMetadataToDisk() 0196 { 0197 #ifdef Q_OS_LINUX 0198 // NB: The daemon being currently running must not be a condition here. If something crashes during logout 0199 // the daemon may already be gone but we'll still want to deal with the crash on next login! 0200 // Similar reasoning applies to not checking the presence of the launcher socket. 0201 const bool drkonqiCoredumpHelper = !QStandardPaths::findExecutable(QStringLiteral("drkonqi-coredump-processor"), libexecPaths()).isEmpty(); 0202 return s_coreConfig.isCoredumpd() && drkonqiCoredumpHelper && !qEnvironmentVariableIsSet("KCRASH_NO_METADATA"); 0203 #else 0204 return false; 0205 #endif 0206 } 0207 0208 void KCrash::initialize() 0209 { 0210 if (s_launchDrKonqi == 0) { // disabled by the program itself 0211 return; 0212 } 0213 0214 bool enableDrKonqi = !qEnvironmentVariableIsSet("KDE_DEBUG"); 0215 if (qEnvironmentVariableIsSet("KCRASH_AUTO_RESTARTED") || qEnvironmentVariableIntValue("RUNNING_UNDER_RR") == 1 0216 || qEnvironmentVariableIntValue("KCRASH_DUMP_ONLY") == 1) { 0217 enableDrKonqi = false; 0218 } 0219 0220 const QStringList args = QCoreApplication::arguments(); 0221 // Default to core dumping whenever a process is set. When not or when explicitly opting into just in time debugging 0222 // we enable drkonqi. This causes the signal handler to directly fork drkonqi opening us to race conditions. 0223 // NOTE: depending on the specific signal other threads are running while the signal handler runs and may trip over 0224 // the signal handler's closed FDs. That is primarily why we do not like JIT debugging. 0225 if (enableDrKonqi && (!s_coreConfig.isProcess() || qEnvironmentVariableIntValue("KCRASH_JIT_DRKONQI") == 1)) { 0226 KCrash::setDrKonqiEnabled(true); 0227 } else { 0228 // Don't qDebug here, it loads qtlogging.ini very early which prevents unittests from doing QStandardPaths::setTestModeEnabled(true) in initTestCase() 0229 } 0230 0231 if (QCoreApplication::instance()) { 0232 const QString path = QCoreApplication::applicationFilePath(); 0233 s_appFilePath.reset(qstrdup(qPrintable(path))); // This intentionally cannot be changed by the application! 0234 KCrash::setApplicationFilePath(path); 0235 if (auto guiApp = qobject_cast<QGuiApplication *>(QCoreApplication::instance())) { 0236 s_glRenderer.reset(qstrdup(glRenderer().toUtf8().constData())); 0237 } 0238 } else { 0239 qWarning() << "This process needs a QCoreApplication instance in order to use KCrash"; 0240 } 0241 0242 #ifdef Q_OS_LINUX 0243 // Create socket path to transfer ptrace scope and open connection 0244 s_socketpath = QFile::encodeName(QStringLiteral("%1/kcrash_%2").arg(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation)).arg(getpid())); 0245 #endif 0246 0247 if (shouldWriteMetadataToDisk()) { 0248 // We do not actively clean up metadata via KCrash but some other service. This potentially means we litter 0249 // a lot -> put the metadata in a subdir. 0250 // This data is consumed by DrKonqi in combination with coredumpd metadata. 0251 const QString metadataDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/kcrash-metadata"); 0252 if (QDir().mkpath(metadataDir)) { 0253 const auto bootId = ::bootId(); 0254 const auto exe = QString::fromUtf8(s_appName.get()); 0255 const auto pid = QString::number(QCoreApplication::applicationPid()); 0256 s_metadataPath = QFile::encodeName(metadataDir + // 0257 QStringLiteral("/%1.%2.%3.ini").arg(exe, bootId, pid)); 0258 } 0259 if (!s_crashHandler) { 0260 // Always enable the default handler. We cannot create the metadata ahead of time since we do not know 0261 // when the application metadata is "complete". 0262 // TODO: kf6 maybe change the way init works and have the users run it when their done with kaboutdata etc.? 0263 // the problem with delayed writing is that our crash handler (or any crash handler really) introduces a delay 0264 // in dumping and this in turn increases the risk of another stepping into a puddle (SEGV only runs on the 0265 // faulting thread; all other threads continue running!). therefore it'd be greatly preferred if we 0266 // were able to write the metadata during initial app setup instead of when a crash occurs 0267 setCrashHandler(defaultCrashHandler); 0268 } 0269 } // empty s_metadataPath disables writing 0270 } 0271 0272 void KCrash::setEmergencySaveFunction(HandlerType saveFunction) 0273 { 0274 s_emergencySaveFunction = saveFunction; 0275 0276 /* 0277 * We need at least the default crash handler for 0278 * emergencySaveFunction to be called 0279 */ 0280 if (s_emergencySaveFunction && !s_crashHandler) { 0281 setCrashHandler(defaultCrashHandler); 0282 } 0283 } 0284 0285 KCrash::HandlerType KCrash::emergencySaveFunction() 0286 { 0287 return s_emergencySaveFunction; 0288 } 0289 0290 // Set the default crash handler in 10 seconds 0291 // This is used after an autorestart, the second instance of the application 0292 // is started with KCRASH_AUTO_RESTARTED=1, and we 0293 // set the defaultCrashHandler (to handle autorestart) after 10s. 0294 // The delay is to see if we stay up for more than 10s time, to avoid infinite 0295 // respawning if the app crashes on startup. 0296 class KCrashDelaySetHandler : public QObject 0297 { 0298 public: 0299 KCrashDelaySetHandler() 0300 { 0301 startTimer(10s); 0302 } 0303 0304 protected: 0305 void timerEvent(QTimerEvent *event) override 0306 { 0307 if (!s_crashHandler) { // not set meanwhile 0308 KCrash::setCrashHandler(KCrash::defaultCrashHandler); 0309 } 0310 killTimer(event->timerId()); 0311 this->deleteLater(); 0312 } 0313 }; 0314 0315 void KCrash::setFlags(KCrash::CrashFlags flags) 0316 { 0317 s_flags = flags; 0318 if (s_flags & AutoRestart) { 0319 // We need at least the default crash handler for autorestart to work. 0320 if (!s_crashHandler) { 0321 if (qEnvironmentVariableIsSet("KCRASH_AUTO_RESTARTED")) { 0322 new KCrashDelaySetHandler; 0323 } else { 0324 setCrashHandler(defaultCrashHandler); 0325 } 0326 } 0327 } 0328 } 0329 0330 void KCrash::setApplicationFilePath(const QString &filePath) 0331 { 0332 const auto pos = filePath.lastIndexOf(QLatin1Char('/')); 0333 const QString appName = filePath.mid(pos + 1); 0334 const QString appPath = filePath.left(pos); // could be empty, in theory 0335 0336 s_appName.reset(qstrdup(QFile::encodeName(appName).constData())); 0337 s_appPath.reset(qstrdup(QFile::encodeName(appPath).constData())); 0338 0339 // Prepare the auto-restart command 0340 QStringList args = QCoreApplication::arguments(); 0341 if (args.isEmpty()) { // edge case: tst_QX11Info::startupId does QApplication app(argc, nullptr)... 0342 args.append(filePath); 0343 } else { 0344 args[0] = filePath; // replace argv[0] with full path above 0345 } 0346 0347 s_autoRestartCommandLine.resize(args.count()); 0348 for (int i = 0; i < args.count(); ++i) { 0349 s_autoRestartCommandLine.argv[i] = qstrdup(QFile::encodeName(args.at(i)).constData()); 0350 } 0351 } 0352 0353 void KCrash::setDrKonqiEnabled(bool enabled) 0354 { 0355 const int launchDrKonqi = enabled ? 1 : 0; 0356 if (s_launchDrKonqi == launchDrKonqi) { 0357 return; 0358 } 0359 s_launchDrKonqi = launchDrKonqi; 0360 if (s_launchDrKonqi && !s_drkonqiPath) { 0361 const QString exec = QStandardPaths::findExecutable(QStringLiteral("drkonqi"), libexecPaths()); 0362 if (exec.isEmpty()) { 0363 qCDebug(LOG_KCRASH) << "Could not find drkonqi in search paths:" << libexecPaths(); 0364 s_launchDrKonqi = 0; 0365 } else { 0366 s_drkonqiPath.reset(qstrdup(qPrintable(exec))); 0367 } 0368 } 0369 0370 // we need at least the default crash handler to launch drkonqi 0371 if (s_launchDrKonqi && !s_crashHandler) { 0372 setCrashHandler(defaultCrashHandler); 0373 } 0374 } 0375 0376 bool KCrash::isDrKonqiEnabled() 0377 { 0378 return s_launchDrKonqi == 1; 0379 } 0380 0381 void KCrash::setCrashHandler(HandlerType handler) 0382 { 0383 #if defined(Q_OS_WIN) 0384 static LPTOP_LEVEL_EXCEPTION_FILTER s_previousExceptionFilter = NULL; 0385 0386 if (handler && !s_previousExceptionFilter) { 0387 s_previousExceptionFilter = SetUnhandledExceptionFilter(KCrash::win32UnhandledExceptionFilter); 0388 } else if (!handler && s_previousExceptionFilter) { 0389 SetUnhandledExceptionFilter(s_previousExceptionFilter); 0390 s_previousExceptionFilter = NULL; 0391 } 0392 #else 0393 if (!handler) { 0394 handler = SIG_DFL; 0395 } 0396 0397 sigset_t mask; 0398 sigemptyset(&mask); 0399 0400 #ifdef SIGSEGV 0401 signal(SIGSEGV, handler); 0402 sigaddset(&mask, SIGSEGV); 0403 #endif 0404 #ifdef SIGBUS 0405 signal(SIGBUS, handler); 0406 sigaddset(&mask, SIGBUS); 0407 #endif 0408 #ifdef SIGFPE 0409 signal(SIGFPE, handler); 0410 sigaddset(&mask, SIGFPE); 0411 #endif 0412 #ifdef SIGILL 0413 signal(SIGILL, handler); 0414 sigaddset(&mask, SIGILL); 0415 #endif 0416 #ifdef SIGABRT 0417 signal(SIGABRT, handler); 0418 sigaddset(&mask, SIGABRT); 0419 #endif 0420 0421 sigprocmask(SIG_UNBLOCK, &mask, nullptr); 0422 #endif 0423 0424 s_crashHandler = handler; 0425 } 0426 0427 KCrash::HandlerType KCrash::crashHandler() 0428 { 0429 return s_crashHandler; 0430 } 0431 0432 #if !defined(Q_OS_WIN) && !defined(Q_OS_OSX) 0433 static void closeAllFDs() 0434 { 0435 // Close all remaining file descriptors except for stdin/stdout/stderr 0436 struct rlimit rlp = {}; 0437 getrlimit(RLIMIT_NOFILE, &rlp); 0438 for (rlim_t i = 3; i < rlp.rlim_cur; i++) { 0439 close(i); 0440 } 0441 } 0442 #endif 0443 0444 void crashOnSigTerm(int sig) 0445 { 0446 Q_UNUSED(sig) 0447 raise(s_originalSignal); 0448 } 0449 0450 void KCrash::defaultCrashHandler(int sig) 0451 { 0452 // WABA: Do NOT use qDebug() in this function because it is much too risky! 0453 // Handle possible recursions 0454 static int crashRecursionCounter = 0; 0455 crashRecursionCounter++; // Nothing before this, please ! 0456 s_originalSignal = sig; 0457 0458 #if !defined(Q_OS_WIN) 0459 signal(SIGALRM, SIG_DFL); 0460 alarm(3); // Kill me... (in case we deadlock in malloc) 0461 #endif 0462 0463 if (crashRecursionCounter < 2) { 0464 if (s_emergencySaveFunction) { 0465 s_emergencySaveFunction(sig); 0466 } 0467 if ((s_flags & AutoRestart) && s_autoRestartCommandLine) { 0468 QThread::sleep(1); 0469 startProcess(s_autoRestartCommandLine.argc, const_cast<const char **>(s_autoRestartCommandLine.argv), false); 0470 } 0471 crashRecursionCounter++; 0472 } 0473 0474 if (crashRecursionCounter < 3) { 0475 // If someone is telling me to stop while I'm already crashing, then I should resume crashing 0476 signal(SIGTERM, &crashOnSigTerm); 0477 0478 // NB: all metadata writing ought to happen before closing FDs to reduce synchronization problems with dbus. 0479 0480 // WARNING: do not forget to increase Metadata::argv's size when adding more potential arguments! 0481 Metadata data(s_drkonqiPath.get()); 0482 #ifdef Q_OS_LINUX 0483 // The ini is required to be scoped here, as opposed to the conditional scope, so its lifetime is the same as 0484 // the regular data instance! 0485 MetadataINIWriter ini(s_metadataPath); 0486 // s_appFilePath can point to nullptr 0487 // not exactly sure how, maybe some race condition due to KCrashDelaySetHandler ? 0488 if (!s_appFilePath) { 0489 fprintf(stderr, "KCrash: appFilePath points to nullptr!\n"); 0490 } else if (ini.isWritable()) { 0491 // Add the canonical exe path so the coredump daemon has more data points to map metadata to journald entry. 0492 ini.add("--exe", s_appFilePath.get(), MetadataWriter::BoolValue::No); 0493 data.setAdditionalWriter(&ini); 0494 } 0495 #endif 0496 0497 if (auto optionalExceptionMetadata = KCrash::exceptionMetadata(); optionalExceptionMetadata.has_value()) { 0498 if (optionalExceptionMetadata->klass) { 0499 data.add("--exceptionname", optionalExceptionMetadata->klass); 0500 } 0501 if (optionalExceptionMetadata->what) { 0502 data.add("--exceptionwhat", optionalExceptionMetadata->what); 0503 } 0504 } 0505 0506 if (s_glRenderer) { 0507 data.add("--glrenderer", s_glRenderer.get()); 0508 } 0509 0510 const QByteArray platformName = QGuiApplication::platformName().toUtf8(); 0511 if (!platformName.isEmpty()) { 0512 data.add("--platform", platformName.constData()); 0513 } 0514 0515 #if HAVE_X11 0516 if (platformName == QByteArrayLiteral("xcb")) { 0517 // start up on the correct display 0518 char *display = nullptr; 0519 if (auto disp = qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display()) { 0520 display = XDisplayString(disp); 0521 } else { 0522 display = getenv("DISPLAY"); 0523 } 0524 data.add("--display", display); 0525 } 0526 #endif 0527 0528 data.add("--appname", s_appName ? s_appName.get() : "<unknown>"); 0529 0530 // only add apppath if it's not NULL 0531 if (s_appPath && s_appPath[0]) { 0532 data.add("--apppath", s_appPath.get()); 0533 } 0534 0535 // signal number -- will never be NULL 0536 char sigtxt[10]; 0537 sprintf(sigtxt, "%d", sig); 0538 data.add("--signal", sigtxt); 0539 0540 char pidtxt[20]; 0541 sprintf(pidtxt, "%lld", QCoreApplication::applicationPid()); 0542 data.add("--pid", pidtxt); 0543 0544 const KAboutData *about = KAboutData::applicationDataPointer(); 0545 if (about) { 0546 if (about->internalVersion()) { 0547 data.add("--appversion", about->internalVersion()); 0548 } 0549 0550 if (about->internalProgramName()) { 0551 data.add("--programname", about->internalProgramName()); 0552 } 0553 0554 if (about->internalBugAddress()) { 0555 data.add("--bugaddress", about->internalBugAddress()); 0556 } 0557 0558 if (about->internalProductName()) { 0559 data.add("--productname", about->internalProductName()); 0560 } 0561 } 0562 0563 if (s_flags & SaferDialog) { 0564 data.addBool("--safer"); 0565 } 0566 0567 if ((s_flags & AutoRestart) && s_autoRestartCommandLine) { 0568 data.addBool("--restarted"); 0569 } 0570 0571 #if defined(Q_OS_WIN) 0572 char threadId[8] = {0}; 0573 sprintf(threadId, "%d", GetCurrentThreadId()); 0574 data.add("--thread", threadId); 0575 #endif 0576 0577 data.close(); 0578 const int argc = data.argc; 0579 const char **argv = data.argv.data(); 0580 0581 fprintf(stderr, "KCrash: Application '%s' crashing... crashRecursionCounter = %d\n", s_appName ? s_appName.get() : "<unknown>", crashRecursionCounter); 0582 0583 if (s_launchDrKonqi != 1) { 0584 setCrashHandler(nullptr); 0585 #if !defined(Q_OS_WIN) 0586 raise(sig); // dump core, or whatever is the default action for this signal. 0587 #endif 0588 return; 0589 } 0590 0591 #if !defined(Q_OS_WIN) && !defined(Q_OS_OSX) 0592 if (!(s_flags & KeepFDs)) { 0593 // This tries to prevent problems where applications fail to release resources that drkonqi might need. 0594 // Specifically this was introduced to ensure that an application that had grabbed the X11 cursor would 0595 // forcefully have it removed upon crash to ensure it is ungrabbed by the time drkonqi makes an appearance. 0596 // This is also the point in time when, for example, dbus services are lost. Closing the socket indicates 0597 // to dbus-daemon that the process has disappeared and it will forcefully reclaim the registered service names. 0598 // 0599 // Once we close our socket we lose potential dbus names and if we were running as a systemd service anchored to a name, 0600 // the daemon may decide to jump at us with a TERM signal. We'll want to have finished the metadata by now and 0601 // be near our tracing/raise(). 0602 closeAllFDs(); 0603 } 0604 #if HAVE_X11 0605 else if (auto display = qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display()) { 0606 close(ConnectionNumber(display)); 0607 } 0608 #endif 0609 #endif 0610 0611 #ifndef NDEBUG 0612 fprintf(stderr, 0613 "KCrash: Application Name = %s path = %s pid = %lld\n", 0614 s_appName ? s_appName.get() : "<unknown>", 0615 s_appPath ? s_appPath.get() : "<unknown>", 0616 QCoreApplication::applicationPid()); 0617 fprintf(stderr, "KCrash: Arguments: "); 0618 for (int i = 0; i < s_autoRestartCommandLine.argc; ++i) { 0619 fprintf(stderr, "%s ", s_autoRestartCommandLine.argv[i]); 0620 } 0621 fprintf(stderr, "\n"); 0622 #endif 0623 0624 startProcess(argc, argv, true); 0625 } 0626 0627 if (crashRecursionCounter < 4) { 0628 fprintf(stderr, "Unable to start Dr. Konqi\n"); 0629 } 0630 0631 if (s_coreConfig.isProcess()) { 0632 fprintf(stderr, "Re-raising signal for core dump handling.\n"); 0633 KCrash::setCrashHandler(nullptr); 0634 raise(sig); 0635 // not getting here 0636 } 0637 0638 _exit(255); 0639 } 0640 0641 #if defined(Q_OS_WIN) 0642 0643 void KCrash::startProcess(int argc, const char *argv[], bool waitAndExit) 0644 { 0645 QString cmdLine; 0646 for (int i = 0; i < argc; ++i) { 0647 cmdLine.append(QLatin1Char('\"')); 0648 cmdLine.append(QFile::decodeName(argv[i])); 0649 cmdLine.append(QStringLiteral("\" ")); 0650 } 0651 0652 PROCESS_INFORMATION procInfo; 0653 STARTUPINFOW startupInfo = 0654 {sizeof(STARTUPINFO), 0, 0, 0, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 0655 0656 bool success = CreateProcess(0, (wchar_t *)cmdLine.utf16(), NULL, NULL, false, CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &startupInfo, &procInfo); 0657 0658 if (success && waitAndExit) { 0659 // wait for child to exit 0660 WaitForSingleObject(procInfo.hProcess, INFINITE); 0661 _exit(253); 0662 } 0663 } 0664 0665 // glue function for calling the unix signal handler from the windows unhandled exception filter 0666 LONG WINAPI KCrash::win32UnhandledExceptionFilter(_EXCEPTION_POINTERS *exceptionInfo) 0667 { 0668 // kdbgwin needs the context inside exceptionInfo because if getting the context after the 0669 // exception happened, it will walk down the stack and will stop at KiUserEventDispatch in 0670 // ntdll.dll, which is supposed to dispatch the exception from kernel mode back to user mode 0671 // so... let's create some shared memory 0672 HANDLE hMapFile = NULL; 0673 hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(CONTEXT), TEXT("Local\\KCrashShared")); 0674 0675 LPCTSTR pBuf = NULL; 0676 pBuf = (LPCTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(CONTEXT)); 0677 CopyMemory((PVOID)pBuf, exceptionInfo->ContextRecord, sizeof(CONTEXT)); 0678 0679 if (s_crashHandler) { 0680 s_crashHandler(exceptionInfo->ExceptionRecord->ExceptionCode); 0681 } 0682 0683 CloseHandle(hMapFile); 0684 return EXCEPTION_EXECUTE_HANDLER; // allow windows to do the default action (terminate) 0685 } 0686 #else 0687 0688 static pid_t startDirectly(const char *argv[]); 0689 0690 #ifdef Q_OS_LINUX 0691 static int write_socket(int sock, char *buffer, int len); 0692 static int read_socket(int sock, char *buffer, int len); 0693 0694 static int openDrKonqiSocket(const QByteArray &socketpath); 0695 static int pollDrKonqiSocket(pid_t pid, int sockfd); 0696 #endif 0697 0698 void KCrash::startProcess(int argc, const char *argv[], bool waitAndExit) 0699 { 0700 Q_UNUSED(argc); 0701 fprintf(stderr, "KCrash: Attempting to start %s\n", argv[0]); 0702 0703 pid_t pid = startDirectly(argv); 0704 0705 if (pid > 0 && waitAndExit) { 0706 // Seems we made it.... 0707 alarm(0); // Stop the pending alarm that was set at the top of the defaultCrashHandler 0708 0709 bool running = true; 0710 // Wait forever until the started process exits. This code path is executed 0711 // when launching drkonqi. Note that DrKonqi will SIGSTOP this process in the meantime 0712 // and only send SIGCONT when it is about to attach a debugger. 0713 #ifdef Q_OS_LINUX 0714 // Declare the process that will be debugging the crashed KDE app (#245529). 0715 // For now that will be DrKonqi, which may ask to transfer the ptrace scope to 0716 // a debugger it is not an ancestor of (because it was started via kdeinit or 0717 // KProcess::startDetached()) using a socket. 0718 #ifndef PR_SET_PTRACER 0719 #define PR_SET_PTRACER 0x59616d61 0720 #endif 0721 prctl(PR_SET_PTRACER, pid, 0, 0, 0); 0722 0723 int sockfd = openDrKonqiSocket(s_socketpath); 0724 0725 if (sockfd >= 0) { 0726 // Wait while DrKonqi is running and the socket connection exists 0727 // If the process was started directly, use waitpid(), as it's a child... 0728 while ((running = waitpid(pid, nullptr, WNOHANG) != pid) && pollDrKonqiSocket(pid, sockfd) >= 0) { } 0729 close(sockfd); 0730 unlink(s_socketpath.constData()); 0731 } 0732 #endif 0733 if (running) { 0734 // If the process was started directly, use waitpid(), as it's a child... 0735 while (waitpid(pid, nullptr, 0) != pid) { } 0736 } 0737 if (!s_coreConfig.isProcess()) { 0738 // Only exit if we don't forward to core dumps 0739 _exit(253); 0740 } 0741 } 0742 } 0743 0744 extern "C" char **environ; 0745 static pid_t startDirectly(const char *argv[]) 0746 { 0747 char **environ_end; 0748 for (environ_end = environ; *environ_end; ++environ_end) { } 0749 0750 std::array<const char *, 1024> environ_data; // hope it's big enough 0751 if ((unsigned)(environ_end - environ) + 2 >= environ_data.size()) { 0752 fprintf(stderr, "environ_data in KCrash not big enough!\n"); 0753 return 0; 0754 } 0755 auto end = std::copy_if(environ, environ_end, environ_data.begin(), [](const char *s) { 0756 static const char envvar[] = "KCRASH_AUTO_RESTARTED="; 0757 return strncmp(envvar, s, sizeof(envvar) - 1) != 0; 0758 }); 0759 *end++ = "KCRASH_AUTO_RESTARTED=1"; 0760 *end++ = nullptr; 0761 pid_t pid = fork(); 0762 switch (pid) { 0763 case -1: 0764 fprintf(stderr, "KCrash failed to fork(), errno = %d\n", errno); 0765 return 0; 0766 case 0: 0767 setgroups(0, nullptr); // Remove any extraneous groups 0768 if (setgid(getgid()) < 0 || setuid(getuid()) < 0) { 0769 _exit(253); // This cannot happen. Theoretically. 0770 } 0771 #ifndef Q_OS_OSX 0772 closeAllFDs(); // We are in the child now. Close FDs unconditionally. 0773 #endif 0774 execve(argv[0], const_cast<char **>(argv), const_cast<char **>(environ_data.data())); 0775 fprintf(stderr, "KCrash failed to exec(), errno = %d\n", errno); 0776 _exit(253); 0777 default: 0778 return pid; 0779 } 0780 } 0781 0782 #ifdef Q_OS_LINUX 0783 0784 /* 0785 * Write 'len' bytes from 'buffer' into 'sock'. 0786 * returns 0 on success, -1 on failure. 0787 */ 0788 static int write_socket(int sock, char *buffer, int len) 0789 { 0790 ssize_t result; 0791 int bytes_left = len; 0792 while (bytes_left > 0) { 0793 result = write(sock, buffer, bytes_left); 0794 if (result > 0) { 0795 buffer += result; 0796 bytes_left -= result; 0797 } else if (result == 0) { 0798 return -1; 0799 } else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN)) { 0800 return -1; 0801 } 0802 } 0803 return 0; 0804 } 0805 0806 /* 0807 * Read 'len' bytes from 'sock' into 'buffer'. 0808 * returns 0 on success, -1 on failure. 0809 */ 0810 static int read_socket(int sock, char *buffer, int len) 0811 { 0812 ssize_t result; 0813 int bytes_left = len; 0814 while (bytes_left > 0) { 0815 result = read(sock, buffer, bytes_left); 0816 if (result > 0) { 0817 buffer += result; 0818 bytes_left -= result; 0819 } else if (result == 0) { 0820 return -1; 0821 } else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN)) { 0822 return -1; 0823 } 0824 } 0825 return 0; 0826 } 0827 0828 static int openDrKonqiSocket(const QByteArray &socketpath) 0829 { 0830 int sockfd = socket(PF_UNIX, SOCK_STREAM, 0); 0831 if (sockfd < 0) { 0832 perror("Warning: socket() for communication with DrKonqi failed"); 0833 return -1; 0834 } 0835 0836 struct sockaddr_un drkonqi_server; 0837 drkonqi_server.sun_family = AF_UNIX; 0838 0839 if (socketpath.size() >= static_cast<int>(sizeof(drkonqi_server.sun_path))) { 0840 fprintf(stderr, "Warning: socket path is too long\n"); 0841 close(sockfd); 0842 return -1; 0843 } 0844 strcpy(drkonqi_server.sun_path, socketpath.constData()); 0845 0846 unlink(drkonqi_server.sun_path); // remove potential stale socket 0847 if (bind(sockfd, (struct sockaddr *)&drkonqi_server, sizeof(drkonqi_server)) < 0) { 0848 perror("Warning: bind() for communication with DrKonqi failed"); 0849 close(sockfd); 0850 unlink(drkonqi_server.sun_path); 0851 return -1; 0852 } 0853 0854 listen(sockfd, 1); 0855 0856 return sockfd; 0857 } 0858 0859 static int pollDrKonqiSocket(pid_t pid, int sockfd) 0860 { 0861 struct pollfd fd; 0862 fd.fd = sockfd; 0863 fd.events = POLLIN; 0864 int r; 0865 do { 0866 r = poll(&fd, 1, 1000); // wait for 1 second for a request by DrKonqi 0867 } while (r == -1 && errno == EINTR); 0868 // only continue if POLLIN event returned 0869 if (r == 0) { // timeout 0870 return 0; 0871 } else if (r == -1 || !(fd.revents & POLLIN)) { // some error 0872 return -1; 0873 } 0874 0875 static struct sockaddr_un drkonqi_client; 0876 static socklen_t cllength = sizeof(drkonqi_client); 0877 int clsockfd; 0878 do { 0879 clsockfd = accept(sockfd, (struct sockaddr *)&drkonqi_client, &cllength); 0880 } while (clsockfd == -1 && errno == EINTR); 0881 if (clsockfd < 0) { 0882 return -1; 0883 } 0884 0885 // check whether the message is coming from DrKonqi 0886 static struct ucred ucred; 0887 static socklen_t credlen = sizeof(struct ucred); 0888 if (getsockopt(clsockfd, SOL_SOCKET, SO_PEERCRED, &ucred, &credlen) < 0) { 0889 return -1; 0890 } 0891 0892 if (ucred.pid != pid) { 0893 fprintf(stderr, "Warning: peer pid does not match DrKonqi pid\n"); 0894 return -1; 0895 } 0896 0897 // read PID to change ptrace scope 0898 static const int msize = 21; // most digits in a 64bit int (+sign +'\0') 0899 char msg[msize]; 0900 if (read_socket(clsockfd, msg, msize) == 0) { 0901 int dpid = atoi(msg); 0902 prctl(PR_SET_PTRACER, dpid, 0, 0, 0); 0903 // confirm change to DrKonqi 0904 if (write_socket(clsockfd, msg, msize) == 0) { 0905 fprintf(stderr, "KCrash: ptrace access transferred to %s\n", msg); 0906 } 0907 } 0908 close(clsockfd); 0909 0910 return 1; 0911 } 0912 0913 #endif 0914 0915 #endif // Q_OS_UNIX 0916 0917 void KCrash::setErrorMessage(const QString &message) 0918 { 0919 s_kcrashErrorMessage.reset(qstrdup(message.toUtf8().constData())); 0920 }