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 }