1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-23 19:05:08 +00:00

Kernel: Add 'ptrace' syscall

This commit adds a basic implementation of
the ptrace syscall, which allows one process
(the tracer) to control another process (the tracee).

While a process is being traced, it is stopped whenever a signal is
received (other than SIGCONT).

The tracer can start tracing another thread with PT_ATTACH,
which causes the tracee to stop.

From there, the tracer can use PT_CONTINUE
to continue the execution of the tracee,
or use other request codes (which haven't been implemented yet)
to modify the state of the tracee.

Additional request codes are PT_SYSCALL, which causes the tracee to
continue exection but stop at the next entry or exit from a syscall,
and PT_GETREGS which fethces the last saved register set of the tracee
(can be used to inspect syscall arguments and return value).

A special request code is PT_TRACE_ME, which is issued by the tracee
and causes it to stop when it calls execve and wait for the
tracer to attach.
This commit is contained in:
Itamar 2020-03-28 11:47:16 +03:00 committed by Andreas Kling
parent c9396be83f
commit 6b74d38aab
13 changed files with 300 additions and 102 deletions

View file

@ -56,7 +56,6 @@
#include <Kernel/Net/Socket.h>
#include <Kernel/PerformanceEventBuffer.h>
#include <Kernel/Process.h>
#include <Kernel/ProcessTracer.h>
#include <Kernel/Profiling.h>
#include <Kernel/RTC.h>
#include <Kernel/Random.h>
@ -66,6 +65,7 @@
#include <Kernel/TTY/MasterPTY.h>
#include <Kernel/TTY/TTY.h>
#include <Kernel/Thread.h>
#include <Kernel/ThreadTracer.h>
#include <Kernel/Time/TimeManagement.h>
#include <Kernel/VM/PageDirectory.h>
#include <Kernel/VM/PrivateInodeVMObject.h>
@ -1235,6 +1235,9 @@ int Process::sys$execve(const Syscall::SC_execve_params* user_params)
if (params.arguments.length > ARG_MAX || params.environment.length > ARG_MAX)
return -E2BIG;
if (m_wait_for_tracer_at_next_execve)
Thread::current->send_urgent_signal_to_self(SIGSTOP);
String path;
{
auto path_arg = get_syscall_path_argument(params.path);
@ -3078,9 +3081,6 @@ void Process::die()
// slave owner, we have to allow the PTY pair to be torn down.
m_tty = nullptr;
if (m_tracer)
m_tracer->set_dead();
kill_all_threads();
}
@ -3964,24 +3964,6 @@ int Process::sys$watch_file(const char* user_path, size_t path_length)
return fd;
}
int Process::sys$systrace(pid_t pid)
{
REQUIRE_PROMISE(proc);
InterruptDisabler disabler;
auto* peer = Process::from_pid(pid);
if (!peer)
return -ESRCH;
if (peer->uid() != m_euid)
return -EACCES;
int fd = alloc_fd();
if (fd < 0)
return fd;
auto description = FileDescription::create(peer->ensure_tracer());
description->set_readable(true);
m_fds[fd].set(move(description), 0);
return fd;
}
int Process::sys$halt()
{
if (!is_superuser())
@ -4112,13 +4094,6 @@ int Process::sys$umount(const char* user_mountpoint, size_t mountpoint_length)
return VFS::the().unmount(guest_inode_id);
}
ProcessTracer& Process::ensure_tracer()
{
if (!m_tracer)
m_tracer = ProcessTracer::create(m_pid);
return *m_tracer;
}
void Process::FileDescriptionAndFlags::clear()
{
description = nullptr;
@ -4887,4 +4862,102 @@ int Process::sys$get_stack_bounds(FlatPtr* user_stack_base, size_t* user_stack_s
return 0;
}
int Process::sys$ptrace(const Syscall::SC_ptrace_params* user_params)
{
REQUIRE_PROMISE(proc);
Syscall::SC_ptrace_params params;
if (!validate_read_and_copy_typed(&params, user_params))
return -EFAULT;
if (params.request == PT_TRACE_ME) {
if (Thread::current->tracer())
return -EBUSY;
m_wait_for_tracer_at_next_execve = true;
return 0;
}
if (params.pid == m_pid)
return -EINVAL;
InterruptDisabler disabler;
auto* peer = Thread::from_tid(params.pid);
if (!peer)
return -ESRCH;
if (peer->process().uid() != m_euid)
return -EACCES;
if (params.request == PT_ATTACH) {
if (peer->tracer()) {
return -EBUSY;
}
peer->start_tracing_from(m_pid);
if (peer->state() != Thread::State::Stopped && !(peer->m_blocker && peer->m_blocker->is_reason_signal()))
peer->send_signal(SIGSTOP, this);
return 0;
}
auto* tracer = peer->tracer();
if (!tracer)
return -EPERM;
if (tracer->tracer_pid() != m_pid)
return -EBUSY;
if (peer->m_state == Thread::State::Running)
return -EBUSY;
switch (params.request) {
case PT_CONTINUE:
peer->send_signal(SIGCONT, this);
break;
case PT_DETACH:
peer->stop_tracing();
peer->send_signal(SIGCONT, this);
break;
case PT_SYSCALL:
tracer->set_trace_syscalls(true);
peer->send_signal(SIGCONT, this);
break;
case PT_GETREGS: {
if (!tracer->has_regs())
return -EINVAL;
PtraceRegisters* regs = reinterpret_cast<PtraceRegisters*>(params.addr);
if (!validate_write(regs, sizeof(PtraceRegisters)))
return -EFAULT;
{
SmapDisabler disabler;
*regs = tracer->regs();
}
break;
}
default:
return -EINVAL;
}
return 0;
}
bool Process::has_tracee_thread(int tracer_pid) const
{
bool has_tracee = false;
for_each_thread([&](Thread& t) {
if (t.tracer() && t.tracer()->tracer_pid() == tracer_pid) {
has_tracee = true;
return IterationDecision::Break;
}
return IterationDecision::Continue;
});
return has_tracee;
}
}