diff --git a/Libraries/LibELF/Image.cpp b/Libraries/LibELF/Image.cpp index 9e401a1942..714ae98cee 100644 --- a/Libraries/LibELF/Image.cpp +++ b/Libraries/LibELF/Image.cpp @@ -32,11 +32,12 @@ namespace ELF { -Image::Image(const u8* buffer, size_t size) +Image::Image(const u8* buffer, size_t size, bool verbose_logging) : m_buffer(buffer) , m_size(size) + , m_verbose_logging(verbose_logging) { - m_valid = parse(); + parse(); } Image::~Image() @@ -63,6 +64,7 @@ static const char* object_file_type_to_string(Elf32_Half type) StringView Image::section_index_to_string(unsigned index) const { + ASSERT(m_valid); if (index == SHN_UNDEF) return "Undefined"; if (index >= SHN_LORESERVE) @@ -72,6 +74,7 @@ StringView Image::section_index_to_string(unsigned index) const unsigned Image::symbol_count() const { + ASSERT(m_valid); return section(m_symbol_table_section_index).entry_count(); } @@ -130,26 +133,32 @@ void Image::dump() const unsigned Image::section_count() const { + ASSERT(m_valid); return header().e_shnum; } unsigned Image::program_header_count() const { + ASSERT(m_valid); return header().e_phnum; } bool Image::parse() { - if (!validate_elf_header(header(), m_size)) { - dbgputstr("Image::parse(): ELF Header not valid\n"); - return false; + if (m_size < sizeof(Elf32_Ehdr) || !validate_elf_header(header(), m_size, m_verbose_logging)) { + if (m_verbose_logging) + dbgputstr("Image::parse(): ELF Header not valid\n"); + return m_valid = false; } + m_valid = true; + // First locate the string tables. for (unsigned i = 0; i < section_count(); ++i) { auto& sh = section_header(i); if (sh.sh_type == SHT_SYMTAB) { - ASSERT(!m_symbol_table_section_index || m_symbol_table_section_index == i); + if (m_symbol_table_section_index && m_symbol_table_section_index != i) + return m_valid = false; m_symbol_table_section_index = i; } if (sh.sh_type == SHT_STRTAB && i != header().e_shstrndx) { @@ -164,17 +173,19 @@ bool Image::parse() m_sections.set(section.name(), move(i)); } - return true; + return m_valid; } StringView Image::table_string(unsigned table_index, unsigned offset) const { + ASSERT(m_valid); auto& sh = section_header(table_index); if (sh.sh_type != SHT_STRTAB) return nullptr; size_t computed_offset = sh.sh_offset + offset; if (computed_offset >= m_size) { - dbgprintf("SHENANIGANS! Image::table_string() computed offset outside image.\n"); + if (m_verbose_logging) + dbgprintf("SHENANIGANS! Image::table_string() computed offset outside image.\n"); return {}; } size_t max_length = m_size - computed_offset; @@ -184,38 +195,45 @@ StringView Image::table_string(unsigned table_index, unsigned offset) const StringView Image::section_header_table_string(unsigned offset) const { + ASSERT(m_valid); return table_string(header().e_shstrndx, offset); } StringView Image::table_string(unsigned offset) const { + ASSERT(m_valid); return table_string(m_string_table_section_index, offset); } const char* Image::raw_data(unsigned offset) const { + ASSERT(offset < m_size); // Callers must check indices into raw_data()'s result are also in bounds. return reinterpret_cast(m_buffer) + offset; } const Elf32_Ehdr& Image::header() const { + ASSERT(m_size >= sizeof(Elf32_Ehdr)); return *reinterpret_cast(raw_data(0)); } const Elf32_Phdr& Image::program_header_internal(unsigned index) const { + ASSERT(m_valid); ASSERT(index < header().e_phnum); return *reinterpret_cast(raw_data(header().e_phoff + (index * sizeof(Elf32_Phdr)))); } const Elf32_Shdr& Image::section_header(unsigned index) const { + ASSERT(m_valid); ASSERT(index < header().e_shnum); return *reinterpret_cast(raw_data(header().e_shoff + (index * header().e_shentsize))); } const Image::Symbol Image::symbol(unsigned index) const { + ASSERT(m_valid); ASSERT(index < symbol_count()); auto* raw_syms = reinterpret_cast(raw_data(section(m_symbol_table_section_index).offset())); return Symbol(*this, index, raw_syms[index]); @@ -223,12 +241,14 @@ const Image::Symbol Image::symbol(unsigned index) const const Image::Section Image::section(unsigned index) const { + ASSERT(m_valid); ASSERT(index < section_count()); return Section(*this, index); } const Image::ProgramHeader Image::program_header(unsigned index) const { + ASSERT(m_valid); ASSERT(index < program_header_count()); return ProgramHeader(*this, index); } @@ -258,6 +278,7 @@ const Image::RelocationSection Image::Section::relocations() const const Image::Section Image::lookup_section(const String& name) const { + ASSERT(m_valid); if (auto it = m_sections.find(name); it != m_sections.end()) return section((*it).value); return section(0); diff --git a/Libraries/LibELF/Image.h b/Libraries/LibELF/Image.h index 3f43377924..5f7540a3a6 100644 --- a/Libraries/LibELF/Image.h +++ b/Libraries/LibELF/Image.h @@ -37,7 +37,7 @@ namespace ELF { class Image { public: - explicit Image(const u8*, size_t); + explicit Image(const u8*, size_t, bool verbose_logging = true); ~Image(); void dump() const; bool is_valid() const { return m_valid; } @@ -206,7 +206,6 @@ public: VirtualAddress entry() const { return VirtualAddress(header().e_entry); } private: - bool parse_header(); const char* raw_data(unsigned offset) const; const Elf32_Ehdr& header() const; const Elf32_Shdr& section_header(unsigned) const; @@ -218,6 +217,7 @@ private: const u8* m_buffer { nullptr }; size_t m_size { 0 }; + bool m_verbose_logging { true }; HashMap m_sections; bool m_valid { false }; unsigned m_symbol_table_section_index { 0 }; diff --git a/Libraries/LibELF/Loader.cpp b/Libraries/LibELF/Loader.cpp index ba3be75fd3..e98594510c 100644 --- a/Libraries/LibELF/Loader.cpp +++ b/Libraries/LibELF/Loader.cpp @@ -40,10 +40,9 @@ namespace ELF { -Loader::Loader(const u8* buffer, size_t size) - : m_image(buffer, size) +Loader::Loader(const u8* buffer, size_t size, bool verbose_logging) + : m_image(buffer, size, verbose_logging) { - m_symbol_count = m_image.symbol_count(); } Loader::~Loader() @@ -58,6 +57,8 @@ bool Loader::load() if (!m_image.is_valid()) return false; + m_symbol_count = m_image.symbol_count(); + if (!layout()) return false; diff --git a/Libraries/LibELF/Loader.h b/Libraries/LibELF/Loader.h index 9c0f16567d..a349800515 100644 --- a/Libraries/LibELF/Loader.h +++ b/Libraries/LibELF/Loader.h @@ -45,7 +45,7 @@ namespace ELF { class Loader : public RefCounted { public: - static NonnullRefPtr create(const u8* data, size_t size) { return adopt(*new Loader(data, size)); } + static NonnullRefPtr create(const u8* data, size_t size, bool verbose_logging=true) { return adopt(*new Loader(data, size, verbose_logging)); } ~Loader(); bool load(); @@ -68,7 +68,7 @@ public: Optional find_symbol(u32 address, u32* offset = nullptr) const; private: - explicit Loader(const u8*, size_t); + explicit Loader(const u8*, size_t, bool verbose_logging); bool layout(); bool perform_relocations(); diff --git a/Libraries/LibELF/Validation.cpp b/Libraries/LibELF/Validation.cpp index f1b9533187..8445acf223 100644 --- a/Libraries/LibELF/Validation.cpp +++ b/Libraries/LibELF/Validation.cpp @@ -31,99 +31,119 @@ namespace ELF { -bool validate_elf_header(const Elf32_Ehdr& elf_header, size_t file_size) +bool validate_elf_header(const Elf32_Ehdr& elf_header, size_t file_size, bool verbose) { if (!IS_ELF(elf_header)) { - dbgputstr("File is not an ELF file.\n"); + if (verbose) + 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"); + if (verbose) + 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"); + if (verbose) + 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); + if (verbose) + 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]); + if (verbose) + 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]); + if (verbose) + 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); + if (verbose) + 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); + if (verbose) + 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); + if (verbose) + 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 (%zu)!\n", elf_header.e_ehsize, sizeof(Elf32_Ehdr)); + if (verbose) + dbgprintf("File has incorrect ELF header size..? (%d), expected (%zu)!\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); + if (verbose) { + 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); + if (verbose) { + 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); + if (verbose) + 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 (%zu).\n", elf_header.e_phentsize, sizeof(Elf32_Phdr)); + if (verbose) + dbgprintf("File has incorrect program header size..? (%d), expected (%zu).\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 (%zu).\n", elf_header.e_shentsize, sizeof(Elf32_Shdr)); + if (verbose) + dbgprintf("File has incorrect section header size..? (%d), expected (%zu).\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 (%zu) is past the end of the file!\n", end_of_last_program_header); + if (verbose) + dbgprintf("SHENANIGANS! End of last program header (%zu) 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 (%zu) is past the end of the file!\n", end_of_last_section_header); + if (verbose) + dbgprintf("SHENANIGANS! End of last section header (%zu) 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); + if (verbose) + 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; } diff --git a/Libraries/LibELF/Validation.h b/Libraries/LibELF/Validation.h index 7e3af66ac0..85495ce09a 100644 --- a/Libraries/LibELF/Validation.h +++ b/Libraries/LibELF/Validation.h @@ -30,7 +30,7 @@ namespace ELF { -bool validate_elf_header(const Elf32_Ehdr& elf_header, size_t file_size); +bool validate_elf_header(const Elf32_Ehdr& elf_header, size_t file_size, bool verbose=true); bool validate_program_headers(const Elf32_Ehdr& elf_header, size_t file_size, u8* buffer, size_t buffer_size, String& interpreter_path); } // end namespace ELF diff --git a/Meta/Lagom/Fuzzers/CMakeLists.txt b/Meta/Lagom/Fuzzers/CMakeLists.txt index 0d4ffea220..cec56eccb6 100644 --- a/Meta/Lagom/Fuzzers/CMakeLists.txt +++ b/Meta/Lagom/Fuzzers/CMakeLists.txt @@ -1,8 +1,16 @@ +add_executable(FuzzELF FuzzELF.cpp) +target_compile_options(FuzzELF + PRIVATE $<$:-g -O1 -fsanitize=fuzzer> + ) +target_link_libraries(FuzzELF + PUBLIC Lagom + PRIVATE $<$:-fsanitize=fuzzer> + ) + add_executable(FuzzJs FuzzJs.cpp) target_compile_options(FuzzJs PRIVATE $<$:-g -O1 -fsanitize=fuzzer> ) - target_link_libraries(FuzzJs PUBLIC Lagom PRIVATE $<$:-fsanitize=fuzzer> @@ -12,7 +20,6 @@ add_executable(FuzzMarkdown FuzzMarkdown.cpp) target_compile_options(FuzzMarkdown PRIVATE $<$:-g -O1 -fsanitize=fuzzer> ) - target_link_libraries(FuzzMarkdown PUBLIC Lagom PRIVATE $<$:-fsanitize=fuzzer> diff --git a/Meta/Lagom/Fuzzers/FuzzELF.cpp b/Meta/Lagom/Fuzzers/FuzzELF.cpp new file mode 100644 index 0000000000..0717eea45f --- /dev/null +++ b/Meta/Lagom/Fuzzers/FuzzELF.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + ELF::Loader::create(data, size, /*verbose_logging=*/false); + return 0; +}