Kaynağa Gözat

Kernel: Crash on memory access in non-readable regions

This patch makes it possible to make memory regions non-readable.
This is enforced using the "present" bit in the page tables.
A process that hits an not-present page fault in a non-readable
region will be crashed.
Andreas Kling 5 yıl önce
ebeveyn
işleme
f41ae755ec

+ 2 - 0
Base/usr/share/man/man1/crash.md

@@ -28,6 +28,8 @@ kinds of crashes.
 * `-T`: Make a syscall while using an invalid stack pointer.
 * `-t`: Trigger a page fault while using an invalid stack pointer.
 * `-S`: Make a syscall from writeable memory.
+* `-x`: Read from recently freed memory. (Tests an opportunistic malloc guard.)
+* `-y`: Write to recently freed memory. (Tests an opportunistic malloc guard.)
 
 ## Examples
 

+ 1 - 0
Kernel/Process.cpp

@@ -306,6 +306,7 @@ int Process::sys$mprotect(void* addr, size_t size, int prot)
         return -EINVAL;
     if (!region->is_mmap())
         return -EPERM;
+    region->set_readable(prot & PROT_READ);
     region->set_writable(prot & PROT_WRITE);
     region->remap();
     return 0;

+ 7 - 2
Kernel/VM/Region.cpp

@@ -191,7 +191,7 @@ void Region::remap_page(size_t index)
     auto& physical_page = vmobject().physical_pages()[first_page_index() + index];
     ASSERT(physical_page);
     pte.set_physical_page_base(physical_page->paddr().get());
-    pte.set_present(true);
+    pte.set_present(is_readable());
     if (should_cow(index))
         pte.set_writable(false);
     else
@@ -239,7 +239,7 @@ void Region::map(PageDirectory& page_directory)
         auto& physical_page = vmobject().physical_pages()[first_page_index() + i];
         if (physical_page) {
             pte.set_physical_page_base(physical_page->paddr().get());
-            pte.set_present(true); // FIXME: Maybe we should use the is_readable flag here?
+            pte.set_present(is_readable());
             if (should_cow(i))
                 pte.set_writable(false);
             else
@@ -267,6 +267,11 @@ PageFaultResponse Region::handle_fault(const PageFault& fault)
 {
     auto page_index_in_region = page_index_from_address(fault.vaddr());
     if (fault.type() == PageFault::Type::PageNotPresent) {
+        if (!is_readable()) {
+            dbgprintf("NP(non-readable) fault in Region{%p}[%u]\n", this, page_index_in_region);
+            return PageFaultResponse::ShouldCrash;
+        }
+
         if (vmobject().is_inode()) {
 #ifdef PAGE_FAULT_DEBUG
             dbgprintf("NP(inode) fault in Region{%p}[%u]\n", this, page_index_in_region);

+ 8 - 0
Kernel/VM/Region.h

@@ -113,6 +113,14 @@ public:
             m_access &= ~Access::Write;
     }
 
+    void set_readable(bool b)
+    {
+        if (b)
+            m_access |= Access::Read;
+        else
+            m_access &= ~Access::Read;
+    }
+
     void map(PageDirectory&);
     enum class ShouldDeallocateVirtualMemoryRange {
         No,

+ 23 - 0
Userland/crash.cpp

@@ -26,6 +26,8 @@ int main(int argc, char** argv)
         InvalidStackPointerOnSyscall,
         InvalidStackPointerOnPageFault,
         SyscallFromWritableMemory,
+        WriteToFreedMemoryStillCachedByMalloc,
+        ReadFromFreedMemoryStillCachedByMalloc,
     };
     Mode mode = SegmentationViolation;
 
@@ -56,6 +58,10 @@ int main(int argc, char** argv)
         mode = InvalidStackPointerOnPageFault;
     else if (String(argv[1]) == "-S")
         mode = SyscallFromWritableMemory;
+    else if (String(argv[1]) == "-x")
+        mode = ReadFromFreedMemoryStillCachedByMalloc;
+    else if (String(argv[1]) == "-y")
+        mode = WriteToFreedMemoryStillCachedByMalloc;
     else
         print_usage_and_exit();
 
@@ -161,6 +167,23 @@ int main(int argc, char** argv)
         ((void(*)())buffer)();
     }
 
+    if (mode == ReadFromFreedMemoryStillCachedByMalloc) {
+        auto* ptr = (u8*)malloc(1024);
+        free(ptr);
+        dbgprintf("ptr = %p\n", ptr);
+        volatile auto foo = *ptr;
+        (void)foo;
+        ASSERT_NOT_REACHED();
+    }
+
+    if (mode == WriteToFreedMemoryStillCachedByMalloc) {
+        auto* ptr = (u8*)malloc(1024);
+        free(ptr);
+        dbgprintf("ptr = %p\n", ptr);
+        *ptr = 'x';
+        ASSERT_NOT_REACHED();
+    }
+
     ASSERT_NOT_REACHED();
     return 0;
 }