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:
parent
6693cfb26a
commit
5c68929aa1
Notes:
sideshowbarker
2024-07-19 14:37:27 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/5c68929aa17
12 changed files with 188 additions and 1 deletions
|
@ -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)
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
31
Kernel/ProcessTracer.cpp
Normal 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
36
Kernel/ProcessTracer.h
Normal 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;
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -99,6 +99,7 @@
|
|||
__ENUMERATE_SYSCALL(shm_open) \
|
||||
__ENUMERATE_SYSCALL(shm_close) \
|
||||
__ENUMERATE_SYSCALL(ftruncate) \
|
||||
__ENUMERATE_SYSCALL(systrace) \
|
||||
|
||||
|
||||
namespace Syscall {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
40
Userland/strace.cpp
Normal 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;
|
||||
}
|
Loading…
Add table
Reference in a new issue