diff --git a/kernel/include/sys/elf/ELFLoader.h b/kernel/include/sys/elf/ELFLoader.h index 68d5bc97..22116bd5 100644 --- a/kernel/include/sys/elf/ELFLoader.h +++ b/kernel/include/sys/elf/ELFLoader.h @@ -5,7 +5,9 @@ namespace ELFLoader { - ELFImage* load_elf_from_vfs(VFS::Node* node); + ELFImage* load_elf_from_vfs(VFS::Node* node); // This function assumes check_elf_image has been called first. ELFImage* load_elf_from_filesystem(const char* filename); void release_elf_image(ELFImage* image); + int check_elf_image(VFS::Node* node); + int check_elf_image_from_filesystem(const char* filename); } \ No newline at end of file diff --git a/kernel/src/memory/Memory.cpp b/kernel/src/memory/Memory.cpp index 705ed483..6ff02898 100644 --- a/kernel/src/memory/Memory.cpp +++ b/kernel/src/memory/Memory.cpp @@ -45,7 +45,7 @@ uint64_t Memory::get_usable() bool Memory::is_kernel_address(uintptr_t address) { - return address > 0xfffffffff8000000; + return address >= 0xfffffffff8000000; } bool Memory::is_user_address(uintptr_t address) diff --git a/kernel/src/sys/elf/ELFLoader.cpp b/kernel/src/sys/elf/ELFLoader.cpp index ea3ff62c..0bc70cdd 100644 --- a/kernel/src/sys/elf/ELFLoader.cpp +++ b/kernel/src/sys/elf/ELFLoader.cpp @@ -1,9 +1,12 @@ #define MODULE "elf" #include "sys/elf/ELFLoader.h" +#include "assert.h" +#include "errno.h" #include "fs/VFS.h" #include "init/InitRD.h" #include "log/Log.h" +#include "memory/Memory.h" #include "memory/MemoryManager.h" #include "misc/utils.h" #include "std/stdlib.h" @@ -40,44 +43,35 @@ ELFImage* ELFLoader::load_elf_from_filesystem(const char* filename) return result; } +int ELFLoader::check_elf_image_from_filesystem(const char* filename) +{ + VFS::Node* node = VFS::resolve_path(filename); + + if (!node) + { + kwarnln("Failed to open file %s for checking", filename); + return -ENOENT; + } + + if (node->type == VFS_DIRECTORY) + { + kwarnln("Failed to check %s: is a directory", filename); + return -EISDIR; + } + + return check_elf_image(node); +} + ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node) { Elf64_Ehdr elf_ehdr; - if (VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) < 0) - { - kwarnln("Unable to read ELF header"); - return 0; - } - if (strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) != 0) - { - kwarnln("ELF file has invalid magic, skipping"); - return 0; - } - if (elf_ehdr.e_ident[EI_CLASS] != ELFCLASS64) - { - kwarnln("ELF file is not ELF64, skipping"); - return 0; - } - if (elf_ehdr.e_ident[EI_DATA] != ELFDATA2LSB) - { - kwarnln("ELF file is not little-endian, skipping"); - return 0; - } - if (elf_ehdr.e_type != ET_EXEC) - { - kwarnln("not supported: ELF file is not an executable"); - return 0; - } - if (elf_ehdr.e_machine != EM_MACH) - { - kwarnln("Unsupported target machine"); - return 0; - } - if (elf_ehdr.e_phnum == 0) - { - kwarnln("ELF file has no PHDRS"); - return 0; - } + ASSERT(VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) >= 0); + ASSERT(strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) == 0); + ASSERT(elf_ehdr.e_ident[EI_CLASS] == ELFCLASS64); + ASSERT(elf_ehdr.e_ident[EI_DATA] == ELFDATA2LSB); + ASSERT(elf_ehdr.e_type == ET_EXEC); + ASSERT(elf_ehdr.e_machine == EM_MACH); + ASSERT(elf_ehdr.e_phnum != 0); ELFImage* image = (ELFImage*)kmalloc(sizeof(ELFImage) - sizeof(ELFSection)); memset(image, 0, sizeof(ELFImage) - sizeof(ELFSection)); image->entry = elf_ehdr.e_entry; @@ -90,11 +84,7 @@ ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node) { kdbgln("Loading loadable segment at address %lx, file size %ld, mem size %ld, permissions %s", phdr.p_vaddr, phdr.p_filesz, phdr.p_memsz, format_permissions(phdr.p_flags)); - if (!phdr.p_vaddr) - { - kerrorln("Address is NULL, this is invalid :("); - return 0; - } + ASSERT(phdr.p_vaddr); 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( Utilities::round_down_to_nearest_page(phdr.p_vaddr), pages, @@ -111,14 +101,76 @@ ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node) } else { kdbgln("skipping non-loadable segment"); } } - if (!image->section_count) - { - kfree(image); - return 0; - } + ASSERT(image->section_count); return image; } +int ELFLoader::check_elf_image(VFS::Node* node) +{ + Elf64_Ehdr elf_ehdr; + if (VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) < 0) + { + kwarnln("Unable to read ELF header"); + return -1; + } + if (strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) != 0) + { + kwarnln("ELF file has invalid magic, skipping"); + return -1; + } + if (elf_ehdr.e_ident[EI_CLASS] != ELFCLASS64) + { + kwarnln("ELF file is not ELF64, skipping"); + return -1; + } + if (elf_ehdr.e_ident[EI_DATA] != ELFDATA2LSB) + { + kwarnln("ELF file is not little-endian, skipping"); + return -1; + } + if (elf_ehdr.e_type != ET_EXEC) + { + kwarnln("not supported: ELF file is not an executable"); + return -1; + } + if (elf_ehdr.e_machine != EM_MACH) + { + kwarnln("Unsupported target machine"); + return -1; + } + if (elf_ehdr.e_phnum == 0) + { + kwarnln("ELF file has no PHDRS"); + return -1; + } + int i, loadable_sections; + Elf64_Phdr phdr; + for (VFS::read(node, elf_ehdr.e_phoff, sizeof(Elf64_Phdr), (char*)&phdr), i = 0; i < elf_ehdr.e_phnum; + i++, VFS::read(node, elf_ehdr.e_phoff + (i * elf_ehdr.e_phentsize), sizeof(Elf64_Phdr), (char*)&phdr)) + { + if (phdr.p_type == PT_LOAD) + { + if (!phdr.p_vaddr) + { + kerrorln("segment address is NULL, this is invalid :("); + return -1; + } + if (Memory::is_kernel_address(phdr.p_vaddr) || Memory::is_kernel_address(phdr.p_vaddr + phdr.p_memsz)) + { + kerrorln("trying to load ELF into kernel memory"); + return -1; + } + loadable_sections++; + } + } + if (!loadable_sections) + { + kwarnln("No loadable sections"); + return -1; + } + return 0; +} + void ELFLoader::release_elf_image(ELFImage* image) { for (uint64_t i = 0; i < image->section_count; i++) diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 05136040..8d8a85c6 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -117,11 +117,19 @@ void Scheduler::add_user_task(void* task) void Scheduler::load_user_task(const char* filename) { kinfoln("Loading user task: %s", filename); + Interrupts::push_and_disable(); + if (ELFLoader::check_elf_image_from_filesystem(filename) < 0) + { + kerrorln("Failed to load %s from initrd", filename); + return; + } Task* new_task = new Task; ASSERT(new_task); memset(&new_task->regs, 0, sizeof(Context)); new_task->id = free_tid++; - ELFImage* image = ELFLoader::load_elf_from_filesystem(filename); + ELFImage* image = ELFLoader::load_elf_from_filesystem( + filename); // FIXME: TOCTOU? Right now, impossible, since interrupts are disabled and SMP is not a thing. But in + // the future, it might be possible. if (!image) { kerrorln("Failed to load %s from initrd", filename); @@ -150,6 +158,7 @@ void Scheduler::load_user_task(const char* filename) task_num++; kinfoln("Adding user task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip, new_task->id, new_task->regs.rsp, task_num); + Interrupts::pop(); } void Scheduler::reset_task(Task* task, ELFImage* new_image)