Skip to content

Commit

Permalink
Kernel: Add 'ptrace' syscall
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
itamar8910 authored and awesomekling committed Mar 28, 2020
1 parent c9396be commit 6b74d38
Show file tree
Hide file tree
Showing 13 changed files with 300 additions and 102 deletions.
10 changes: 7 additions & 3 deletions AK/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ typedef i16 int16_t;
typedef i32 int32_t;
typedef i64 int64_t;

typedef int pid_t;

#else
# include <stdint.h>
# include <sys/types.h>
Expand All @@ -84,9 +86,9 @@ typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;

#ifdef __ptrdiff_t
# ifdef __ptrdiff_t
typedef __PTRDIFF_TYPE__ __ptrdiff_t;
#endif
# endif

#endif

Expand Down Expand Up @@ -115,4 +117,6 @@ inline constexpr size_t align_up_to(const size_t value, const size_t alignment)
return (value + (alignment - 1)) & ~(alignment - 1);
}

enum class TriState : u8 { False, True, Unknown };
enum class TriState : u8 { False,
True,
Unknown };
2 changes: 1 addition & 1 deletion Kernel/Forward.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class PhysicalPage;
class PhysicalRegion;
class Process;
class ProcessInspectionHandle;
class ProcessTracer;
class ThreadTracer;
class Range;
class RangeAllocator;
class Region;
Expand Down
2 changes: 1 addition & 1 deletion Kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ OBJS = \
PCI/Device.o \
PerformanceEventBuffer.o \
Process.o \
ProcessTracer.o \
ThreadTracer.o \
Profiling.o \
RTC.o \
Random.o \
Expand Down
131 changes: 102 additions & 29 deletions Kernel/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

}
16 changes: 9 additions & 7 deletions Kernel/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,6 @@ class Process : public InlineLinkedListNode<Process> {
int sys$set_thread_name(int tid, const char* buffer, size_t buffer_size);
int sys$get_thread_name(int tid, char* buffer, size_t buffer_size);
int sys$rename(const Syscall::SC_rename_params*);
int sys$systrace(pid_t);
int sys$mknod(const Syscall::SC_mknod_params*);
int sys$shbuf_create(int, void** buffer);
int sys$shbuf_allow_pid(int, pid_t peer_pid);
Expand Down Expand Up @@ -300,6 +299,7 @@ class Process : public InlineLinkedListNode<Process> {
int sys$unveil(const Syscall::SC_unveil_params*);
int sys$perf_event(int type, FlatPtr arg1, FlatPtr arg2);
int sys$get_stack_bounds(FlatPtr* stack_base, size_t* stack_size);
int sys$ptrace(const Syscall::SC_ptrace_params*);

template<bool sockname, typename Params>
int get_sock_or_peer_name(const Params&);
Expand All @@ -316,9 +316,6 @@ class Process : public InlineLinkedListNode<Process> {
const NonnullOwnPtrVector<Region>& regions() const { return m_regions; }
void dump_regions();

ProcessTracer* tracer() { return m_tracer.ptr(); }
ProcessTracer& ensure_tracer();

u32 m_ticks_in_user { 0 };
u32 m_ticks_in_kernel { 0 };

Expand Down Expand Up @@ -442,6 +439,8 @@ class Process : public InlineLinkedListNode<Process> {
KResultOr<String> get_syscall_path_argument(const char* user_path, size_t path_length) const;
KResultOr<String> get_syscall_path_argument(const Syscall::StringArgument&) const;

bool has_tracee_thread(int tracer_pid) const;

RefPtr<PageDirectory> m_page_directory;

Process* m_prev { nullptr };
Expand Down Expand Up @@ -500,8 +499,6 @@ class Process : public InlineLinkedListNode<Process> {

FixedArray<gid_t> m_extra_gids;

RefPtr<ProcessTracer> m_tracer;

WeakPtr<Region> m_master_tls_region;
size_t m_master_tls_size { 0 };
size_t m_master_tls_alignment { 0 };
Expand All @@ -526,6 +523,11 @@ class Process : public InlineLinkedListNode<Process> {
OwnPtr<PerformanceEventBuffer> m_perf_event_buffer;

u32 m_inspector_count { 0 };

// This member is used in the implementation of ptrace's PT_TRACEME flag.
// If it is set to true, the process will stop at the next execve syscall
// and wait for a tracer to attach.
bool m_wait_for_tracer_at_next_execve { false };
};

class ProcessInspectionHandle {
Expand Down Expand Up @@ -585,7 +587,7 @@ inline void Process::for_each_child(Callback callback)
pid_t my_pid = pid();
for (auto* process = g_processes->head(); process;) {
auto* next_process = process->next();
if (process->ppid() == my_pid) {
if (process->ppid() == my_pid || process->has_tracee_thread(m_pid)) {
if (callback(*process) == IterationDecision::Break)
break;
}
Expand Down
17 changes: 12 additions & 5 deletions Kernel/Syscall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@

#include <Kernel/Arch/i386/CPU.h>
#include <Kernel/Process.h>
#include <Kernel/ProcessTracer.h>
#include <Kernel/Random.h>
#include <Kernel/Syscall.h>
#include <Kernel/ThreadTracer.h>
#include <Kernel/VM/MemoryManager.h>

namespace Kernel {
Expand Down Expand Up @@ -92,8 +92,6 @@ int handle(RegisterState& regs, u32 function, u32 arg1, u32 arg2, u32 arg3)
if (function == SC_exit || function == SC_exit_thread) {
// These syscalls need special handling since they never return to the caller.
cli();
if (auto* tracer = process.tracer())
tracer->did_syscall(function, arg1, arg2, arg3, 0);
if (function == SC_exit)
process.sys$exit((int)arg1);
else
Expand Down Expand Up @@ -132,6 +130,11 @@ void syscall_handler(RegisterState& regs)
return;
}

if (Thread::current->tracer() && Thread::current->tracer()->is_tracing_syscalls()) {
Thread::current->tracer()->set_trace_syscalls(false);
Thread::current->tracer_trap(regs);
}

// Make sure SMAP protection is enabled on syscall entry.
clac();

Expand Down Expand Up @@ -168,8 +171,12 @@ void syscall_handler(RegisterState& regs)
u32 arg2 = regs.ecx;
u32 arg3 = regs.ebx;
regs.eax = (u32)Syscall::handle(regs, function, arg1, arg2, arg3);
if (auto* tracer = process.tracer())
tracer->did_syscall(function, arg1, arg2, arg3, regs.eax);

if (Thread::current->tracer() && Thread::current->tracer()->is_tracing_syscalls()) {
Thread::current->tracer()->set_trace_syscalls(false);
Thread::current->tracer_trap(regs);
}

process.big_lock().unlock();

// Check if we're supposed to return to userspace or just die.
Expand Down
11 changes: 9 additions & 2 deletions Kernel/Syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ namespace Kernel {
__ENUMERATE_SYSCALL(donate) \
__ENUMERATE_SYSCALL(rename) \
__ENUMERATE_SYSCALL(ftruncate) \
__ENUMERATE_SYSCALL(systrace) \
__ENUMERATE_SYSCALL(exit_thread) \
__ENUMERATE_SYSCALL(mknod) \
__ENUMERATE_SYSCALL(writev) \
Expand Down Expand Up @@ -182,7 +181,8 @@ namespace Kernel {
__ENUMERATE_SYSCALL(unveil) \
__ENUMERATE_SYSCALL(perf_event) \
__ENUMERATE_SYSCALL(shutdown) \
__ENUMERATE_SYSCALL(get_stack_bounds)
__ENUMERATE_SYSCALL(get_stack_bounds) \
__ENUMERATE_SYSCALL(ptrace)

namespace Syscall {

Expand Down Expand Up @@ -424,6 +424,13 @@ struct SC_stat_params {
bool follow_symlinks;
};

struct SC_ptrace_params {
int request;
pid_t pid;
u8* addr;
int data;
};

void initialize();
int sync();

Expand Down
Loading

0 comments on commit 6b74d38

Please sign in to comment.