mirror of
https://github.com/RGBCube/serenity
synced 2025-05-23 19:15:07 +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:
parent
c9396be83f
commit
6b74d38aab
13 changed files with 300 additions and 102 deletions
10
AK/Types.h
10
AK/Types.h
|
@ -70,6 +70,8 @@ typedef i16 int16_t;
|
||||||
typedef i32 int32_t;
|
typedef i32 int32_t;
|
||||||
typedef i64 int64_t;
|
typedef i64 int64_t;
|
||||||
|
|
||||||
|
typedef int pid_t;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
# include <stdint.h>
|
# include <stdint.h>
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
|
@ -84,9 +86,9 @@ typedef int16_t i16;
|
||||||
typedef int32_t i32;
|
typedef int32_t i32;
|
||||||
typedef int64_t i64;
|
typedef int64_t i64;
|
||||||
|
|
||||||
#ifdef __ptrdiff_t
|
# ifdef __ptrdiff_t
|
||||||
typedef __PTRDIFF_TYPE__ __ptrdiff_t;
|
typedef __PTRDIFF_TYPE__ __ptrdiff_t;
|
||||||
#endif
|
# endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -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);
|
return (value + (alignment - 1)) & ~(alignment - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class TriState : u8 { False, True, Unknown };
|
enum class TriState : u8 { False,
|
||||||
|
True,
|
||||||
|
Unknown };
|
||||||
|
|
|
@ -50,7 +50,7 @@ class PhysicalPage;
|
||||||
class PhysicalRegion;
|
class PhysicalRegion;
|
||||||
class Process;
|
class Process;
|
||||||
class ProcessInspectionHandle;
|
class ProcessInspectionHandle;
|
||||||
class ProcessTracer;
|
class ThreadTracer;
|
||||||
class Range;
|
class Range;
|
||||||
class RangeAllocator;
|
class RangeAllocator;
|
||||||
class Region;
|
class Region;
|
||||||
|
|
|
@ -92,7 +92,7 @@ OBJS = \
|
||||||
PCI/Device.o \
|
PCI/Device.o \
|
||||||
PerformanceEventBuffer.o \
|
PerformanceEventBuffer.o \
|
||||||
Process.o \
|
Process.o \
|
||||||
ProcessTracer.o \
|
ThreadTracer.o \
|
||||||
Profiling.o \
|
Profiling.o \
|
||||||
RTC.o \
|
RTC.o \
|
||||||
Random.o \
|
Random.o \
|
||||||
|
|
|
@ -56,7 +56,6 @@
|
||||||
#include <Kernel/Net/Socket.h>
|
#include <Kernel/Net/Socket.h>
|
||||||
#include <Kernel/PerformanceEventBuffer.h>
|
#include <Kernel/PerformanceEventBuffer.h>
|
||||||
#include <Kernel/Process.h>
|
#include <Kernel/Process.h>
|
||||||
#include <Kernel/ProcessTracer.h>
|
|
||||||
#include <Kernel/Profiling.h>
|
#include <Kernel/Profiling.h>
|
||||||
#include <Kernel/RTC.h>
|
#include <Kernel/RTC.h>
|
||||||
#include <Kernel/Random.h>
|
#include <Kernel/Random.h>
|
||||||
|
@ -66,6 +65,7 @@
|
||||||
#include <Kernel/TTY/MasterPTY.h>
|
#include <Kernel/TTY/MasterPTY.h>
|
||||||
#include <Kernel/TTY/TTY.h>
|
#include <Kernel/TTY/TTY.h>
|
||||||
#include <Kernel/Thread.h>
|
#include <Kernel/Thread.h>
|
||||||
|
#include <Kernel/ThreadTracer.h>
|
||||||
#include <Kernel/Time/TimeManagement.h>
|
#include <Kernel/Time/TimeManagement.h>
|
||||||
#include <Kernel/VM/PageDirectory.h>
|
#include <Kernel/VM/PageDirectory.h>
|
||||||
#include <Kernel/VM/PrivateInodeVMObject.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)
|
if (params.arguments.length > ARG_MAX || params.environment.length > ARG_MAX)
|
||||||
return -E2BIG;
|
return -E2BIG;
|
||||||
|
|
||||||
|
if (m_wait_for_tracer_at_next_execve)
|
||||||
|
Thread::current->send_urgent_signal_to_self(SIGSTOP);
|
||||||
|
|
||||||
String path;
|
String path;
|
||||||
{
|
{
|
||||||
auto path_arg = get_syscall_path_argument(params.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.
|
// slave owner, we have to allow the PTY pair to be torn down.
|
||||||
m_tty = nullptr;
|
m_tty = nullptr;
|
||||||
|
|
||||||
if (m_tracer)
|
|
||||||
m_tracer->set_dead();
|
|
||||||
|
|
||||||
kill_all_threads();
|
kill_all_threads();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3964,24 +3964,6 @@ int Process::sys$watch_file(const char* user_path, size_t path_length)
|
||||||
return fd;
|
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()
|
int Process::sys$halt()
|
||||||
{
|
{
|
||||||
if (!is_superuser())
|
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);
|
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()
|
void Process::FileDescriptionAndFlags::clear()
|
||||||
{
|
{
|
||||||
description = nullptr;
|
description = nullptr;
|
||||||
|
@ -4887,4 +4862,102 @@ int Process::sys$get_stack_bounds(FlatPtr* user_stack_base, size_t* user_stack_s
|
||||||
return 0;
|
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(¶ms, 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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,7 +272,6 @@ public:
|
||||||
int sys$set_thread_name(int tid, const char* buffer, size_t buffer_size);
|
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$get_thread_name(int tid, char* buffer, size_t buffer_size);
|
||||||
int sys$rename(const Syscall::SC_rename_params*);
|
int sys$rename(const Syscall::SC_rename_params*);
|
||||||
int sys$systrace(pid_t);
|
|
||||||
int sys$mknod(const Syscall::SC_mknod_params*);
|
int sys$mknod(const Syscall::SC_mknod_params*);
|
||||||
int sys$shbuf_create(int, void** buffer);
|
int sys$shbuf_create(int, void** buffer);
|
||||||
int sys$shbuf_allow_pid(int, pid_t peer_pid);
|
int sys$shbuf_allow_pid(int, pid_t peer_pid);
|
||||||
|
@ -300,6 +299,7 @@ public:
|
||||||
int sys$unveil(const Syscall::SC_unveil_params*);
|
int sys$unveil(const Syscall::SC_unveil_params*);
|
||||||
int sys$perf_event(int type, FlatPtr arg1, FlatPtr arg2);
|
int sys$perf_event(int type, FlatPtr arg1, FlatPtr arg2);
|
||||||
int sys$get_stack_bounds(FlatPtr* stack_base, size_t* stack_size);
|
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>
|
template<bool sockname, typename Params>
|
||||||
int get_sock_or_peer_name(const Params&);
|
int get_sock_or_peer_name(const Params&);
|
||||||
|
@ -316,9 +316,6 @@ public:
|
||||||
const NonnullOwnPtrVector<Region>& regions() const { return m_regions; }
|
const NonnullOwnPtrVector<Region>& regions() const { return m_regions; }
|
||||||
void dump_regions();
|
void dump_regions();
|
||||||
|
|
||||||
ProcessTracer* tracer() { return m_tracer.ptr(); }
|
|
||||||
ProcessTracer& ensure_tracer();
|
|
||||||
|
|
||||||
u32 m_ticks_in_user { 0 };
|
u32 m_ticks_in_user { 0 };
|
||||||
u32 m_ticks_in_kernel { 0 };
|
u32 m_ticks_in_kernel { 0 };
|
||||||
|
|
||||||
|
@ -442,6 +439,8 @@ private:
|
||||||
KResultOr<String> get_syscall_path_argument(const char* user_path, size_t path_length) const;
|
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;
|
KResultOr<String> get_syscall_path_argument(const Syscall::StringArgument&) const;
|
||||||
|
|
||||||
|
bool has_tracee_thread(int tracer_pid) const;
|
||||||
|
|
||||||
RefPtr<PageDirectory> m_page_directory;
|
RefPtr<PageDirectory> m_page_directory;
|
||||||
|
|
||||||
Process* m_prev { nullptr };
|
Process* m_prev { nullptr };
|
||||||
|
@ -500,8 +499,6 @@ private:
|
||||||
|
|
||||||
FixedArray<gid_t> m_extra_gids;
|
FixedArray<gid_t> m_extra_gids;
|
||||||
|
|
||||||
RefPtr<ProcessTracer> m_tracer;
|
|
||||||
|
|
||||||
WeakPtr<Region> m_master_tls_region;
|
WeakPtr<Region> m_master_tls_region;
|
||||||
size_t m_master_tls_size { 0 };
|
size_t m_master_tls_size { 0 };
|
||||||
size_t m_master_tls_alignment { 0 };
|
size_t m_master_tls_alignment { 0 };
|
||||||
|
@ -526,6 +523,11 @@ private:
|
||||||
OwnPtr<PerformanceEventBuffer> m_perf_event_buffer;
|
OwnPtr<PerformanceEventBuffer> m_perf_event_buffer;
|
||||||
|
|
||||||
u32 m_inspector_count { 0 };
|
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 {
|
class ProcessInspectionHandle {
|
||||||
|
@ -585,7 +587,7 @@ inline void Process::for_each_child(Callback callback)
|
||||||
pid_t my_pid = pid();
|
pid_t my_pid = pid();
|
||||||
for (auto* process = g_processes->head(); process;) {
|
for (auto* process = g_processes->head(); process;) {
|
||||||
auto* next_process = process->next();
|
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)
|
if (callback(*process) == IterationDecision::Break)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,9 +26,9 @@
|
||||||
|
|
||||||
#include <Kernel/Arch/i386/CPU.h>
|
#include <Kernel/Arch/i386/CPU.h>
|
||||||
#include <Kernel/Process.h>
|
#include <Kernel/Process.h>
|
||||||
#include <Kernel/ProcessTracer.h>
|
|
||||||
#include <Kernel/Random.h>
|
#include <Kernel/Random.h>
|
||||||
#include <Kernel/Syscall.h>
|
#include <Kernel/Syscall.h>
|
||||||
|
#include <Kernel/ThreadTracer.h>
|
||||||
#include <Kernel/VM/MemoryManager.h>
|
#include <Kernel/VM/MemoryManager.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
@ -92,8 +92,6 @@ int handle(RegisterState& regs, u32 function, u32 arg1, u32 arg2, u32 arg3)
|
||||||
if (function == SC_exit || function == SC_exit_thread) {
|
if (function == SC_exit || function == SC_exit_thread) {
|
||||||
// These syscalls need special handling since they never return to the caller.
|
// These syscalls need special handling since they never return to the caller.
|
||||||
cli();
|
cli();
|
||||||
if (auto* tracer = process.tracer())
|
|
||||||
tracer->did_syscall(function, arg1, arg2, arg3, 0);
|
|
||||||
if (function == SC_exit)
|
if (function == SC_exit)
|
||||||
process.sys$exit((int)arg1);
|
process.sys$exit((int)arg1);
|
||||||
else
|
else
|
||||||
|
@ -132,6 +130,11 @@ void syscall_handler(RegisterState& regs)
|
||||||
return;
|
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.
|
// Make sure SMAP protection is enabled on syscall entry.
|
||||||
clac();
|
clac();
|
||||||
|
|
||||||
|
@ -168,8 +171,12 @@ void syscall_handler(RegisterState& regs)
|
||||||
u32 arg2 = regs.ecx;
|
u32 arg2 = regs.ecx;
|
||||||
u32 arg3 = regs.ebx;
|
u32 arg3 = regs.ebx;
|
||||||
regs.eax = (u32)Syscall::handle(regs, function, arg1, arg2, arg3);
|
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();
|
process.big_lock().unlock();
|
||||||
|
|
||||||
// Check if we're supposed to return to userspace or just die.
|
// Check if we're supposed to return to userspace or just die.
|
||||||
|
|
|
@ -133,7 +133,6 @@ namespace Kernel {
|
||||||
__ENUMERATE_SYSCALL(donate) \
|
__ENUMERATE_SYSCALL(donate) \
|
||||||
__ENUMERATE_SYSCALL(rename) \
|
__ENUMERATE_SYSCALL(rename) \
|
||||||
__ENUMERATE_SYSCALL(ftruncate) \
|
__ENUMERATE_SYSCALL(ftruncate) \
|
||||||
__ENUMERATE_SYSCALL(systrace) \
|
|
||||||
__ENUMERATE_SYSCALL(exit_thread) \
|
__ENUMERATE_SYSCALL(exit_thread) \
|
||||||
__ENUMERATE_SYSCALL(mknod) \
|
__ENUMERATE_SYSCALL(mknod) \
|
||||||
__ENUMERATE_SYSCALL(writev) \
|
__ENUMERATE_SYSCALL(writev) \
|
||||||
|
@ -182,7 +181,8 @@ namespace Kernel {
|
||||||
__ENUMERATE_SYSCALL(unveil) \
|
__ENUMERATE_SYSCALL(unveil) \
|
||||||
__ENUMERATE_SYSCALL(perf_event) \
|
__ENUMERATE_SYSCALL(perf_event) \
|
||||||
__ENUMERATE_SYSCALL(shutdown) \
|
__ENUMERATE_SYSCALL(shutdown) \
|
||||||
__ENUMERATE_SYSCALL(get_stack_bounds)
|
__ENUMERATE_SYSCALL(get_stack_bounds) \
|
||||||
|
__ENUMERATE_SYSCALL(ptrace)
|
||||||
|
|
||||||
namespace Syscall {
|
namespace Syscall {
|
||||||
|
|
||||||
|
@ -424,6 +424,13 @@ struct SC_stat_params {
|
||||||
bool follow_symlinks;
|
bool follow_symlinks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SC_ptrace_params {
|
||||||
|
int request;
|
||||||
|
pid_t pid;
|
||||||
|
u8* addr;
|
||||||
|
int data;
|
||||||
|
};
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
int sync();
|
int sync();
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <Kernel/Profiling.h>
|
#include <Kernel/Profiling.h>
|
||||||
#include <Kernel/Scheduler.h>
|
#include <Kernel/Scheduler.h>
|
||||||
#include <Kernel/Thread.h>
|
#include <Kernel/Thread.h>
|
||||||
|
#include <Kernel/ThreadTracer.h>
|
||||||
#include <Kernel/VM/MemoryManager.h>
|
#include <Kernel/VM/MemoryManager.h>
|
||||||
#include <Kernel/VM/PageDirectory.h>
|
#include <Kernel/VM/PageDirectory.h>
|
||||||
#include <Kernel/VM/ProcessPagingScope.h>
|
#include <Kernel/VM/ProcessPagingScope.h>
|
||||||
|
@ -508,6 +509,29 @@ ShouldUnblockThread Thread::dispatch_signal(u8 signal)
|
||||||
ASSERT(m_stop_state != State::Invalid);
|
ASSERT(m_stop_state != State::Invalid);
|
||||||
set_state(m_stop_state);
|
set_state(m_stop_state);
|
||||||
m_stop_state = State::Invalid;
|
m_stop_state = State::Invalid;
|
||||||
|
// make sure SemiPermanentBlocker is unblocked
|
||||||
|
if (m_state != Thread::Runnable && m_state != Thread::Running
|
||||||
|
&& m_blocker && m_blocker->is_reason_signal())
|
||||||
|
unblock();
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
auto* thread_tracer = tracer();
|
||||||
|
if (thread_tracer != nullptr) {
|
||||||
|
// when a thread is traced, it should be stopped whenever it receives a signal
|
||||||
|
// the tracer is notified of this by using waitpid()
|
||||||
|
// only "pending signals" from the tracer are sent to the tracee
|
||||||
|
if (!thread_tracer->has_pending_signal(signal)) {
|
||||||
|
m_stop_signal = signal;
|
||||||
|
// make sure SemiPermanentBlocker is unblocked
|
||||||
|
if (m_blocker && m_blocker->is_reason_signal())
|
||||||
|
unblock();
|
||||||
|
m_stop_state = m_state;
|
||||||
|
set_state(Stopped);
|
||||||
|
return ShouldUnblockThread::No;
|
||||||
|
}
|
||||||
|
thread_tracer->unset_signal(signal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto handler_vaddr = action.handler_or_sigaction;
|
auto handler_vaddr = action.handler_or_sigaction;
|
||||||
|
@ -900,4 +924,21 @@ void Thread::reset_fpu_state()
|
||||||
memcpy(m_fpu_state, &s_clean_fpu_state, sizeof(FPUState));
|
memcpy(m_fpu_state, &s_clean_fpu_state, sizeof(FPUState));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Thread::start_tracing_from(pid_t tracer)
|
||||||
|
{
|
||||||
|
m_tracer = ThreadTracer::create(tracer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::stop_tracing()
|
||||||
|
{
|
||||||
|
m_tracer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::tracer_trap(const RegisterState& regs)
|
||||||
|
{
|
||||||
|
ASSERT(m_tracer.ptr());
|
||||||
|
m_tracer->set_regs(regs);
|
||||||
|
send_urgent_signal_to_self(SIGTRAP);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,6 +122,7 @@ public:
|
||||||
virtual ~Blocker() {}
|
virtual ~Blocker() {}
|
||||||
virtual bool should_unblock(Thread&, time_t now_s, long us) = 0;
|
virtual bool should_unblock(Thread&, time_t now_s, long us) = 0;
|
||||||
virtual const char* state_string() const = 0;
|
virtual const char* state_string() const = 0;
|
||||||
|
virtual bool is_reason_signal() const { return false; }
|
||||||
void set_interrupted_by_death() { m_was_interrupted_by_death = true; }
|
void set_interrupted_by_death() { m_was_interrupted_by_death = true; }
|
||||||
bool was_interrupted_by_death() const { return m_was_interrupted_by_death; }
|
bool was_interrupted_by_death() const { return m_was_interrupted_by_death; }
|
||||||
void set_interrupted_by_signal() { m_was_interrupted_while_blocked = true; }
|
void set_interrupted_by_signal() { m_was_interrupted_while_blocked = true; }
|
||||||
|
@ -253,6 +254,7 @@ public:
|
||||||
}
|
}
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
virtual bool is_reason_signal() const override { return m_reason == Reason::Signal; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Reason m_reason;
|
Reason m_reason;
|
||||||
|
@ -356,6 +358,7 @@ public:
|
||||||
void terminate_due_to_signal(u8 signal);
|
void terminate_due_to_signal(u8 signal);
|
||||||
bool should_ignore_signal(u8 signal) const;
|
bool should_ignore_signal(u8 signal) const;
|
||||||
bool has_signal_handler(u8 signal) const;
|
bool has_signal_handler(u8 signal) const;
|
||||||
|
bool has_pending_signal(u8 signal) const { return m_pending_signals & (1 << (signal - 1)); }
|
||||||
|
|
||||||
FPUState& fpu_state() { return *m_fpu_state; }
|
FPUState& fpu_state() { return *m_fpu_state; }
|
||||||
|
|
||||||
|
@ -431,6 +434,11 @@ public:
|
||||||
static constexpr u32 default_kernel_stack_size = 65536;
|
static constexpr u32 default_kernel_stack_size = 65536;
|
||||||
static constexpr u32 default_userspace_stack_size = 4 * MB;
|
static constexpr u32 default_userspace_stack_size = 4 * MB;
|
||||||
|
|
||||||
|
ThreadTracer* tracer() { return m_tracer.ptr(); }
|
||||||
|
void start_tracing_from(pid_t tracer);
|
||||||
|
void stop_tracing();
|
||||||
|
void tracer_trap(const RegisterState&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IntrusiveListNode m_runnable_list_node;
|
IntrusiveListNode m_runnable_list_node;
|
||||||
IntrusiveListNode m_wait_queue_node;
|
IntrusiveListNode m_wait_queue_node;
|
||||||
|
@ -491,6 +499,8 @@ private:
|
||||||
bool m_dump_backtrace_on_finalization { false };
|
bool m_dump_backtrace_on_finalization { false };
|
||||||
bool m_should_die { false };
|
bool m_should_die { false };
|
||||||
|
|
||||||
|
OwnPtr<ThreadTracer> m_tracer;
|
||||||
|
|
||||||
void yield_without_holding_big_lock();
|
void yield_without_holding_big_lock();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -25,39 +25,35 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Memory.h>
|
#include <AK/Memory.h>
|
||||||
#include <Kernel/ProcessTracer.h>
|
#include <Kernel/ThreadTracer.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
ProcessTracer::ProcessTracer(pid_t pid)
|
ThreadTracer::ThreadTracer(pid_t tracer_pid)
|
||||||
: m_pid(pid)
|
: m_tracer_pid(tracer_pid)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessTracer::~ProcessTracer()
|
void ThreadTracer::set_regs(const RegisterState& regs)
|
||||||
{
|
{
|
||||||
|
PtraceRegisters r = {
|
||||||
|
regs.eax,
|
||||||
|
regs.ecx,
|
||||||
|
regs.edx,
|
||||||
|
regs.ebx,
|
||||||
|
regs.esp,
|
||||||
|
regs.ebp,
|
||||||
|
regs.esi,
|
||||||
|
regs.edi,
|
||||||
|
regs.eip,
|
||||||
|
regs.eflags,
|
||||||
|
regs.cs,
|
||||||
|
regs.ss,
|
||||||
|
regs.ds,
|
||||||
|
regs.es,
|
||||||
|
regs.fs,
|
||||||
|
regs.gs,
|
||||||
|
};
|
||||||
|
m_regs = r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessTracer::did_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3, u32 result)
|
|
||||||
{
|
|
||||||
CallData data = { function, arg1, arg2, arg3, result };
|
|
||||||
m_calls.enqueue(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ProcessTracer::read(FileDescription&, u8* buffer, int buffer_size)
|
|
||||||
{
|
|
||||||
if (m_calls.is_empty())
|
|
||||||
return 0;
|
|
||||||
auto data = m_calls.dequeue();
|
|
||||||
// FIXME: This should not be an assertion.
|
|
||||||
ASSERT(buffer_size == sizeof(data));
|
|
||||||
memcpy(buffer, &data, sizeof(data));
|
|
||||||
return sizeof(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
String ProcessTracer::absolute_path(const FileDescription&) const
|
|
||||||
{
|
|
||||||
return String::format("tracer:%d", m_pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -26,46 +26,48 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/CircularQueue.h>
|
#include <AK/CircularDeque.h>
|
||||||
#include <Kernel/FileSystem/File.h>
|
#include <AK/NonnullOwnPtr.h>
|
||||||
|
#include <AK/Optional.h>
|
||||||
|
#include <AK/RefCounted.h>
|
||||||
|
#include <Kernel/Arch/i386/CPU.h>
|
||||||
#include <Kernel/UnixTypes.h>
|
#include <Kernel/UnixTypes.h>
|
||||||
|
#include <LibC/sys/arch/i386/regs.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
class ProcessTracer : public File {
|
class ThreadTracer {
|
||||||
public:
|
public:
|
||||||
static NonnullRefPtr<ProcessTracer> create(pid_t pid) { return adopt(*new ProcessTracer(pid)); }
|
static NonnullOwnPtr<ThreadTracer> create(pid_t tracer) { return make<ThreadTracer>(tracer); }
|
||||||
virtual ~ProcessTracer() override;
|
|
||||||
|
|
||||||
bool is_dead() const { return m_dead; }
|
pid_t tracer_pid() const { return m_tracer_pid; }
|
||||||
void set_dead() { m_dead = true; }
|
bool has_pending_signal(u32 signal) const { return m_pending_signals & (1 << (signal - 1)); }
|
||||||
|
void set_signal(u32 signal) { m_pending_signals |= (1 << (signal - 1)); }
|
||||||
|
void unset_signal(u32 signal) { m_pending_signals &= ~(1 << (signal - 1)); }
|
||||||
|
|
||||||
virtual bool can_read(const FileDescription&) const override { return !m_calls.is_empty() || m_dead; }
|
bool is_tracing_syscalls() const { return m_trace_syscalls; }
|
||||||
virtual int read(FileDescription&, u8*, int) override;
|
void set_trace_syscalls(bool val) { m_trace_syscalls = val; }
|
||||||
|
|
||||||
virtual bool can_write(const FileDescription&) const override { return true; }
|
void set_regs(const RegisterState& regs);
|
||||||
virtual int write(FileDescription&, const u8*, int) override { return -EIO; }
|
bool has_regs() const { return m_regs.has_value(); }
|
||||||
|
const PtraceRegisters& regs() const
|
||||||
|
{
|
||||||
|
ASSERT(m_regs.has_value());
|
||||||
|
return m_regs.value();
|
||||||
|
}
|
||||||
|
|
||||||
virtual String absolute_path(const FileDescription&) const override;
|
explicit ThreadTracer(pid_t);
|
||||||
|
|
||||||
void did_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3, u32 result);
|
|
||||||
pid_t pid() const { return m_pid; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual const char* class_name() const override { return "ProcessTracer"; }
|
pid_t m_tracer_pid { -1 };
|
||||||
explicit ProcessTracer(pid_t);
|
|
||||||
|
|
||||||
struct CallData {
|
// This is a bitmap for signals that are sent from the tracer to the tracee
|
||||||
u32 function;
|
// TODO: Since we do not currently support sending signals
|
||||||
u32 arg1;
|
// to the tracee via PT_CONTINUE, this bitmap is always zeroed
|
||||||
u32 arg2;
|
u32 m_pending_signals { 0 };
|
||||||
u32 arg3;
|
|
||||||
u32 result;
|
|
||||||
};
|
|
||||||
|
|
||||||
pid_t m_pid;
|
bool m_trace_syscalls { false };
|
||||||
bool m_dead { false };
|
Optional<PtraceRegisters> m_regs;
|
||||||
CircularQueue<CallData, 200> m_calls;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -544,3 +544,10 @@ struct rtentry {
|
||||||
|
|
||||||
#define PURGE_ALL_VOLATILE 0x1
|
#define PURGE_ALL_VOLATILE 0x1
|
||||||
#define PURGE_ALL_CLEAN_INODE 0x2
|
#define PURGE_ALL_CLEAN_INODE 0x2
|
||||||
|
|
||||||
|
#define PT_TRACE_ME 1
|
||||||
|
#define PT_ATTACH 2
|
||||||
|
#define PT_CONTINUE 3
|
||||||
|
#define PT_SYSCALL 4
|
||||||
|
#define PT_GETREGS 5
|
||||||
|
#define PT_DETACH 6
|
||||||
|
|
49
Libraries/LibC/sys/arch/i386/regs.h
Normal file
49
Libraries/LibC/sys/arch/i386/regs.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <AK/kmalloc.h>
|
||||||
|
#include <bits/stdint.h>
|
||||||
|
|
||||||
|
struct [[gnu::packed]] PtraceRegisters
|
||||||
|
{
|
||||||
|
uint32_t eax;
|
||||||
|
uint32_t ecx;
|
||||||
|
uint32_t edx;
|
||||||
|
uint32_t ebx;
|
||||||
|
uint32_t esp;
|
||||||
|
uint32_t ebp;
|
||||||
|
uint32_t esi;
|
||||||
|
uint32_t edi;
|
||||||
|
uint32_t eip;
|
||||||
|
uint32_t eflags;
|
||||||
|
uint32_t cs;
|
||||||
|
uint32_t ss;
|
||||||
|
uint32_t ds;
|
||||||
|
uint32_t es;
|
||||||
|
uint32_t fs;
|
||||||
|
uint32_t gs;
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue