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(). :^)
This commit is contained in:
Andreas Kling 2019-04-22 18:44:45 +02:00
parent 6693cfb26a
commit 5c68929aa1
Notes: sideshowbarker 2024-07-19 14:37:27 +09:00
12 changed files with 188 additions and 1 deletions

View File

@ -12,6 +12,7 @@
#include <Kernel/Devices/BlockDevice.h>
#include <Kernel/VM/MemoryManager.h>
#include <Kernel/SharedMemory.h>
#include <Kernel/ProcessTracer.h>
Retained<FileDescriptor> FileDescriptor::create(RetainPtr<Inode>&& inode)
{
@ -23,6 +24,11 @@ Retained<FileDescriptor> FileDescriptor::create(RetainPtr<Device>&& device)
return adopt(*new FileDescriptor(move(device)));
}
Retained<FileDescriptor> FileDescriptor::create(RetainPtr<ProcessTracer>&& tracer)
{
return adopt(*new FileDescriptor(move(tracer)));
}
Retained<FileDescriptor> FileDescriptor::create(RetainPtr<SharedMemory>&& shared_memory)
{
return adopt(*new FileDescriptor(move(shared_memory)));
@ -53,6 +59,11 @@ FileDescriptor::FileDescriptor(RetainPtr<Device>&& device)
{
}
FileDescriptor::FileDescriptor(RetainPtr<ProcessTracer>&& tracer)
: m_tracer(move(tracer))
{
}
FileDescriptor::FileDescriptor(RetainPtr<SharedMemory>&& 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<String> 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)

View File

@ -14,6 +14,7 @@
class TTY;
class MasterPTY;
class Process;
class ProcessTracer;
class Region;
class CharacterDevice;
@ -24,6 +25,7 @@ public:
static Retained<FileDescriptor> create(RetainPtr<Inode>&&);
static Retained<FileDescriptor> create(RetainPtr<Device>&&);
static Retained<FileDescriptor> create(RetainPtr<SharedMemory>&&);
static Retained<FileDescriptor> create(RetainPtr<ProcessTracer>&&);
static Retained<FileDescriptor> create_pipe_writer(FIFO&);
static Retained<FileDescriptor> 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<Socket>&&, SocketRole);
explicit FileDescriptor(RetainPtr<Inode>&&);
explicit FileDescriptor(RetainPtr<Device>&&);
explicit FileDescriptor(RetainPtr<ProcessTracer>&&);
explicit FileDescriptor(RetainPtr<SharedMemory>&&);
FileDescriptor(FIFO&, FIFO::Direction);
@ -126,6 +132,7 @@ private:
FIFO::Direction m_fifo_direction { FIFO::Neither };
RetainPtr<SharedMemory> m_shared_memory;
RetainPtr<ProcessTracer> m_tracer;
bool m_closed { false };
};

View File

@ -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 \

View File

@ -22,6 +22,7 @@
#include <Kernel/ELF/exec_elf.h>
#include <AK/StringBuilder.h>
#include <Kernel/SharedMemory.h>
#include <Kernel/ProcessTracer.h>
//#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;
}

View File

@ -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<Retained<Region>>& 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<ProcessTracer> m_tracer;
Lock m_big_lock;
};

31
Kernel/ProcessTracer.cpp Normal file
View File

@ -0,0 +1,31 @@
#include <Kernel/ProcessTracer.h>
#include <AK/kstdio.h>
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;
}

36
Kernel/ProcessTracer.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <AK/Retainable.h>
#include <AK/Retained.h>
#include <AK/CircularQueue.h>
#include <Kernel/UnixTypes.h>
class ProcessTracer : public Retainable<ProcessTracer> {
public:
static Retained<ProcessTracer> 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<CallData, 200> m_calls;
};

View File

@ -3,6 +3,7 @@
#include "Syscall.h"
#include "Console.h"
#include "Scheduler.h"
#include <Kernel/ProcessTracer.h>
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();
}

View File

@ -99,6 +99,7 @@
__ENUMERATE_SYSCALL(shm_open) \
__ENUMERATE_SYSCALL(shm_close) \
__ENUMERATE_SYSCALL(ftruncate) \
__ENUMERATE_SYSCALL(systrace) \
namespace Syscall {

View File

@ -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);

View File

@ -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*);

40
Userland/strace.cpp Normal file
View File

@ -0,0 +1,40 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <AK/Assertions.h>
#include <AK/Types.h>
#include <Kernel/Syscall.h>
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;
}