File indexing completed on 2024-04-14 03:51:42

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 }