Commit 9f3cfcff authored by Christoph Roick's avatar Christoph Roick

Request change of ptrace scope from KCrash

Summary:
- On Linux recent kernels only allow attaching a debugger
  to a process if
  * the right to attach to processes is enabled system-wide
  * the debugger is an ancestor of the debuggee
  * or the debuggee sets a tracer process by a call to
    `prctl(PR_SET_PTRACER, debugger_pid, 0, 0, 0);`
- DrKonqi will ask the debuggee by a socket connection to
  set a new ptracer. This is required if an external debugger
  is started (using the usually hidden Debug button).
- When the debugger has finished, the access is requested back
  to DrKonqi to allow internal backtraces again.

Test Plan:
- try to attach gdb or KDevelop to the process by
  using the Debug button: operation will not be permitted
- apply corresponding patches to KCrash D11236 and DrKonqi
- the process can now be debugged
- after detaching the debugger again, a backtrace can be
  created using the DrKonqi dialog as usual

Reviewers: #plasma_workspaces, #frameworks, ossi

Reviewed By: ossi

Subscribers: ossi, lepagevalleeemmanuel, maximilianocuria, adridg, plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D11235
parent afdf7cc6
......@@ -34,6 +34,7 @@ set(drkonqi_SRCS
crashedapplication.cpp
debugger.cpp
debuggerlaunchers.cpp
ptracer.cpp
debuggermanager.cpp
applicationdetailsexamples.cpp
gdbhighlighter.cpp
......
......@@ -16,6 +16,7 @@
*/
#include "debuggerlaunchers.h"
#include <QCoreApplication>
#include <QDBusConnection>
#include <KShell>
......@@ -26,6 +27,8 @@
#include "drkonqi.h"
#include "crashedapplication.h"
#include "ptracer.h"
DefaultDebuggerLauncher::DefaultDebuggerLauncher(const Debugger & debugger, DebuggerManager *parent)
: AbstractDebuggerLauncher(parent), m_debugger(debugger)
{
......@@ -51,6 +54,7 @@ void DefaultDebuggerLauncher::start()
emit starting();
int pid = KProcess::startDetached(KShell::splitArgs(str));
if ( pid > 0 ) {
setPtracer(pid, DrKonqi::pid());
m_monitor->startMonitoring(pid);
} else {
qCWarning(DRKONQI_LOG) << "Could not start debugger:" << name();
......@@ -60,6 +64,7 @@ void DefaultDebuggerLauncher::start()
void DefaultDebuggerLauncher::onProcessFinished()
{
setPtracer(QCoreApplication::applicationPid(), DrKonqi::pid());
emit finished();
}
......@@ -76,8 +81,8 @@ void TerminalDebuggerLauncher::start()
#endif
DBusInterfaceLauncher::DBusInterfaceLauncher(const QString &name, DBusInterfaceAdaptor *parent)
: AbstractDebuggerLauncher(parent), m_name(name)
DBusInterfaceLauncher::DBusInterfaceLauncher(const QString &name, qint64 pid, DBusInterfaceAdaptor *parent)
: AbstractDebuggerLauncher(parent), m_name(name), m_pid(pid)
{
}
......@@ -89,6 +94,9 @@ QString DBusInterfaceLauncher::name() const
void DBusInterfaceLauncher::start()
{
emit starting();
setPtracer(m_pid, DrKonqi::pid());
emit static_cast<DBusInterfaceAdaptor*>(parent())->acceptDebuggingApplication(m_name);
}
......@@ -108,10 +116,10 @@ int DBusInterfaceAdaptor::pid()
return DrKonqi::crashedApplication()->pid();
}
void DBusInterfaceAdaptor::registerDebuggingApplication(const QString &name)
void DBusInterfaceAdaptor::registerDebuggingApplication(const QString &name, qint64 pid)
{
if (!name.isEmpty() && !m_launchers.contains(name)) {
auto launcher = new DBusInterfaceLauncher(name, this);
auto launcher = new DBusInterfaceLauncher(name, pid, this);
m_launchers.insert(name, launcher);
static_cast<DebuggerManager*>(parent())->addDebugger(launcher, true);
}
......@@ -121,6 +129,7 @@ void DBusInterfaceAdaptor::debuggingFinished(const QString &name)
{
auto it = m_launchers.find(name);
if (it != m_launchers.end()) {
setPtracer(QCoreApplication::applicationPid(), DrKonqi::pid());
emit it.value()->finished();
}
}
......
......@@ -78,7 +78,7 @@ class DBusInterfaceLauncher : public AbstractDebuggerLauncher
{
Q_OBJECT
public:
explicit DBusInterfaceLauncher(const QString &name, DBusInterfaceAdaptor *parent = nullptr);
explicit DBusInterfaceLauncher(const QString &name, qint64 pid, DBusInterfaceAdaptor *parent = nullptr);
QString name() const override;
public Q_SLOTS:
......@@ -86,6 +86,7 @@ public Q_SLOTS:
private:
QString m_name;
qint64 m_pid;
};
class DBusInterfaceAdaptor : public QDBusAbstractAdaptor
......@@ -97,7 +98,7 @@ public:
public Q_SLOTS:
int pid();
Q_NOREPLY void registerDebuggingApplication(const QString &name);
Q_NOREPLY void registerDebuggingApplication(const QString &name, qint64 pid = 0);
Q_NOREPLY void debuggingFinished(const QString &name);
Q_NOREPLY void debuggerClosed(const QString &name);
......
/*
Copyright (C) 2019 Christoph Roick <chrisito@gmx.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ptracer.h"
#ifdef Q_OS_LINUX
#include "drkonqi_debug.h"
#include <QFile>
#include <QStandardPaths>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
void setPtracer(qint64 debuggerpid, qint64 debuggeepid)
{
int sfd = socket(PF_UNIX, SOCK_STREAM, 0);
if (sfd < 0) {
qCWarning(DRKONQI_LOG) << "socket to set ptracer not accessible";
return;
}
static struct sockaddr_un server;
static socklen_t sl = sizeof(server);
server.sun_family = AF_UNIX;
const QString socketPath =
QStringLiteral("%1/kcrash_%2").arg(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation))
.arg(debuggeepid);
if (socketPath.size() >= static_cast<int>(sizeof(server.sun_path))) {
qCWarning(DRKONQI_LOG) << "socket path is too long";
close(sfd);
return;
}
strcpy(server.sun_path, QFile::encodeName(socketPath).constData());
if (::connect(sfd, (struct sockaddr *)&server, sl) == 0) {
static const int msize = 21; // most digits in a 64bit int (+sign +'\0')
char msg[msize];
sprintf(msg, "%lld", debuggerpid);
int r, bytes = 0;
while (bytes < msize) {
r = write(sfd, msg + bytes, msize - bytes);
if (r > 0)
bytes += r;
else if (r == -1 && errno != EINTR)
break;
}
if (bytes == msize) {
struct pollfd fd;
fd.fd = sfd;
fd.events = POLLIN;
while ((r = poll(&fd, 1, 1000)) == -1 && errno == EINTR) {}
if (r > 0 && (fd.revents & POLLIN)) {
char rmsg[msize];
bytes = 0;
while (bytes < msize) {
r = read(sfd, rmsg + bytes, msize - bytes);
if (r > 0)
bytes += r;
else if (r == -1 && errno != EINTR)
break;
}
if (bytes == msize && memcmp(msg, rmsg, msize) == 0)
qCInfo(DRKONQI_LOG) << "ptracer set to" << debuggerpid << "by debugged process";
else
qCWarning(DRKONQI_LOG) << "debugged process did not acknowledge setting ptracer to" << debuggerpid;
close(sfd);
return;
}
}
}
qCWarning(DRKONQI_LOG) << "unable to set ptracer to" << debuggerpid;
close(sfd);
}
#else
void setPtracer(qint64, qint64)
{
}
#endif
/*
Copyright (C) 2019 Christoph Roick <chrisito@gmx.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PTRACER_H
#define PTRACER_H
#include <QtGlobal>
/** On Linux, tell the process to allow the debugger to attach to it */
void setPtracer(qint64 debuggerpid, qint64 debuggeepid);
#endif
......@@ -130,13 +130,17 @@ int main(int argc, char *argv[])
QCommandLineParser parser;
parser.addOption(QCommandLineOption(QStringLiteral("autorestart"), i18n("Automatically restart")));
parser.addOption(QCommandLineOption(QStringLiteral("kdeinit"), i18n("Start DrKonqi using kdeinit")));
parser.addPositionalArgument(QStringLiteral("type"), i18n("Type of crash."), QStringLiteral("crash|malloc|div0|assert|threads"));
aboutData.setupCommandLine(&parser);
parser.process(app);
aboutData.processCommandLine(&parser);
//start drkonqi directly so that drkonqi's output goes to the console
// Start drkonqi directly by default so that drkonqi's output goes to the console.
KCrash::CrashFlags flags = KCrash::AlwaysDirectly;
// This can be disabled to be able to test kcrash's real default behavior.
if (parser.isSet(QStringLiteral("kdeinit")))
flags &= ~KCrash::AlwaysDirectly;
if (parser.isSet(QStringLiteral("autorestart")))
flags |= KCrash::AutoRestart;
KCrash::setFlags(flags);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment