diff --git a/Libraries/LibELF/ELFImage.cpp b/Libraries/LibELF/ELFImage.cpp index ca4004f1ba..a7e7df1b77 100644 --- a/Libraries/LibELF/ELFImage.cpp +++ b/Libraries/LibELF/ELFImage.cpp @@ -64,6 +64,15 @@ void ELFImage::dump() const dbgprintf(" phnum: %u\n", header().e_phnum); dbgprintf(" shstrndx: %u\n", header().e_shstrndx); + for_each_program_header([&](const ProgramHeader& program_header) { + dbgprintf(" Program Header %d: {\n", program_header.index()); + dbgprintf(" type: %x\n", program_header.type()); + dbgprintf(" offset: %x\n", program_header.offset()); + dbgprintf(" flags: %x\n", program_header.flags()); + dbgprintf(" \n"); + dbgprintf(" }\n"); + }); + for (unsigned i = 0; i < header().e_shnum; ++i) { auto& section = this->section(i); dbgprintf(" Section %u: {\n", i); @@ -100,9 +109,8 @@ unsigned ELFImage::program_header_count() const bool ELFImage::parse() { - // We only support i386. - if (header().e_machine != 3) { - dbgprintf("ELFImage::parse(): e_machine=%u not supported!\n", header().e_machine); + if (!validate_elf_header(header(), m_size)) { + dbgputstr("ELFImage::parse(): ELF Header not valid\n"); return false; } @@ -214,3 +222,159 @@ const ELFImage::Section ELFImage::lookup_section(const String& name) const return section((*it).value); return section(0); } + +bool ELFImage::validate_elf_header(const Elf32_Ehdr& elf_header, size_t file_size) +{ + if (!IS_ELF(elf_header)) { + dbgputstr("File is not an ELF file.\n"); + return false; + } + + if (ELFCLASS32 != elf_header.e_ident[EI_CLASS]) { + dbgputstr("File is not a 32 bit ELF file.\n"); + return false; + } + + if (ELFDATA2LSB != elf_header.e_ident[EI_DATA]) { + dbgputstr("File is not a little endian ELF file.\n"); + return false; + } + + if (EV_CURRENT != elf_header.e_ident[EI_VERSION]) { + dbgprintf("File has unrecognized ELF version (%d), expected (%d)!\n", elf_header.e_ident[EI_VERSION], EV_CURRENT); + return false; + } + + if (ELFOSABI_SYSV != elf_header.e_ident[EI_OSABI]) { + dbgprintf("File has unknown OS ABI (%d), expected SYSV(0)!\n", elf_header.e_ident[EI_OSABI]); + return false; + } + + if (0 != elf_header.e_ident[EI_ABIVERSION]) { + dbgprintf("File has unknown SYSV ABI version (%d)!\n", elf_header.e_ident[EI_ABIVERSION]); + return false; + } + + if (EM_386 != elf_header.e_machine) { + dbgprintf("File has unknown machine (%d), expected i386 (3)!\n", elf_header.e_machine); + return false; + } + + if (ET_EXEC != elf_header.e_type && ET_DYN != elf_header.e_type && ET_REL != elf_header.e_type) { + dbgprintf("File has unloadable ELF type (%d), expected REL (1), EXEC (2) or DYN (3)!\n", elf_header.e_type); + return false; + } + + if (EV_CURRENT != elf_header.e_version) { + dbgprintf("File has unrecognized ELF version (%d), expected (%d)!\n", elf_header.e_version, EV_CURRENT); + return false; + } + + if (sizeof(Elf32_Ehdr) != elf_header.e_ehsize) { + dbgprintf("File has incorrect ELF header size..? (%d), expected (%d)!\n", elf_header.e_ehsize, sizeof(Elf32_Ehdr)); + return false; + } + + if (elf_header.e_phoff > file_size || elf_header.e_shoff > file_size) { + dbgprintf("SHENANIGANS! program header offset (%d) or section header offset (%d) are past the end of the file!\n", + elf_header.e_phoff, elf_header.e_shoff); + return false; + } + + if (elf_header.e_phnum != 0 && elf_header.e_phoff != elf_header.e_ehsize) { + dbgprintf("File does not have program headers directly after the ELF header? program header offset (%d), expected (%d).\n", + elf_header.e_phoff, elf_header.e_ehsize); + return false; + } + + if (0 != elf_header.e_flags) { + dbgprintf("File has incorrect ELF header flags...? (%d), expected (%d).\n", elf_header.e_flags, 0); + return false; + } + + if (0 != elf_header.e_phnum && sizeof(Elf32_Phdr) != elf_header.e_phentsize) { + dbgprintf("File has incorrect program header size..? (%d), expected (%d).\n", elf_header.e_phentsize, sizeof(Elf32_Phdr)); + return false; + } + + if (sizeof(Elf32_Shdr) != elf_header.e_shentsize) { + dbgprintf("File has incorrect section header size..? (%d), expected (%d).\n", elf_header.e_shentsize, sizeof(Elf32_Shdr)); + return false; + } + + size_t end_of_last_program_header = elf_header.e_phoff + (elf_header.e_phnum * elf_header.e_phentsize); + if (end_of_last_program_header > file_size) { + dbgprintf("SHENANIGANS! End of last program header (%d) is past the end of the file!\n", end_of_last_program_header); + return false; + } + + size_t end_of_last_section_header = elf_header.e_shoff + (elf_header.e_shnum * elf_header.e_shentsize); + if (end_of_last_section_header > file_size) { + dbgprintf("SHENANIGANS! End of last section header (%d) is past the end of the file!\n", end_of_last_section_header); + return false; + } + + if (elf_header.e_shstrndx >= elf_header.e_shnum) { + dbgprintf("SHENANIGANS! Section header string table index (%d) is not a valid index given we have %d section headers!\n", elf_header.e_shstrndx, elf_header.e_shnum); + return false; + } + + return true; +} + +bool ELFImage::validate_program_headers(const Elf32_Ehdr& elf_header, size_t file_size, u8* buffer, size_t buffer_size, String& interpreter_path) +{ + // Can we actually parse all the program headers in the given buffer? + size_t end_of_last_program_header = elf_header.e_phoff + (elf_header.e_phnum * elf_header.e_phentsize); + if (end_of_last_program_header > buffer_size) { + dbgprintf("Unable to parse program headers from buffer, buffer too small! Buffer size: %zu, End of program headers %zu\n", + buffer_size, end_of_last_program_header); + return false; + } + + if (file_size < buffer_size) { + dbgputstr("We somehow read more from a file than was in the file in the first place!\n"); + ASSERT_NOT_REACHED(); + } + + size_t num_program_headers = elf_header.e_phnum; + auto program_header_begin = (const Elf32_Phdr*)&(buffer[elf_header.e_phoff]); + + for (size_t header_index = 0; header_index < num_program_headers; ++header_index) { + auto& program_header = program_header_begin[header_index]; + switch (program_header.p_type) { + case PT_INTERP: + if (ET_DYN != elf_header.e_type) { + dbgprintf("Found PT_INTERP header (%d) in non-DYN ELF object! What? We can't handle this!\n", header_index); + return false; + } + // We checked above that file_size was >= buffer size. We only care about buffer size anyway, we're trying to read this! + if (program_header.p_offset + program_header.p_filesz > buffer_size) { + dbgprintf("Found PT_INTERP header (%d), but the .interp section was not within our buffer :( Your program will not be loaded today.\n", header_index); + return false; + } + interpreter_path = String((const char*)&buffer[program_header.p_offset], program_header.p_filesz - 1); + break; + case PT_LOAD: + case PT_DYNAMIC: + case PT_NOTE: + case PT_PHDR: + case PT_TLS: + if (program_header.p_offset + program_header.p_filesz > file_size) { + dbgprintf("SHENANIGANS! Program header %d segment leaks beyond end of file!\n", header_index); + return false; + } + if ((program_header.p_flags & PF_X) && (program_header.p_flags & PF_W)) { + dbgprintf("SHENANIGANS! Program header %d segment is marked write and execute\n", header_index); + return false; + } + break; + default: + // Not handling other program header types in other code so... let's not surprise them + dbgprintf("Found program header (%d) of unrecognized type %d!\n", header_index, program_header.p_type); + ASSERT_NOT_REACHED(); + break; + } + } + return true; +} diff --git a/Libraries/LibELF/ELFImage.h b/Libraries/LibELF/ELFImage.h index d4529d79ba..c03b8f9991 100644 --- a/Libraries/LibELF/ELFImage.h +++ b/Libraries/LibELF/ELFImage.h @@ -14,7 +14,7 @@ public: bool is_valid() const { return m_valid; } bool parse(); - bool is_within_image(const void* address, size_t size) + bool is_within_image(const void* address, size_t size) const { if (address < m_buffer) return false; @@ -174,6 +174,9 @@ public: VirtualAddress entry() const { return VirtualAddress(header().e_entry); } + static bool validate_elf_header(const Elf32_Ehdr& elf_header, size_t file_size); + static bool validate_program_headers(const Elf32_Ehdr& elf_header, size_t file_size, u8* buffer, size_t buffer_size, String& interpreter_path); + private: bool parse_header(); const char* raw_data(unsigned offset) const; diff --git a/Libraries/LibELF/ELFLoader.cpp b/Libraries/LibELF/ELFLoader.cpp index 95645f553f..3797a6875c 100644 --- a/Libraries/LibELF/ELFLoader.cpp +++ b/Libraries/LibELF/ELFLoader.cpp @@ -78,7 +78,16 @@ bool ELFLoader::layout() failed = true; return; } - do_memcpy(program_header.vaddr().as_ptr(), program_header.raw_data(), program_header.size_in_image()); + // It's not always the case with PIE executables (and very well shouldn't be) that the + // virtual address in the program header matches the one we end up giving the process. + // In order to copy the data image correctly into memory, we need to copy the data starting at + // the right initial page offset into the pages allocated for the elf_alloc-XX section. + // FIXME: There's an opportunity to munmap, or at least mprotect, the padding space between + // the .text and .data PT_LOAD sections of the executable. + // Accessing it would definitely be a bug. + auto page_offset = program_header.vaddr(); + page_offset.mask(~PAGE_MASK); + do_memcpy((u8*)allocated_section + page_offset.get(), program_header.raw_data(), program_header.size_in_image()); } else { auto* mapped_section = map_section_hook( program_header.vaddr(),