소스 검색

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.
Itamar 5 년 전
부모
커밋
6b74d38aab
13개의 변경된 파일300개의 추가작업 그리고 102개의 파일을 삭제
  1. 7 3
      AK/Types.h
  2. 1 1
      Kernel/Forward.h
  3. 1 1
      Kernel/Makefile
  4. 102 29
      Kernel/Process.cpp
  5. 9 7
      Kernel/Process.h
  6. 12 5
      Kernel/Syscall.cpp
  7. 9 2
      Kernel/Syscall.h
  8. 41 0
      Kernel/Thread.cpp
  9. 10 0
      Kernel/Thread.h
  10. 23 27
      Kernel/ThreadTracer.cpp
  11. 29 27
      Kernel/ThreadTracer.h
  12. 7 0
      Kernel/UnixTypes.h
  13. 49 0
      Libraries/LibC/sys/arch/i386/regs.h

+ 7 - 3
AK/Types.h

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

+ 1 - 1
Kernel/Forward.h

@@ -50,7 +50,7 @@ class PhysicalPage;
 class PhysicalRegion;
 class Process;
 class ProcessInspectionHandle;
-class ProcessTracer;
+class ThreadTracer;
 class Range;
 class RangeAllocator;
 class Region;

+ 1 - 1
Kernel/Makefile

@@ -92,7 +92,7 @@ OBJS = \
     PCI/Device.o \
     PerformanceEventBuffer.o \
     Process.o \
-    ProcessTracer.o \
+    ThreadTracer.o \
     Profiling.o \
     RTC.o \
     Random.o \

+ 102 - 29
Kernel/Process.cpp

@@ -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>
@@ -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>
@@ -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);
@@ -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();
 }
 
@@ -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())
@@ -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;
@@ -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;
+}
+
 }

+ 9 - 7
Kernel/Process.h

@@ -272,7 +272,6 @@ public:
     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);
@@ -300,6 +299,7 @@ public:
     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&);
@@ -316,9 +316,6 @@ public:
     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 };
 
@@ -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 Syscall::StringArgument&) const;
 
+    bool has_tracee_thread(int tracer_pid) const;
+
     RefPtr<PageDirectory> m_page_directory;
 
     Process* m_prev { nullptr };
@@ -500,8 +499,6 @@ private:
 
     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 };
@@ -526,6 +523,11 @@ private:
     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 {
@@ -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;
         }

+ 12 - 5
Kernel/Syscall.cpp

@@ -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 {
@@ -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
@@ -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();
 
@@ -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.

+ 9 - 2
Kernel/Syscall.h

@@ -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)               \
@@ -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 {
 
@@ -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();
 

+ 41 - 0
Kernel/Thread.cpp

@@ -33,6 +33,7 @@
 #include <Kernel/Profiling.h>
 #include <Kernel/Scheduler.h>
 #include <Kernel/Thread.h>
+#include <Kernel/ThreadTracer.h>
 #include <Kernel/VM/MemoryManager.h>
 #include <Kernel/VM/PageDirectory.h>
 #include <Kernel/VM/ProcessPagingScope.h>
@@ -508,6 +509,29 @@ ShouldUnblockThread Thread::dispatch_signal(u8 signal)
         ASSERT(m_stop_state != State::Invalid);
         set_state(m_stop_state);
         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;
@@ -900,4 +924,21 @@ void Thread::reset_fpu_state()
     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);
+}
+
 }

+ 10 - 0
Kernel/Thread.h

@@ -122,6 +122,7 @@ public:
         virtual ~Blocker() {}
         virtual bool should_unblock(Thread&, time_t now_s, long us) = 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; }
         bool was_interrupted_by_death() const { return m_was_interrupted_by_death; }
         void set_interrupted_by_signal() { m_was_interrupted_while_blocked = true; }
@@ -253,6 +254,7 @@ public:
             }
             ASSERT_NOT_REACHED();
         }
+        virtual bool is_reason_signal() const override { return m_reason == Reason::Signal; }
 
     private:
         Reason m_reason;
