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