1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 19:17:44 +00:00

Kernel+LibELF: Abort ELF executable load sooner when something fails

Make it possible to bail out of ELF::Image::for_each_program_header()
and then do exactly that if something goes wrong during executable
loading in the kernel.

Also make the errors we return slightly more nuanced than just ENOEXEC.
This commit is contained in:
Andreas Kling 2020-12-25 14:42:42 +01:00
parent 791b32e3c6
commit 6c9a6bea1e
5 changed files with 52 additions and 39 deletions

View file

@ -208,8 +208,10 @@ bool Emulator::load_elf()
m_loader_text_size = region->size(); m_loader_text_size = region->size();
} }
mmu().add_region(move(region)); mmu().add_region(move(region));
return; return IterationDecision::Continue;
} }
return IterationDecision::Continue;
}); });
auto entry_point = interpreter_image.entry().offset(interpreter_load_offset).get(); auto entry_point = interpreter_image.entry().offset(interpreter_load_offset).get();

View file

@ -91,7 +91,7 @@ KResultOr<Process::LoadResult> Process::load_elf_object(FileDescription& object_
String elf_name = object_description.absolute_path(); String elf_name = object_description.absolute_path();
ASSERT(!Processor::current().in_critical()); ASSERT(!Processor::current().in_critical());
bool failed = false; KResult ph_load_result = KSuccess;
elf_image.for_each_program_header([&](const ELF::Image::ProgramHeader& program_header) { elf_image.for_each_program_header([&](const ELF::Image::ProgramHeader& program_header) {
if (program_header.type() == PT_TLS) { if (program_header.type() == PT_TLS) {
ASSERT(should_allocate_tls == ShouldAllocateTls::Yes); ASSERT(should_allocate_tls == ShouldAllocateTls::Yes);
@ -99,34 +99,36 @@ KResultOr<Process::LoadResult> Process::load_elf_object(FileDescription& object_
if (!elf_image.is_within_image(program_header.raw_data(), program_header.size_in_image())) { if (!elf_image.is_within_image(program_header.raw_data(), program_header.size_in_image())) {
dbgln("Shenanigans! ELF PT_TLS header sneaks outside of executable."); dbgln("Shenanigans! ELF PT_TLS header sneaks outside of executable.");
failed = true; ph_load_result = KResult(-ENOEXEC);
return; return IterationDecision::Break;
} }
master_tls_region = allocate_region({}, program_header.size_in_memory(), String::formatted("{} (master-tls)", elf_name), PROT_READ | PROT_WRITE); master_tls_region = allocate_region({}, program_header.size_in_memory(), String::formatted("{} (master-tls)", elf_name), PROT_READ | PROT_WRITE);
if (!master_tls_region) { if (!master_tls_region) {
failed = true; ph_load_result = KResult(-ENOMEM);
return; return IterationDecision::Break;
} }
master_tls_size = program_header.size_in_memory(); master_tls_size = program_header.size_in_memory();
master_tls_alignment = program_header.alignment(); master_tls_alignment = program_header.alignment();
if (!copy_to_user(master_tls_region->vaddr().as_ptr(), program_header.raw_data(), program_header.size_in_image())) { if (!copy_to_user(master_tls_region->vaddr().as_ptr(), program_header.raw_data(), program_header.size_in_image())) {
failed = false; ph_load_result = KResult(-EFAULT);
return; return IterationDecision::Break;
} }
return; return IterationDecision::Continue;
} }
if (program_header.type() != PT_LOAD) if (program_header.type() != PT_LOAD)
return; return IterationDecision::Continue;
if (program_header.is_writable()) { if (program_header.is_writable()) {
// Writable section: create a copy in memory.
ASSERT(program_header.size_in_memory()); ASSERT(program_header.size_in_memory());
ASSERT(program_header.alignment() == PAGE_SIZE); ASSERT(program_header.alignment() == PAGE_SIZE);
if (!elf_image.is_within_image(program_header.raw_data(), program_header.size_in_image())) { if (!elf_image.is_within_image(program_header.raw_data(), program_header.size_in_image())) {
dbgln("Shenanigans! Writable ELF PT_LOAD header sneaks outside of executable."); dbgln("Shenanigans! Writable ELF PT_LOAD header sneaks outside of executable.");
failed = true; ph_load_result = KResult(-ENOEXEC);
return; return IterationDecision::Break;
} }
int prot = 0; int prot = 0;
@ -137,8 +139,8 @@ KResultOr<Process::LoadResult> Process::load_elf_object(FileDescription& object_
auto region_name = String::formatted("{} (data-{}{})", elf_name, program_header.is_readable() ? "r" : "", program_header.is_writable() ? "w" : ""); auto region_name = String::formatted("{} (data-{}{})", elf_name, program_header.is_readable() ? "r" : "", program_header.is_writable() ? "w" : "");
auto* region = allocate_region(program_header.vaddr().offset(load_offset), program_header.size_in_memory(), move(region_name), prot); auto* region = allocate_region(program_header.vaddr().offset(load_offset), program_header.size_in_memory(), move(region_name), prot);
if (!region) { if (!region) {
failed = true; ph_load_result = KResult(-ENOMEM);
return; return IterationDecision::Break;
} }
// It's not always the case with PIE executables (and very well shouldn't be) that the // It's not always the case with PIE executables (and very well shouldn't be) that the
@ -151,33 +153,36 @@ KResultOr<Process::LoadResult> Process::load_elf_object(FileDescription& object_
auto page_offset = program_header.vaddr(); auto page_offset = program_header.vaddr();
page_offset.mask(~PAGE_MASK); page_offset.mask(~PAGE_MASK);
if (!copy_to_user((u8*)region->vaddr().as_ptr() + page_offset.get(), program_header.raw_data(), program_header.size_in_image())) { if (!copy_to_user((u8*)region->vaddr().as_ptr() + page_offset.get(), program_header.raw_data(), program_header.size_in_image())) {
failed = false; ph_load_result = KResult(-EFAULT);
return; return IterationDecision::Break;
} }
} else { return IterationDecision::Continue;
ASSERT(program_header.size_in_memory());
ASSERT(program_header.alignment() == PAGE_SIZE);
int prot = 0;
if (program_header.is_readable())
prot |= PROT_READ;
if (program_header.is_writable())
prot |= PROT_WRITE;
if (program_header.is_executable())
prot |= PROT_EXEC;
auto* region = allocate_region_with_vmobject(program_header.vaddr().offset(load_offset), program_header.size_in_memory(), *vmobject, program_header.offset(), elf_name, prot);
if (!region) {
failed = true;
return;
}
region->set_shared(true);
if (program_header.offset() == 0)
load_base_address = (FlatPtr)region->vaddr().as_ptr();
} }
// Non-writable section: map the executable itself in memory.
ASSERT(program_header.size_in_memory());
ASSERT(program_header.alignment() == PAGE_SIZE);
int prot = 0;
if (program_header.is_readable())
prot |= PROT_READ;
if (program_header.is_writable())
prot |= PROT_WRITE;
if (program_header.is_executable())
prot |= PROT_EXEC;
auto* region = allocate_region_with_vmobject(program_header.vaddr().offset(load_offset), program_header.size_in_memory(), *vmobject, program_header.offset(), elf_name, prot);
if (!region) {
ph_load_result = KResult(-ENOMEM);
return IterationDecision::Break;
}
region->set_shared(true);
if (program_header.offset() == 0)
load_base_address = (FlatPtr)region->vaddr().as_ptr();
return IterationDecision::Continue;
}); });
if (failed) { if (ph_load_result.is_error()) {
dbgln("do_exec: Failure loading program"); dbgln("do_exec: Failure loading program ({})", ph_load_result.error());
return KResult(-ENOEXEC); return ph_load_result;
} }
if (!elf_image.entry().offset(load_offset).get()) { if (!elf_image.entry().offset(load_offset).get()) {

View file

@ -101,6 +101,7 @@ RefPtr<DynamicObject> DynamicLoader::dynamic_object_from_image() const
if (program_header.type() == PT_DYNAMIC) { if (program_header.type() == PT_DYNAMIC) {
dynamic_section_address = VirtualAddress(program_header.raw_data()); dynamic_section_address = VirtualAddress(program_header.raw_data());
} }
return IterationDecision::Continue;
}); });
ASSERT(!dynamic_section_address.is_null()); ASSERT(!dynamic_section_address.is_null());
@ -114,6 +115,7 @@ size_t DynamicLoader::calculate_tls_size() const
if (program_header.type() == PT_TLS) { if (program_header.type() == PT_TLS) {
tls_size = program_header.size_in_memory(); tls_size = program_header.size_in_memory();
} }
return IterationDecision::Continue;
}); });
return tls_size; return tls_size;
} }
@ -232,6 +234,7 @@ void DynamicLoader::load_program_headers()
} else if (region.is_dynamic()) { } else if (region.is_dynamic()) {
dynamic_region_desired_vaddr = region.desired_load_address(); dynamic_region_desired_vaddr = region.desired_load_address();
} }
return IterationDecision::Continue;
}); });
ASSERT(text_region_ptr && data_region_ptr); ASSERT(text_region_ptr && data_region_ptr);

View file

@ -108,6 +108,7 @@ void Image::dump() const
dbgprintf(" flags: %x\n", program_header.flags()); dbgprintf(" flags: %x\n", program_header.flags());
dbgprintf(" \n"); dbgprintf(" \n");
dbgprintf(" }\n"); dbgprintf(" }\n");
return IterationDecision::Continue;
}); });
for (unsigned i = 0; i < header().e_shnum; ++i) { for (unsigned i = 0; i < header().e_shnum; ++i) {

View file

@ -287,8 +287,10 @@ template<typename F>
inline void Image::for_each_program_header(F func) const inline void Image::for_each_program_header(F func) const
{ {
auto program_header_count = this->program_header_count(); auto program_header_count = this->program_header_count();
for (unsigned i = 0; i < program_header_count; ++i) for (unsigned i = 0; i < program_header_count; ++i) {
func(program_header(i)); if (func(program_header(i)) == IterationDecision::Break)
return;
}
} }
} // end namespace ELF } // end namespace ELF