@@ -356,6 +358,7 @@ public:
     void terminate_due_to_signal(u8 signal);
     bool should_ignore_signal(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; }
 
@@ -431,6 +434,11 @@ public:
     static constexpr u32 default_kernel_stack_size = 65536;
     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:
     IntrusiveListNode m_runnable_list_node;
     IntrusiveListNode m_wait_queue_node;
@@ -491,6 +499,8 @@ private:
     bool m_dump_backtrace_on_finalization { false };
     bool m_should_die { false };
 
+    OwnPtr<ThreadTracer> m_tracer;
+
     void yield_without_holding_big_lock();
 };
 

+ 23 - 27
Kernel/ProcessTracer.cpp → Kernel/ThreadTracer.cpp

@@ -25,39 +25,35 @@
  */
 
 #include <AK/Memory.h>
-#include <Kernel/ProcessTracer.h>
+#include <Kernel/ThreadTracer.h>
 
 namespace Kernel {
 
-ProcessTracer::ProcessTracer(pid_t pid)
-    : m_pid(pid)
+ThreadTracer::ThreadTracer(pid_t tracer_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);
-}
-
 }

+ 29 - 27
Kernel/ProcessTracer.h → Kernel/ThreadTracer.h

@@ -26,46 +26,48 @@
 
 #pragma once
 
-#include <AK/CircularQueue.h>
-#include <Kernel/FileSystem/File.h>
+#include <AK/CircularDeque.h>
+#include <AK/NonnullOwnPtr.h>
+#include <AK/Optional.h>
+#include <AK/RefCounted.h>
+#include <Kernel/Arch/i386/CPU.h>
 #include <Kernel/UnixTypes.h>
+#include <LibC/sys/arch/i386/regs.h>
 
 namespace Kernel {
 
-class ProcessTracer : public File {
+class ThreadTracer {
 public:
-    static NonnullRefPtr<ProcessTracer> create(pid_t pid) { return adopt(*new ProcessTracer(pid)); }
-    virtual ~ProcessTracer() override;
+    static NonnullOwnPtr<ThreadTracer> create(pid_t tracer) { return make<ThreadTracer>(tracer); }
 
-    bool is_dead() const { return m_dead; }
-    void set_dead() { m_dead = true; }
+    pid_t tracer_pid() const { return m_tracer_pid; }
+    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; }
-    virtual int read(FileDescription&, u8*, int) override;
+    bool is_tracing_syscalls() const { return m_trace_syscalls; }
+    void set_trace_syscalls(bool val) { m_trace_syscalls = val; }
 
-    virtual bool can_write(const FileDescription&) const override { return true; }
-    virtual int write(FileDescription&, const u8*, int) override { return -EIO; }
+    void set_regs(const RegisterState& regs);
+    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;
-
-    void did_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3, u32 result);
-    pid_t pid() const { return m_pid; }
+    explicit ThreadTracer(pid_t);
 
 private:
-    virtual const char* class_name() const override { return "ProcessTracer"; }
-    explicit ProcessTracer(pid_t);
+    pid_t m_tracer_pid { -1 };
 
-    struct CallData {
-        u32 function;
-        u32 arg1;
-        u32 arg2;
-        u32 arg3;
-        u32 result;
-    };
+    // This is a bitmap for signals that are sent from the tracer to the tracee
+    // TODO: Since we do not currently support sending signals
+    //       to the tracee via PT_CONTINUE, this bitmap is always zeroed
+    u32 m_pending_signals { 0 };
 
-    pid_t m_pid;
-    bool m_dead { false };
-    CircularQueue<CallData, 200> m_calls;
+    bool m_trace_syscalls { false };
+    Optional<PtraceRegisters> m_regs;
 };
 
 }

+ 7 - 0
Kernel/UnixTypes.h

@@ -544,3 +544,10 @@ struct rtentry {
 
 #define PURGE_ALL_VOLATILE 0x1
 #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 - 0
Libraries/LibC/sys/arch/i386/regs.h

@@ -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;
+};