Browse Source

ptrace: Add PT_POKE

PT_POKE writes a single word to the tracee's address space.

Some caveats:
- If the user requests to write to an address in a read-only region, we
temporarily change the page's protections to allow it.

- If the user requests to write to a region that's backed by a
SharedInodeVMObject, we replace the vmobject with a PrivateIndoeVMObject.
Itamar 5 years ago
parent
commit
b306ac9b2b
5 changed files with 52 additions and 3 deletions
  1. 8 1
      Applications/Debugger/main.cpp
  2. 40 2
      Kernel/Process.cpp
  3. 1 0
      Kernel/UnixTypes.h
  4. 2 0
      Kernel/VM/Region.h
  5. 1 0
      Libraries/LibC/sys/ptrace.h

+ 8 - 1
Applications/Debugger/main.cpp

@@ -122,6 +122,8 @@ VirtualAddress get_entry_point(int pid)
 
 
 int main(int argc, char** argv)
 int main(int argc, char** argv)
 {
 {
+    // TODO: pledge & unveil
+    // TOOD: check that we didn't somehow hurt performance. boot seems slower? (or it's just laptop battey)
     if (argc == 1)
     if (argc == 1)
         return usage();
         return usage();
 
 
@@ -157,7 +159,12 @@ int main(int argc, char** argv)
     printf("eip:0x%x\n", regs.eip);
     printf("eip:0x%x\n", regs.eip);
 
 
     uint32_t data = ptrace(PT_PEEK, g_pid, (void*)regs.eip, 0);
     uint32_t data = ptrace(PT_PEEK, g_pid, (void*)regs.eip, 0);
-    printf("data: 0x%x\n", data);
+    printf("peeked data: 0x%x\n", data);
+
+    if (ptrace(PT_POKE, g_pid, (void*)regs.eip, data) < 0) {
+        perror("poke");
+        return 1;
+    }
 
 
     if (ptrace(PT_CONTINUE, g_pid, 0, 0) == -1) {
     if (ptrace(PT_CONTINUE, g_pid, 0, 0) == -1) {
         perror("continue");
         perror("continue");

+ 40 - 2
Kernel/Process.cpp

@@ -4916,8 +4916,11 @@ int Process::sys$ptrace(const Syscall::SC_ptrace_params* user_params)
     if (params.pid == m_pid)
     if (params.pid == m_pid)
         return -EINVAL;
         return -EINVAL;
 
 
-    InterruptDisabler disabler;
-    auto* peer = Thread::from_tid(params.pid);
+    Thread* peer = nullptr;
+    {
+        InterruptDisabler disabler;
+        peer = Thread::from_tid(params.pid);
+    }
     if (!peer)
     if (!peer)
         return -ESRCH;
         return -ESRCH;
 
 
@@ -4974,6 +4977,7 @@ int Process::sys$ptrace(const Syscall::SC_ptrace_params* user_params)
         }
         }
         break;
         break;
     }
     }
+
     case PT_PEEK: {
     case PT_PEEK: {
         uint32_t* addr = reinterpret_cast<uint32_t*>(params.addr);
         uint32_t* addr = reinterpret_cast<uint32_t*>(params.addr);
         if (!MM.validate_user_read(peer->process(), VirtualAddress(addr), sizeof(uint32_t))) {
         if (!MM.validate_user_read(peer->process(), VirtualAddress(addr), sizeof(uint32_t))) {
@@ -4987,6 +4991,40 @@ int Process::sys$ptrace(const Syscall::SC_ptrace_params* user_params)
         result = *addr;
         result = *addr;
 
 
         return result;
         return result;
+    }
+
+    case PT_POKE: {
+        uint32_t* addr = reinterpret_cast<uint32_t*>(params.addr);
+        // We validate for "read" because PT_POKE can write to readonly pages,
+        // as long as they are user pages
+        if (!MM.validate_user_read(peer->process(), VirtualAddress(addr), sizeof(uint32_t))) {
+            return -EFAULT;
+        }
+        ProcessPagingScope scope(peer->process());
+        Range range = { VirtualAddress(addr), sizeof(uint32_t) };
+        auto* region = peer->process().region_containing(range);
+        ASSERT(region != nullptr);
+        if (region->is_shared()) {
+            // If the region is shared, we change its vmobject to a PrivateInodeVMObject
+            // to prevent the write operation from chaning any shared inode data
+            ASSERT(region->vmobject().is_shared_inode());
+            region->set_vmobject(PrivateInodeVMObject::create_with_inode(static_cast<SharedInodeVMObject&>(region->vmobject()).inode()));
+            region->set_shared(false);
+        }
+        const bool was_writable = region->is_writable();
+        if (!was_writable) //TODO refactor into scopeguard
+            region->set_writable(true);
+        region->remap();
+
+        {
+            SmapDisabler dis;
+            *addr = params.data;
+        }
+
+        if (!was_writable) {
+            region->set_writable(false);
+            region->remap();
+        }
         break;
         break;
     }
     }
 
 

+ 1 - 0
Kernel/UnixTypes.h

@@ -555,3 +555,4 @@ struct rtentry {
 #define PT_GETREGS 5
 #define PT_GETREGS 5
 #define PT_DETACH 6
 #define PT_DETACH 6
 #define PT_PEEK 7
 #define PT_PEEK 7
+#define PT_POKE 8

+ 2 - 0
Kernel/VM/Region.h

@@ -31,6 +31,7 @@
 #include <AK/Weakable.h>
 #include <AK/Weakable.h>
 #include <Kernel/Heap/SlabAllocator.h>
 #include <Kernel/Heap/SlabAllocator.h>
 #include <Kernel/VM/RangeAllocator.h>
 #include <Kernel/VM/RangeAllocator.h>
+#include <Kernel/VM/VMObject.h>
 
 
 namespace Kernel {
 namespace Kernel {
 
 
@@ -79,6 +80,7 @@ public:
 
 
     const VMObject& vmobject() const { return *m_vmobject; }
     const VMObject& vmobject() const { return *m_vmobject; }
     VMObject& vmobject() { return *m_vmobject; }
     VMObject& vmobject() { return *m_vmobject; }
+    void set_vmobject(NonnullRefPtr<VMObject>&& obj) { m_vmobject = obj; }
 
 
     bool is_shared() const { return m_shared; }
     bool is_shared() const { return m_shared; }
     void set_shared(bool shared) { m_shared = shared; }
     void set_shared(bool shared) { m_shared = shared; }

+ 1 - 0
Libraries/LibC/sys/ptrace.h

@@ -37,6 +37,7 @@ __BEGIN_DECLS
 #define PT_GETREGS 5
 #define PT_GETREGS 5
 #define PT_DETACH 6
 #define PT_DETACH 6
 #define PT_PEEK 7
 #define PT_PEEK 7
+#define PT_POKE 8
 
 
 int ptrace(int request, pid_t pid, void* addr, int data);
 int ptrace(int request, pid_t pid, void* addr, int data);