From 2bf65ef512bf1beca9d7e1a538d73841f6917eda Mon Sep 17 00:00:00 2001 From: Bananymous Date: Wed, 28 Aug 2024 17:06:32 +0300 Subject: [PATCH] Kernel: Invoke ELF interpreter instead if it is specified --- kernel/arch/i686/Thread.S | 7 +- kernel/arch/x86_64/Thread.S | 7 +- kernel/include/kernel/Process.h | 1 + kernel/kernel/Process.cpp | 9 +- kernel/kernel/Thread.cpp | 11 +- .../libraries/LibELF/LibELF/LoadableELF.cpp | 496 ++++++++++++------ .../LibELF/include/LibELF/LoadableELF.h | 42 +- 7 files changed, 382 insertions(+), 191 deletions(-) diff --git a/kernel/arch/i686/Thread.S b/kernel/arch/i686/Thread.S index ef8dd7d7..d7ce92f1 100644 --- a/kernel/arch/i686/Thread.S +++ b/kernel/arch/i686/Thread.S @@ -55,14 +55,15 @@ start_userspace_thread: movw %bx, %gs xorw %bx, %bx - popl %edx - popl %esi popl %edi + popl %esi + popl %edx popl %ecx + popl %ebx pushl $(0x20 | 3) pushl %eax pushl $0x202 pushl $(0x18 | 3) - pushl %ecx + pushl %ebx iret diff --git a/kernel/arch/x86_64/Thread.S b/kernel/arch/x86_64/Thread.S index a1757157..079b525c 100644 --- a/kernel/arch/x86_64/Thread.S +++ b/kernel/arch/x86_64/Thread.S @@ -39,14 +39,15 @@ start_userspace_thread: call get_userspace_thread_stack_top - popq %rdx - popq %rsi popq %rdi + popq %rsi + popq %rdx popq %rcx + popq %rbx pushq $(0x20 | 3) pushq %rax pushq $0x202 pushq $(0x18 | 3) - pushq %rcx + pushq %rbx iretq diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index ce719a10..53417976 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -41,6 +41,7 @@ namespace Kernel int argc { 0 }; char** argv { nullptr }; char** envp { nullptr }; + int file_fd { -1 }; }; public: diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 0a9679b6..5f397835 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -411,7 +411,7 @@ namespace Kernel } auto file = TRY(VirtualFileSystem::get().file_from_absolute_path(credentials, absolute_path, O_EXEC)); - return TRY(LibELF::LoadableELF::load_from_inode(page_table, file.inode)); + return TRY(LibELF::LoadableELF::load_from_inode(page_table, credentials, file.inode)); } BAN::ErrorOr Process::sys_fork(uintptr_t sp, uintptr_t ip) @@ -515,6 +515,13 @@ namespace Kernel m_loadable_elf->reserve_address_space(); m_loadable_elf->update_suid_sgid(m_credentials); m_userspace_info.entry = m_loadable_elf->entry_point(); + if (m_loadable_elf->has_interpreter()) + { + VirtualFileSystem::File file; + TRY(file.canonical_path.append("")); + file.inode = m_loadable_elf->inode(); + m_userspace_info.file_fd = TRY(m_open_file_descriptors.open(BAN::move(file), O_EXEC)); + } for (size_t i = 0; i < sizeof(m_signal_handlers) / sizeof(*m_signal_handlers); i++) { diff --git a/kernel/kernel/Thread.cpp b/kernel/kernel/Thread.cpp index fd29666f..867ca8d0 100644 --- a/kernel/kernel/Thread.cpp +++ b/kernel/kernel/Thread.cpp @@ -190,22 +190,23 @@ namespace Kernel // Signal mask is inherited - auto& userspace_info = process().userspace_info(); + const auto& userspace_info = process().userspace_info(); ASSERT(userspace_info.entry); // Initialize stack for returning PageTable::with_fast_page(process().page_table().physical_address_of(kernel_stack_top() - PAGE_SIZE), [&] { uintptr_t sp = PageTable::fast_page() + PAGE_SIZE; write_to_stack(sp, userspace_info.entry); - write_to_stack(sp, userspace_info.argc); - write_to_stack(sp, userspace_info.argv); + write_to_stack(sp, userspace_info.file_fd); write_to_stack(sp, userspace_info.envp); + write_to_stack(sp, userspace_info.argv); + write_to_stack(sp, userspace_info.argc); }); - m_interrupt_stack.ip = reinterpret_cast(start_userspace_thread);; + m_interrupt_stack.ip = reinterpret_cast(start_userspace_thread); m_interrupt_stack.cs = 0x08; m_interrupt_stack.flags = 0x002; - m_interrupt_stack.sp = kernel_stack_top() - 4 * sizeof(uintptr_t); + m_interrupt_stack.sp = kernel_stack_top() - 5 * sizeof(uintptr_t); m_interrupt_stack.ss = 0x10; memset(&m_interrupt_registers, 0, sizeof(InterruptRegisters)); diff --git a/userspace/libraries/LibELF/LibELF/LoadableELF.cpp b/userspace/libraries/LibELF/LibELF/LoadableELF.cpp index d556a879..b9fa5531 100644 --- a/userspace/libraries/LibELF/LibELF/LoadableELF.cpp +++ b/userspace/libraries/LibELF/LibELF/LoadableELF.cpp @@ -1,145 +1,253 @@ #include +#include #include #include #include #include #include +#include namespace LibELF { using namespace Kernel; - BAN::ErrorOr> LoadableELF::load_from_inode(PageTable& page_table, BAN::RefPtr inode) + BAN::ErrorOr> LoadableELF::load_from_inode(PageTable& page_table, const Credentials& credentials, BAN::RefPtr inode) { - auto* elf_ptr = new LoadableELF(page_table, inode); - if (elf_ptr == nullptr) - return BAN::Error::from_errno(ENOMEM); - auto elf = BAN::UniqPtr::adopt(elf_ptr); - TRY(elf->initialize()); - return BAN::move(elf); + auto elf = TRY(BAN::UniqPtr::create(page_table)); + TRY(elf->initialize(credentials, inode)); + return elf; } - LoadableELF::LoadableELF(PageTable& page_table, BAN::RefPtr inode) - : m_inode(inode) - , m_page_table(page_table) + LoadableELF::LoadableELF(PageTable& page_table) + : m_page_table(page_table) { } LoadableELF::~LoadableELF() { - if (!m_loaded) - return; - for (const auto& program_header : m_program_headers) - { - switch (program_header.p_type) + const auto cleanup_program_headers = + [&](BAN::Span headers) { - case PT_NULL: - continue; - case PT_LOAD: + for (const auto& header : headers) { - vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK; - size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz); + ASSERT(header.p_type == PT_LOAD); + + const vaddr_t vaddr = header.p_vaddr & PAGE_ADDR_MASK; + const size_t pages = range_page_count(header.p_vaddr, header.p_memsz); for (size_t i = 0; i < pages; i++) - { - paddr_t paddr = m_page_table.physical_address_of(start + i * PAGE_SIZE); - if (paddr != 0) + if (paddr_t paddr = m_page_table.physical_address_of(vaddr + i * PAGE_SIZE)) Heap::get().release_page(paddr); - } - m_page_table.unmap_range(start, pages * PAGE_SIZE); - break; + m_page_table.unmap_range(vaddr, pages * PAGE_SIZE); } - default: - ASSERT_NOT_REACHED(); - } - } + }; + + if (!m_is_loaded) + return; + cleanup_program_headers(m_executable.program_headers.span()); + cleanup_program_headers(m_interpreter.program_headers.span()); } - BAN::ErrorOr LoadableELF::initialize() + static BAN::ErrorOr read_and_validate_file_header(BAN::RefPtr inode) { - if ((size_t)m_inode->size() < sizeof(ElfNativeFileHeader)) + if ((size_t)inode->size() < sizeof(ElfNativeFileHeader)) { - dprintln("Too small file"); + dprintln("File is too small to be ELF"); return BAN::Error::from_errno(ENOEXEC); } - size_t nread = TRY(m_inode->read(0, BAN::ByteSpan::from(m_file_header))); - ASSERT(nread == sizeof(m_file_header)); + ElfNativeFileHeader file_header; - if (m_file_header.e_ident[EI_MAG0] != ELFMAG0 || - m_file_header.e_ident[EI_MAG1] != ELFMAG1 || - m_file_header.e_ident[EI_MAG2] != ELFMAG2 || - m_file_header.e_ident[EI_MAG3] != ELFMAG3) + size_t nread = TRY(inode->read(0, BAN::ByteSpan::from(file_header))); + ASSERT(nread == sizeof(file_header)); + + if (file_header.e_ident[EI_MAG0] != ELFMAG0 || + file_header.e_ident[EI_MAG1] != ELFMAG1 || + file_header.e_ident[EI_MAG2] != ELFMAG2 || + file_header.e_ident[EI_MAG3] != ELFMAG3) { - dprintln("Invalid magic in header"); + dprintln("Not an ELF file"); return BAN::Error::from_errno(ENOEXEC); } - if (m_file_header.e_ident[EI_DATA] != ELFDATA2LSB) + if (file_header.e_ident[EI_DATA] != ELFDATA2LSB) { - dprintln("Only little-endian is supported"); + dprintln("Not in little-endian"); return BAN::Error::from_errno(ENOEXEC); } - if (m_file_header.e_ident[EI_VERSION] != EV_CURRENT) + if (file_header.e_ident[EI_VERSION] != EV_CURRENT) { - dprintln("Invalid version"); + dprintln("Unsupported version {}", file_header.e_ident[EI_VERSION]); return BAN::Error::from_errno(ENOEXEC); } #if ARCH(i686) - if (m_file_header.e_ident[EI_CLASS] != ELFCLASS32) + if (file_header.e_ident[EI_CLASS] != ELFCLASS32) #elif ARCH(x86_64) - if (m_file_header.e_ident[EI_CLASS] != ELFCLASS64) + if (file_header.e_ident[EI_CLASS] != ELFCLASS64) #endif { dprintln("Not in native format"); return BAN::Error::from_errno(EINVAL); } - if (m_file_header.e_type != ET_EXEC && m_file_header.e_type != ET_DYN) + if (file_header.e_type != ET_EXEC && file_header.e_type != ET_DYN) { - dprintln("Unsupported file header type {}", m_file_header.e_type); + dprintln("Unsupported file header type {}", file_header.e_type); return BAN::Error::from_errno(ENOTSUP); } - if (m_file_header.e_version != EV_CURRENT) + if (file_header.e_version != EV_CURRENT) { - dprintln("Unsupported version"); + dprintln("Unsupported version {}", file_header.e_version); return BAN::Error::from_errno(EINVAL); } - ASSERT(m_file_header.e_phentsize <= sizeof(ElfNativeProgramHeader)); + if (file_header.e_phentsize < sizeof(ElfNativeProgramHeader)) + { + dprintln("Too small program header size ({} bytes)", file_header.e_phentsize); + return BAN::Error::from_errno(EINVAL); + } + + return file_header; + } + + BAN::ErrorOr LoadableELF::load_elf_file(const Credentials& credentials, BAN::RefPtr inode) const + { + auto file_header = TRY(read_and_validate_file_header(inode)); BAN::Vector pheader_buffer; - TRY(pheader_buffer.resize(m_file_header.e_phnum * m_file_header.e_phentsize)); - TRY(m_inode->read(m_file_header.e_phoff, BAN::ByteSpan(pheader_buffer.span()))); + TRY(pheader_buffer.resize(file_header.e_phnum * file_header.e_phentsize)); + TRY(inode->read(file_header.e_phoff, BAN::ByteSpan(pheader_buffer.span()))); - const vaddr_t dyn_base = (m_file_header.e_type == ET_DYN) ? 0x40000000 + (Random::get_u32() & 0x3FFFF000) : 0; - m_file_header.e_entry += dyn_base; + BAN::Vector program_headers; + BAN::RefPtr interp; - for (size_t i = 0; i < m_file_header.e_phnum; i++) + for (size_t i = 0; i < file_header.e_phnum; i++) { - const auto& pheader = *reinterpret_cast(pheader_buffer.data() + i * m_file_header.e_phentsize); + const auto& pheader = *reinterpret_cast(pheader_buffer.data() + i * file_header.e_phentsize); if (pheader.p_memsz < pheader.p_filesz) { - dprintln("Invalid program header"); + dprintln("Invalid program header, memsz less than filesz"); return BAN::Error::from_errno(EINVAL); } switch (pheader.p_type) { - case PT_NULL: - case PT_DYNAMIC: - break; case PT_LOAD: - TRY(m_program_headers.push_back(pheader)); - m_program_headers.back().p_vaddr += dyn_base; - m_virtual_page_count += BAN::Math::div_round_up((pheader.p_vaddr % PAGE_SIZE) + pheader.p_memsz, PAGE_SIZE); + for (const auto& program_header : program_headers) + { + const vaddr_t a1 = program_header.p_vaddr & PAGE_ADDR_MASK; + const vaddr_t b1 = pheader.p_vaddr & PAGE_ADDR_MASK; + const vaddr_t a2 = (program_header.p_vaddr + program_header.p_memsz + PAGE_SIZE - 1) & PAGE_ADDR_MASK; + const vaddr_t b2 = (pheader.p_vaddr + pheader.p_memsz + PAGE_SIZE - 1) & PAGE_ADDR_MASK; + if (a1 < b2 && b1 < a2) + { + dwarnln("Overlapping LOAD segments"); + return BAN::Error::from_errno(EINVAL); + } + } + TRY(program_headers.push_back(pheader)); break; + case PT_INTERP: + { + BAN::Vector buffer; + TRY(buffer.resize(pheader.p_memsz, 0)); + TRY(inode->read(pheader.p_offset, BAN::ByteSpan(buffer.data(), pheader.p_filesz))); + + BAN::StringView path(reinterpret_cast(buffer.data())); + interp = TRY(VirtualFileSystem::get().file_from_absolute_path(credentials, path, O_EXEC)).inode; + break; + } default: - dprintln("Unsupported program header type {}", pheader.p_type); - return BAN::Error::from_errno(ENOTSUP); + break; + } + } + + return LoadResult { + .elf_file = { + .inode = inode, + .file_header = file_header, + .program_headers = BAN::move(program_headers), + .dynamic_base = 0 + }, + .interp = interp + }; + } + + bool LoadableELF::does_executable_and_interpreter_overlap() const + { + ASSERT(m_executable.inode); + ASSERT(m_interpreter.inode); + + for (const auto& epheader : m_executable.program_headers) + { + for (const auto& ipheader : m_interpreter.program_headers) + { + const vaddr_t e1 = epheader.p_vaddr & PAGE_ADDR_MASK; + const vaddr_t i1 = ipheader.p_vaddr & PAGE_ADDR_MASK; + const vaddr_t e2 = (epheader.p_vaddr + epheader.p_memsz + PAGE_SIZE - 1) & PAGE_ADDR_MASK; + const vaddr_t i2 = (ipheader.p_vaddr + ipheader.p_memsz + PAGE_SIZE - 1) & PAGE_ADDR_MASK; + if (e1 < i2 && i1 < e2) + return true; + } + } + + return false; + } + + BAN::ErrorOr LoadableELF::initialize(const Credentials& credentials, BAN::RefPtr inode) + { + auto executable_load_result = TRY(load_elf_file(credentials, inode)); + m_executable = executable_load_result.elf_file; + + if (m_executable.file_header.e_type == ET_DYN) + { + m_executable.dynamic_base = (Random::get_u32() & 0x7FFFF000) + 0x100000; + m_executable.file_header.e_entry += m_executable.dynamic_base; + for (auto& program_header : m_executable.program_headers) + program_header.p_vaddr += m_executable.dynamic_base; + } + + if (executable_load_result.interp) + { + auto interp_load_result = TRY(load_elf_file(credentials, executable_load_result.interp)); + m_interpreter = interp_load_result.elf_file; + + if (interp_load_result.interp) + { + dwarnln("Executable has specified interpreter for its interpreter"); + return BAN::Error::from_errno(EINVAL); + } + + if (m_interpreter.file_header.e_type == ET_DYN) + { + for (int attempt = 0; attempt < 100; attempt++) + { + const vaddr_t dynamic_base = (Random::get_u32() & 0x3FFFF000) + 0x40000000; + for (auto& program_header : m_interpreter.program_headers) + program_header.p_vaddr += dynamic_base; + if (does_executable_and_interpreter_overlap()) + { + for (auto& program_header : m_interpreter.program_headers) + program_header.p_vaddr -= dynamic_base; + continue; + } + m_interpreter.dynamic_base = dynamic_base; + m_interpreter.file_header.e_entry += dynamic_base; + break; + } + } + + const bool can_load_interpreter = (m_interpreter.file_header.e_type == ET_DYN) + ? (m_interpreter.dynamic_base != 0) + : !does_executable_and_interpreter_overlap(); + + if (!can_load_interpreter) + { + dwarnln("Could not find space to load interpreter"); + return BAN::Error::from_errno(EINVAL); } } @@ -148,149 +256,199 @@ namespace LibELF vaddr_t LoadableELF::entry_point() const { - return m_file_header.e_entry; + if (m_interpreter.inode) + return m_interpreter.file_header.e_entry; + return m_executable.file_header.e_entry; } bool LoadableELF::contains(vaddr_t address) const { - for (const auto& program_header : m_program_headers) - { - ASSERT(program_header.p_type == PT_LOAD); + for (const auto& program_header : m_executable.program_headers) + if (program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz) + return true; + for (const auto& program_header : m_interpreter.program_headers) if (program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz) return true; - } return false; } bool LoadableELF::is_address_space_free() const { - for (const auto& program_header : m_program_headers) - { - ASSERT(program_header.p_type == PT_LOAD); - const vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK; - const size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz); - if (!m_page_table.is_range_free(page_vaddr, pages * PAGE_SIZE)) - return false; - } + const auto are_program_headers_free = + [&](BAN::Span program_headers) -> bool + { + for (const auto& program_header : program_headers) + { + ASSERT(program_header.p_type == PT_LOAD); + const vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK; + const size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz); + if (!m_page_table.is_range_free(page_vaddr, pages * PAGE_SIZE)) + return false; + } + return true; + }; + if (!are_program_headers_free(m_executable.program_headers.span())) + return false; + if (!are_program_headers_free(m_interpreter.program_headers.span())) + return false; return true; } void LoadableELF::reserve_address_space() { - for (const auto& program_header : m_program_headers) - { - ASSERT(program_header.p_type == PT_LOAD); - const vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK; - const size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz); - if (!m_page_table.reserve_range(page_vaddr, pages * PAGE_SIZE)) - ASSERT_NOT_REACHED(); - } - m_loaded = true; + const auto reserve_program_headers = + [&](BAN::Span program_headers) + { + for (const auto& program_header : program_headers) + { + ASSERT(program_header.p_type == PT_LOAD); + const vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK; + const size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz); + if (!m_page_table.reserve_range(page_vaddr, pages * PAGE_SIZE)) + ASSERT_NOT_REACHED(); + m_virtual_page_count += pages; + } + }; + reserve_program_headers(m_executable.program_headers.span()); + reserve_program_headers(m_interpreter.program_headers.span()); + m_is_loaded = true; } void LoadableELF::update_suid_sgid(Kernel::Credentials& credentials) { - if (m_inode->mode().mode & +Inode::Mode::ISUID) - credentials.set_euid(m_inode->uid()); - if (m_inode->mode().mode & +Inode::Mode::ISGID) - credentials.set_egid(m_inode->gid()); + auto inode = m_executable.inode; + ASSERT(inode); + + if (inode->mode().mode & +Inode::Mode::ISUID) + credentials.set_euid(inode->uid()); + if (inode->mode().mode & +Inode::Mode::ISGID) + credentials.set_egid(inode->gid()); } BAN::ErrorOr LoadableELF::load_page_to_memory(vaddr_t address) { - for (const auto& program_header : m_program_headers) - { - ASSERT(program_header.p_type == PT_LOAD); - - if (!(program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz)) - continue; - - PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present; - if (program_header.p_flags & LibELF::PF_W) - flags |= PageTable::Flags::ReadWrite; - if (program_header.p_flags & LibELF::PF_X) - flags |= PageTable::Flags::Execute; - - const vaddr_t vaddr = address & PAGE_ADDR_MASK; - const paddr_t paddr = Heap::get().take_free_page(); - if (paddr == 0) - return BAN::Error::from_errno(ENOMEM); - - // Temporarily map page as RW so kernel can write to it - m_page_table.map_page_at(paddr, vaddr, PageTable::Flags::ReadWrite | PageTable::Flags::Present); - m_physical_page_count++; - - memset((void*)vaddr, 0x00, PAGE_SIZE); - - if (vaddr / PAGE_SIZE < BAN::Math::div_round_up(program_header.p_vaddr + program_header.p_filesz, PAGE_SIZE)) + const auto load_page_from_program_header = + [&](BAN::RefPtr inode, BAN::Span program_headers) -> BAN::ErrorOr { - size_t vaddr_offset = 0; - if (vaddr < program_header.p_vaddr) - vaddr_offset = program_header.p_vaddr - vaddr; + for (const auto& program_header : program_headers) + { + ASSERT(program_header.p_type == PT_LOAD); + if (!(program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz)) + continue; - size_t file_offset = 0; - if (vaddr > program_header.p_vaddr) - file_offset = vaddr - program_header.p_vaddr; + PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present; + if (program_header.p_flags & LibELF::PF_W) + flags |= PageTable::Flags::ReadWrite; + if (program_header.p_flags & LibELF::PF_X) + flags |= PageTable::Flags::Execute; - size_t bytes = BAN::Math::min(PAGE_SIZE - vaddr_offset, program_header.p_filesz - file_offset); - TRY(m_inode->read(program_header.p_offset + file_offset, { (uint8_t*)vaddr + vaddr_offset, bytes })); - } + const vaddr_t vaddr = address & PAGE_ADDR_MASK; + const paddr_t paddr = Heap::get().take_free_page(); + if (paddr == 0) + return BAN::Error::from_errno(ENOMEM); - // Map page with the correct flags - m_page_table.map_page_at(paddr, vaddr, flags); + // Temporarily map page as RW so kernel can write to it + m_page_table.map_page_at(paddr, vaddr, PageTable::Flags::ReadWrite | PageTable::Flags::Present); + m_physical_page_count++; + memset((void*)vaddr, 0x00, PAGE_SIZE); + + if (vaddr / PAGE_SIZE < BAN::Math::div_round_up(program_header.p_vaddr + program_header.p_filesz, PAGE_SIZE)) + { + size_t vaddr_offset = 0; + if (vaddr < program_header.p_vaddr) + vaddr_offset = program_header.p_vaddr - vaddr; + + size_t file_offset = 0; + if (vaddr > program_header.p_vaddr) + file_offset = vaddr - program_header.p_vaddr; + + size_t bytes = BAN::Math::min(PAGE_SIZE - vaddr_offset, program_header.p_filesz - file_offset); + TRY(inode->read(program_header.p_offset + file_offset, { (uint8_t*)vaddr + vaddr_offset, bytes })); + } + + // Map page with the correct flags + m_page_table.map_page_at(paddr, vaddr, flags); + + return true; + } + + return false; + }; + + if (TRY(load_page_from_program_header(m_executable.inode, m_executable.program_headers.span()))) + return {}; + if (TRY(load_page_from_program_header(m_interpreter.inode, m_interpreter.program_headers.span()))) return {}; - } ASSERT_NOT_REACHED(); } BAN::ErrorOr> LoadableELF::clone(Kernel::PageTable& new_page_table) { - auto* elf_ptr = new LoadableELF(new_page_table, m_inode); - if (elf_ptr == nullptr) - return BAN::Error::from_errno(ENOMEM); - auto elf = BAN::UniqPtr::adopt(elf_ptr); + auto elf = TRY(BAN::UniqPtr::create(new_page_table)); - memcpy(&elf->m_file_header, &m_file_header, sizeof(ElfNativeFileHeader)); + const auto clone_loadable_file = + [](const LoadableElfFile& source, LoadableElfFile& destination) -> BAN::ErrorOr + { + if (!source.inode) + return {}; - TRY(elf->m_program_headers.resize(m_program_headers.size())); - memcpy(elf->m_program_headers.data(), m_program_headers.data(), m_program_headers.size() * sizeof(ElfNativeProgramHeader)); + destination.inode = source.inode; + destination.file_header = source.file_header; + destination.dynamic_base = source.dynamic_base; + + TRY(destination.program_headers.reserve(source.program_headers.size())); + for (const auto& program_header : source.program_headers) + MUST(destination.program_headers.emplace_back(program_header)); + + return {}; + }; + + const auto map_loadable_file = + [&](BAN::Span program_headers) -> BAN::ErrorOr + { + for (const auto& program_header : program_headers) + { + ASSERT(program_header.p_type == PT_LOAD); + if (!(program_header.p_flags & LibELF::PF_W)) + continue; + + PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present; + if (program_header.p_flags & LibELF::PF_W) + flags |= PageTable::Flags::ReadWrite; + if (program_header.p_flags & LibELF::PF_X) + flags |= PageTable::Flags::Execute; + + vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK; + size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz); + + for (size_t i = 0; i < pages; i++) + { + if (m_page_table.physical_address_of(start + i * PAGE_SIZE) == 0) + continue; + + paddr_t paddr = Heap::get().take_free_page(); + if (paddr == 0) + return BAN::Error::from_errno(ENOMEM); + + PageTable::with_fast_page(paddr, [&] { + memcpy(PageTable::fast_page_as_ptr(), (void*)(start + i * PAGE_SIZE), PAGE_SIZE); + }); + + new_page_table.map_page_at(paddr, start + i * PAGE_SIZE, flags); + elf->m_physical_page_count++; + } + } + return {}; + }; + + TRY(clone_loadable_file(m_executable, elf->m_executable)); + TRY(clone_loadable_file(m_interpreter, elf->m_interpreter)); elf->reserve_address_space(); - for (const auto& program_header : m_program_headers) - { - ASSERT(program_header.p_type == PT_LOAD); - - if (!(program_header.p_flags & LibELF::PF_W)) - continue; - - PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present; - if (program_header.p_flags & LibELF::PF_W) - flags |= PageTable::Flags::ReadWrite; - if (program_header.p_flags & LibELF::PF_X) - flags |= PageTable::Flags::Execute; - - vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK; - size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz); - - for (size_t i = 0; i < pages; i++) - { - if (m_page_table.physical_address_of(start + i * PAGE_SIZE) == 0) - continue; - - paddr_t paddr = Heap::get().take_free_page(); - if (paddr == 0) - return BAN::Error::from_errno(ENOMEM); - - PageTable::with_fast_page(paddr, [&] { - memcpy(PageTable::fast_page_as_ptr(), (void*)(start + i * PAGE_SIZE), PAGE_SIZE); - }); - - new_page_table.map_page_at(paddr, start + i * PAGE_SIZE, flags); - elf->m_physical_page_count++; - } - } + TRY(map_loadable_file(elf->m_executable.program_headers.span())); + TRY(map_loadable_file(elf->m_interpreter.program_headers.span())); return elf; } diff --git a/userspace/libraries/LibELF/include/LibELF/LoadableELF.h b/userspace/libraries/LibELF/include/LibELF/LoadableELF.h index 3bdad48f..1db27f35 100644 --- a/userspace/libraries/LibELF/include/LibELF/LoadableELF.h +++ b/userspace/libraries/LibELF/include/LibELF/LoadableELF.h @@ -22,11 +22,14 @@ namespace LibELF BAN_NON_MOVABLE(LoadableELF); public: - static BAN::ErrorOr> load_from_inode(Kernel::PageTable&, BAN::RefPtr); + static BAN::ErrorOr> load_from_inode(Kernel::PageTable&, const Kernel::Credentials&, BAN::RefPtr); ~LoadableELF(); Kernel::vaddr_t entry_point() const; + bool has_interpreter() const { return !!m_interpreter.inode; } + BAN::RefPtr inode() { return m_executable.inode; } + bool contains(Kernel::vaddr_t address) const; bool is_address_space_free() const; void reserve_address_space(); @@ -41,17 +44,36 @@ namespace LibELF size_t physical_page_count() const { return m_physical_page_count; } private: - LoadableELF(Kernel::PageTable&, BAN::RefPtr); - BAN::ErrorOr initialize(); + struct LoadableElfFile + { + BAN::RefPtr inode; + ElfNativeFileHeader file_header; + BAN::Vector program_headers; + Kernel::vaddr_t dynamic_base; + }; + + struct LoadResult + { + LoadableElfFile elf_file; + BAN::RefPtr interp; + }; private: - BAN::RefPtr m_inode; - Kernel::PageTable& m_page_table; - ElfNativeFileHeader m_file_header; - BAN::Vector m_program_headers; - size_t m_virtual_page_count = 0; - size_t m_physical_page_count = 0; - bool m_loaded { false }; + LoadableELF(Kernel::PageTable&); + BAN::ErrorOr initialize(const Kernel::Credentials&, BAN::RefPtr); + + bool does_executable_and_interpreter_overlap() const; + BAN::ErrorOr load_elf_file(const Kernel::Credentials&, BAN::RefPtr) const; + + private: + LoadableElfFile m_executable; + LoadableElfFile m_interpreter; + Kernel::PageTable& m_page_table; + size_t m_virtual_page_count { 0 }; + size_t m_physical_page_count { 0 }; + bool m_is_loaded { false }; + + friend class BAN::UniqPtr; }; }