From e5b26410190200f0b63ddd426f78ffe0d6bec855 Mon Sep 17 00:00:00 2001 From: apio Date: Thu, 3 Nov 2022 20:28:54 +0100 Subject: [PATCH] Kernel: Enforce W^X when loading executables From now on, if an executable contains segments that want to be loaded as both writable and executable, we refuse and abort with ENOEXEC. --- kernel/src/sys/elf/ELFLoader.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/kernel/src/sys/elf/ELFLoader.cpp b/kernel/src/sys/elf/ELFLoader.cpp index 3003b9f9..80b5a53a 100644 --- a/kernel/src/sys/elf/ELFLoader.cpp +++ b/kernel/src/sys/elf/ELFLoader.cpp @@ -25,6 +25,16 @@ static const char* format_permissions(uint32_t flags) return perms; } +static bool can_execute_segment(int flags) +{ + return flags & 1; +} + +static bool can_write_segment(int flags) +{ + return flags & 2; +} + ELFImage* ELFLoader::load_elf_from_filesystem(const char* filename) { VFS::Node* node = VFS::resolve_path(filename); @@ -90,6 +100,8 @@ ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node) phdr.p_filesz, phdr.p_memsz, format_permissions(phdr.p_flags)); ensure(phdr.p_vaddr); + ensure(!(can_write_segment(phdr.p_flags) && can_execute_segment(phdr.p_flags))); + uint64_t pages = Utilities::get_blocks_from_size(PAGE_SIZE, (phdr.p_vaddr % PAGE_SIZE) + phdr.p_memsz); void* buffer = (void*)((uint64_t)MemoryManager::get_pages_at(round_down_to_nearest_page(phdr.p_vaddr), pages, MAP_READ_WRITE) + @@ -106,8 +118,8 @@ ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node) VMM::switch_to_previous_user_address_space(); int new_flags = MAP_USER | MAP_AS_OWNED_BY_TASK; - if(phdr.p_flags & 2) new_flags |= MAP_READ_WRITE; - if(phdr.p_flags & 1) new_flags |= MAP_EXEC; + if (can_write_segment(phdr.p_flags)) new_flags |= MAP_READ_WRITE; + else if (can_execute_segment(phdr.p_flags)) new_flags |= MAP_EXEC; MemoryManager::protect(buffer, pages, new_flags); @@ -181,6 +193,11 @@ long ELFLoader::check_elf_image(VFS::Node* node) kerrorln("trying to load ELF into kernel memory"); return -ENOEXEC; } + if (can_write_segment(phdr.p_flags) && can_execute_segment(phdr.p_flags)) + { + kwarnln("executable violates W^X"); + return -ENOEXEC; + } loadable_sections++; memusage += Utilities::get_blocks_from_size(PAGE_SIZE, phdr.p_memsz) * PAGE_SIZE; } @@ -202,4 +219,4 @@ void ELFLoader::release_elf_image(ELFImage* image) MemoryManager::release_pages((void*)round_down_to_nearest_page(section.base), section.pages); } kfree(image); -} \ No newline at end of file +}