From 5c68929aa1724867eec6b65c0ae50dc8a8d4c589 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 22 Apr 2019 18:44:45 +0200 Subject: [PATCH] Kernel: Add a systrace() syscall and implement /bin/strace using it. Calling systrace(pid) gives you a file descriptor with a stream of the syscalls made by a peer process. The process must be owned by the same UID who calls systrace(). :^) --- Kernel/FileSystem/FileDescriptor.cpp | 25 +++++++++++++++++ Kernel/FileSystem/FileDescriptor.h | 7 +++++ Kernel/Makefile | 3 ++- Kernel/Process.cpp | 27 +++++++++++++++++++ Kernel/Process.h | 7 +++++ Kernel/ProcessTracer.cpp | 31 +++++++++++++++++++++ Kernel/ProcessTracer.h | 36 +++++++++++++++++++++++++ Kernel/Syscall.cpp | 5 ++++ Kernel/Syscall.h | 1 + LibC/unistd.cpp | 6 +++++ LibC/unistd.h | 1 + Userland/strace.cpp | 40 ++++++++++++++++++++++++++++ 12 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 Kernel/ProcessTracer.cpp create mode 100644 Kernel/ProcessTracer.h create mode 100644 Userland/strace.cpp diff --git a/Kernel/FileSystem/FileDescriptor.cpp b/Kernel/FileSystem/FileDescriptor.cpp index fd839a55a3..5b482affc8 100644 --- a/Kernel/FileSystem/FileDescriptor.cpp +++ b/Kernel/FileSystem/FileDescriptor.cpp @@ -12,6 +12,7 @@ #include #include #include +#include Retained FileDescriptor::create(RetainPtr&& inode) { @@ -23,6 +24,11 @@ Retained FileDescriptor::create(RetainPtr&& device) return adopt(*new FileDescriptor(move(device))); } +Retained FileDescriptor::create(RetainPtr&& tracer) +{ + return adopt(*new FileDescriptor(move(tracer))); +} + Retained FileDescriptor::create(RetainPtr&& shared_memory) { return adopt(*new FileDescriptor(move(shared_memory))); @@ -53,6 +59,11 @@ FileDescriptor::FileDescriptor(RetainPtr&& device) { } +FileDescriptor::FileDescriptor(RetainPtr&& tracer) + : m_tracer(move(tracer)) +{ +} + FileDescriptor::FileDescriptor(RetainPtr&& shared_memory) : m_shared_memory(move(shared_memory)) { @@ -199,6 +210,8 @@ off_t FileDescriptor::seek(off_t offset, int whence) ssize_t FileDescriptor::read(Process& process, byte* buffer, ssize_t count) { + if (m_tracer) + return m_tracer->read(buffer, count); if (is_fifo()) { ASSERT(fifo_direction() == FIFO::Reader); return m_fifo->read(buffer, count); @@ -217,6 +230,10 @@ ssize_t FileDescriptor::read(Process& process, byte* buffer, ssize_t count) ssize_t FileDescriptor::write(Process& process, const byte* data, ssize_t size) { + if (m_tracer) { + // FIXME: Figure out what the right error code would be. + return -EIO; + } if (is_fifo()) { ASSERT(fifo_direction() == FIFO::Writer); return m_fifo->write(data, size); @@ -235,6 +252,8 @@ ssize_t FileDescriptor::write(Process& process, const byte* data, ssize_t size) bool FileDescriptor::can_write(Process& process) { + if (m_tracer) + return true; if (is_fifo()) { ASSERT(fifo_direction() == FIFO::Writer); return m_fifo->can_write(); @@ -248,6 +267,8 @@ bool FileDescriptor::can_write(Process& process) bool FileDescriptor::can_read(Process& process) { + if (m_tracer) + return m_tracer->can_read(); if (is_fifo()) { ASSERT(fifo_direction() == FIFO::Reader); return m_fifo->can_read(); @@ -376,6 +397,8 @@ bool FileDescriptor::is_file() const KResultOr FileDescriptor::absolute_path() { Stopwatch sw("absolute_path"); + if (m_tracer) + return String::format("tracer:%d", m_tracer->pid()); if (is_tty()) return tty()->tty_name(); if (is_fifo()) @@ -405,6 +428,8 @@ InodeMetadata FileDescriptor::metadata() const bool FileDescriptor::supports_mmap() const { + if (m_tracer) + return false; if (m_inode) return true; if (m_shared_memory) diff --git a/Kernel/FileSystem/FileDescriptor.h b/Kernel/FileSystem/FileDescriptor.h index 4b0892651e..e0f8e71049 100644 --- a/Kernel/FileSystem/FileDescriptor.h +++ b/Kernel/FileSystem/FileDescriptor.h @@ -14,6 +14,7 @@ class TTY; class MasterPTY; class Process; +class ProcessTracer; class Region; class CharacterDevice; @@ -24,6 +25,7 @@ public: static Retained create(RetainPtr&&); static Retained create(RetainPtr&&); static Retained create(RetainPtr&&); + static Retained create(RetainPtr&&); static Retained create_pipe_writer(FIFO&); static Retained create_pipe_reader(FIFO&); ~FileDescriptor(); @@ -101,11 +103,15 @@ public: KResult truncate(off_t); + ProcessTracer* tracer() { return m_tracer.ptr(); } + const ProcessTracer* tracer() const { return m_tracer.ptr(); } + private: friend class VFS; FileDescriptor(RetainPtr&&, SocketRole); explicit FileDescriptor(RetainPtr&&); explicit FileDescriptor(RetainPtr&&); + explicit FileDescriptor(RetainPtr&&); explicit FileDescriptor(RetainPtr&&); FileDescriptor(FIFO&, FIFO::Direction); @@ -126,6 +132,7 @@ private: FIFO::Direction m_fifo_direction { FIFO::Neither }; RetainPtr m_shared_memory; + RetainPtr m_tracer; bool m_closed { false }; }; diff --git a/Kernel/Makefile b/Kernel/Makefile index e38eaa9c0d..6ef0396872 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -47,7 +47,8 @@ KERNEL_OBJS = \ Net/E1000NetworkAdapter.o \ Net/LoopbackAdapter.o \ Net/Routing.o \ - Net/NetworkTask.o + Net/NetworkTask.o \ + ProcessTracer.o VFS_OBJS = \ FileSystem/ProcFS.o \ diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index ccfb0953c8..ca05b11741 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -22,6 +22,7 @@ #include #include #include +#include //#define DEBUG_IO //#define TASK_DEBUG @@ -1894,6 +1895,9 @@ void Process::finalize() void Process::die() { + if (m_tracer) + m_tracer->set_dead(); + { InterruptDisabler disabler; for_each_thread([] (Thread& thread) { @@ -2482,3 +2486,26 @@ int Process::sys$ftruncate(int fd, off_t length) return -EINVAL; return descriptor->truncate(length); } + +int Process::sys$systrace(pid_t pid) +{ + 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 descriptor = FileDescriptor::create(peer->ensure_tracer()); + m_fds[fd].set(move(descriptor), 0); + return fd; +} + +ProcessTracer& Process::ensure_tracer() +{ + if (!m_tracer) + m_tracer = ProcessTracer::create(m_pid); + return *m_tracer; +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 90b0fc9108..9ade3ef64b 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -18,6 +18,7 @@ class FileDescriptor; class PageDirectory; class Region; class VMObject; +class ProcessTracer; void kgettimeofday(timeval&); @@ -175,6 +176,7 @@ public: int sys$restore_signal_mask(dword mask); int sys$create_thread(int(*)(void*), void*); int sys$rename(const char* oldpath, const char* newpath); + int sys$systrace(pid_t); int sys$create_shared_buffer(pid_t peer_pid, int, void** buffer); void* sys$get_shared_buffer(int shared_buffer_id); @@ -194,6 +196,9 @@ public: const Vector>& regions() const { return m_regions; } void dump_regions(); + ProcessTracer* tracer() { return m_tracer.ptr(); } + ProcessTracer& ensure_tracer(); + dword m_ticks_in_user { 0 }; dword m_ticks_in_kernel { 0 }; @@ -318,6 +323,8 @@ private: unsigned m_syscall_count { 0 }; + RetainPtr m_tracer; + Lock m_big_lock; }; diff --git a/Kernel/ProcessTracer.cpp b/Kernel/ProcessTracer.cpp new file mode 100644 index 0000000000..b15763d632 --- /dev/null +++ b/Kernel/ProcessTracer.cpp @@ -0,0 +1,31 @@ +#include +#include + +ProcessTracer::ProcessTracer(pid_t pid) + : m_pid(pid) +{ + +} + +ProcessTracer::~ProcessTracer() +{ + +} + +void ProcessTracer::did_syscall(dword function, dword arg1, dword arg2, dword arg3, dword result) +{ + CallData data = { function, arg1, arg2, arg3, result }; + m_calls.enqueue(data); +} + +int ProcessTracer::read(byte* buffer, int buffer_size) +{ + if (!m_calls.is_empty()) { + 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); + } + return 0; +} diff --git a/Kernel/ProcessTracer.h b/Kernel/ProcessTracer.h new file mode 100644 index 0000000000..d178fb123d --- /dev/null +++ b/Kernel/ProcessTracer.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include + +class ProcessTracer : public Retainable { +public: + static Retained create(pid_t pid) { return adopt(*new ProcessTracer(pid)); } + ~ProcessTracer(); + + bool is_dead() const { return m_dead; } + void set_dead() { m_dead = true; } + + bool can_read() const { return !m_calls.is_empty() || m_dead; } + int read(byte*, int); + + void did_syscall(dword function, dword arg1, dword arg2, dword arg3, dword result); + pid_t pid() const { return m_pid; } + +private: + explicit ProcessTracer(pid_t); + + struct CallData { + dword function; + dword arg1; + dword arg2; + dword arg3; + dword result; + }; + + pid_t m_pid; + bool m_dead { false }; + CircularQueue m_calls; +}; diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 34b817bd5d..e393b7e0de 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -3,6 +3,7 @@ #include "Syscall.h" #include "Console.h" #include "Scheduler.h" +#include extern "C" void syscall_trap_entry(RegisterDump&); extern "C" void syscall_trap_handler(); @@ -251,6 +252,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, return current->process().sys$shm_unlink((const char*)arg1); case Syscall::SC_ftruncate: return current->process().sys$ftruncate((int)arg1, (off_t)arg2); + case Syscall::SC_systrace: + return current->process().sys$systrace((pid_t)arg1); default: kprintf("<%u> int0x82: Unknown function %u requested {%x, %x, %x}\n", current->process().pid(), function, arg1, arg2, arg3); break; @@ -268,6 +271,8 @@ void syscall_trap_entry(RegisterDump& regs) dword arg2 = regs.ecx; dword arg3 = regs.ebx; regs.eax = Syscall::handle(regs, function, arg1, arg2, arg3); + if (auto* tracer = current->process().tracer()) + tracer->did_syscall(function, arg1, arg2, arg3, regs.eax); current->process().big_lock().unlock(); } diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 14dc789558..39f1a43a55 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -99,6 +99,7 @@ __ENUMERATE_SYSCALL(shm_open) \ __ENUMERATE_SYSCALL(shm_close) \ __ENUMERATE_SYSCALL(ftruncate) \ + __ENUMERATE_SYSCALL(systrace) \ namespace Syscall { diff --git a/LibC/unistd.cpp b/LibC/unistd.cpp index 19f02c034e..5cba9f33ae 100644 --- a/LibC/unistd.cpp +++ b/LibC/unistd.cpp @@ -16,6 +16,12 @@ extern "C" { +int systrace(pid_t pid) +{ + int rc = syscall(SC_systrace, pid); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + int chown(const char* pathname, uid_t uid, gid_t gid) { int rc = syscall(SC_chown, pathname, uid, gid); diff --git a/LibC/unistd.h b/LibC/unistd.h index b1483bcba0..2f9f7d01e6 100644 --- a/LibC/unistd.h +++ b/LibC/unistd.h @@ -14,6 +14,7 @@ __BEGIN_DECLS extern char** environ; +int systrace(pid_t); int gettid(); int donate(int tid); int create_thread(int(*)(void*), void*); diff --git a/Userland/strace.cpp b/Userland/strace.cpp new file mode 100644 index 0000000000..374ac1cf80 --- /dev/null +++ b/Userland/strace.cpp @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc < 2) + return 1; + + int pid = atoi(argv[1]); + int fd = systrace(pid); + if (fd < 0) { + perror("systrace"); + return 1; + } + + for (;;) { + dword call[5]; + int nread = read(fd, &call, sizeof(call)); + if (nread == 0) + break; + if (nread < 0) { + perror("read"); + return 1; + } + ASSERT(nread == sizeof(call)); + printf("%s(%#x, %#x, %#x) = %#x\n", Syscall::to_string((Syscall::Function)call[0]), call[1], call[2], call[3], call[4]); + } + + int rc = close(fd); + if (rc < 0) { + perror("close"); + return 1; + } + + return 0; +}