ELFLoader: Add check_elf_image() and check_elf_image_from_filesystem()
These two functions validate an image, without actually loading it. Very useful for exec!
This commit is contained in:
parent
261fc73146
commit
3ac9fed23a
@ -5,7 +5,9 @@
|
|||||||
|
|
||||||
namespace ELFLoader
|
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);
|
ELFImage* load_elf_from_filesystem(const char* filename);
|
||||||
void release_elf_image(ELFImage* image);
|
void release_elf_image(ELFImage* image);
|
||||||
|
int check_elf_image(VFS::Node* node);
|
||||||
|
int check_elf_image_from_filesystem(const char* filename);
|
||||||
}
|
}
|
@ -45,7 +45,7 @@ uint64_t Memory::get_usable()
|
|||||||
|
|
||||||
bool Memory::is_kernel_address(uintptr_t address)
|
bool Memory::is_kernel_address(uintptr_t address)
|
||||||
{
|
{
|
||||||
return address > 0xfffffffff8000000;
|
return address >= 0xfffffffff8000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Memory::is_user_address(uintptr_t address)
|
bool Memory::is_user_address(uintptr_t address)
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
#define MODULE "elf"
|
#define MODULE "elf"
|
||||||
|
|
||||||
#include "sys/elf/ELFLoader.h"
|
#include "sys/elf/ELFLoader.h"
|
||||||
|
#include "assert.h"
|
||||||
|
#include "errno.h"
|
||||||
#include "fs/VFS.h"
|
#include "fs/VFS.h"
|
||||||
#include "init/InitRD.h"
|
#include "init/InitRD.h"
|
||||||
#include "log/Log.h"
|
#include "log/Log.h"
|
||||||
|
#include "memory/Memory.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "misc/utils.h"
|
#include "misc/utils.h"
|
||||||
#include "std/stdlib.h"
|
#include "std/stdlib.h"
|
||||||
@ -40,44 +43,35 @@ ELFImage* ELFLoader::load_elf_from_filesystem(const char* filename)
|
|||||||
return result;
|
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)
|
ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node)
|
||||||
{
|
{
|
||||||
Elf64_Ehdr elf_ehdr;
|
Elf64_Ehdr elf_ehdr;
|
||||||
if (VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) < 0)
|
ASSERT(VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) >= 0);
|
||||||
{
|
ASSERT(strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) == 0);
|
||||||
kwarnln("Unable to read ELF header");
|
ASSERT(elf_ehdr.e_ident[EI_CLASS] == ELFCLASS64);
|
||||||
return 0;
|
ASSERT(elf_ehdr.e_ident[EI_DATA] == ELFDATA2LSB);
|
||||||
}
|
ASSERT(elf_ehdr.e_type == ET_EXEC);
|
||||||
if (strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) != 0)
|
ASSERT(elf_ehdr.e_machine == EM_MACH);
|
||||||
{
|
ASSERT(elf_ehdr.e_phnum != 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;
|
|
||||||
}
|
|
||||||
ELFImage* image = (ELFImage*)kmalloc(sizeof(ELFImage) - sizeof(ELFSection));
|
ELFImage* image = (ELFImage*)kmalloc(sizeof(ELFImage) - sizeof(ELFSection));
|
||||||
memset(image, 0, sizeof(ELFImage) - sizeof(ELFSection));
|
memset(image, 0, sizeof(ELFImage) - sizeof(ELFSection));
|
||||||
image->entry = elf_ehdr.e_entry;
|
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,
|
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));
|
phdr.p_filesz, phdr.p_memsz, format_permissions(phdr.p_flags));
|
||||||
if (!phdr.p_vaddr)
|
ASSERT(phdr.p_vaddr);
|
||||||
{
|
|
||||||
kerrorln("Address is NULL, this is invalid :(");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
uint64_t pages = Utilities::get_blocks_from_size(PAGE_SIZE, (phdr.p_vaddr % PAGE_SIZE) + phdr.p_memsz);
|
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(
|
void* buffer = (void*)((uint64_t)MemoryManager::get_pages_at(
|
||||||
Utilities::round_down_to_nearest_page(phdr.p_vaddr), pages,
|
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"); }
|
else { kdbgln("skipping non-loadable segment"); }
|
||||||
}
|
}
|
||||||
if (!image->section_count)
|
ASSERT(image->section_count);
|
||||||
{
|
|
||||||
kfree(image);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return image;
|
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)
|
void ELFLoader::release_elf_image(ELFImage* image)
|
||||||
{
|
{
|
||||||
for (uint64_t i = 0; i < image->section_count; i++)
|
for (uint64_t i = 0; i < image->section_count; i++)
|
||||||
|
@ -117,11 +117,19 @@ void Scheduler::add_user_task(void* task)
|
|||||||
void Scheduler::load_user_task(const char* filename)
|
void Scheduler::load_user_task(const char* filename)
|
||||||
{
|
{
|
||||||
kinfoln("Loading user task: %s", 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;
|
Task* new_task = new Task;
|
||||||
ASSERT(new_task);
|
ASSERT(new_task);
|
||||||
memset(&new_task->regs, 0, sizeof(Context));
|
memset(&new_task->regs, 0, sizeof(Context));
|
||||||
new_task->id = free_tid++;
|
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)
|
if (!image)
|
||||||
{
|
{
|
||||||
kerrorln("Failed to load %s from initrd", filename);
|
kerrorln("Failed to load %s from initrd", filename);
|
||||||
@ -150,6 +158,7 @@ void Scheduler::load_user_task(const char* filename)
|
|||||||
task_num++;
|
task_num++;
|
||||||
kinfoln("Adding user task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip,
|
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);
|
new_task->id, new_task->regs.rsp, task_num);
|
||||||
|
Interrupts::pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::reset_task(Task* task, ELFImage* new_image)
|
void Scheduler::reset_task(Task* task, ELFImage* new_image)
|
||||||
|
Loading…
Reference in New Issue
Block a user