diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-11-27 12:40:42 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-11-27 12:40:42 +0100 |
commit | 2d1bcce34af870228165a894d9e0200255ec1ba5 (patch) | |
tree | 388852f4fc050c7f238c37faa50f8dd6d65cd734 /Kernel/VM | |
parent | 5b8cf2ee234c708f1bca3df9abf49a8625b227cc (diff) | |
download | serenity-2d1bcce34af870228165a894d9e0200255ec1ba5.zip |
Kernel: Fix triple-fault when clicking on SystemServer in SystemMonitor
The fault was happening when retrieving a current backtrace for the
SystemServer process.
To generate a backtrace, we go into the paging scope of the process,
meaning we temporarily switch to using its page directory as our own.
Because kernel VM is allocated on demand, it's possible for a process's
mappings above the 3GB mark to be out-of-date. Normally this just gets
fixed up transparently by the page fault handler (which simply copies
the PDE from the canonical MM.kernel_page_directory() into the current
process.)
However, if the current kernel *stack* is in a piece of memory that
the backtraced process lacks up-to-date PDE's for, we still get a page
fault, but are unable to handle it, since the CPU wants to push to the
stack as part of calling the page fault handler. So we're screwed and
it's a triple-fault.
Fix this by always updating the kernel VM mappings before switching
into a paging scope. In practical terms, this is a 1KB memcpy() that
happens when generating a backtrace, or doing exec().
Diffstat (limited to 'Kernel/VM')
-rw-r--r-- | Kernel/VM/MemoryManager.cpp | 6 | ||||
-rw-r--r-- | Kernel/VM/PageDirectory.cpp | 8 | ||||
-rw-r--r-- | Kernel/VM/PageDirectory.h | 2 |
3 files changed, 16 insertions, 0 deletions
diff --git a/Kernel/VM/MemoryManager.cpp b/Kernel/VM/MemoryManager.cpp index dcfb041828..540081d26c 100644 --- a/Kernel/VM/MemoryManager.cpp +++ b/Kernel/VM/MemoryManager.cpp @@ -479,6 +479,12 @@ void MemoryManager::enter_process_paging_scope(Process& process) { ASSERT(current); InterruptDisabler disabler; + + // NOTE: To prevent triple-faulting here, we have to ensure that the current stack + // is accessible to the incoming page directory. We achieve this by forcing + // an update of the kernel VM mappings in the entered scope's page directory. + process.page_directory().update_kernel_mappings(); + current->tss().cr3 = process.page_directory().cr3(); asm volatile("movl %%eax, %%cr3" ::"a"(process.page_directory().cr3()) : "memory"); diff --git a/Kernel/VM/PageDirectory.cpp b/Kernel/VM/PageDirectory.cpp index 3533d6cfa9..9cc949857a 100644 --- a/Kernel/VM/PageDirectory.cpp +++ b/Kernel/VM/PageDirectory.cpp @@ -57,3 +57,11 @@ void PageDirectory::flush(VirtualAddress vaddr) if (this == &MM.kernel_page_directory() || ¤t->process().page_directory() == this) MM.flush_tlb(vaddr); } + +void PageDirectory::update_kernel_mappings() +{ + // This ensures that the kernel virtual address space is up-to-date in this page directory. + // This may be necessary to avoid triple faulting when entering a process's paging scope + // whose mappings are out-of-date. + memcpy(entries() + 768, MM.kernel_page_directory().entries() + 768, sizeof(PageDirectoryEntry) * 256); +} diff --git a/Kernel/VM/PageDirectory.h b/Kernel/VM/PageDirectory.h index b6458c1055..3ffb438bbe 100644 --- a/Kernel/VM/PageDirectory.h +++ b/Kernel/VM/PageDirectory.h @@ -31,6 +31,8 @@ public: Process* process() { return m_process; } const Process* process() const { return m_process; } + void update_kernel_mappings(); + private: PageDirectory(Process&, const RangeAllocator* parent_range_allocator); explicit PageDirectory(PhysicalAddress); |