From 0629b4e170ecfa36da42d4224832b4a49857906f Mon Sep 17 00:00:00 2001 From: Brendan Coles Date: Sat, 15 May 2021 09:29:02 +0000 Subject: [PATCH] Tests: Add LibELF tests --- Tests/CMakeLists.txt | 1 + Tests/LibELF/CMakeLists.txt | 5 + Tests/LibELF/test-elf.cpp | 291 ++++++++++++++++++++++++++++++++++++ 3 files changed, 297 insertions(+) create mode 100644 Tests/LibELF/CMakeLists.txt create mode 100644 Tests/LibELF/test-elf.cpp diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 0d57a8dce4d..6de315454e8 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(Kernel) add_subdirectory(LibC) add_subdirectory(LibCore) add_subdirectory(LibCompress) +add_subdirectory(LibELF) add_subdirectory(LibGfx) add_subdirectory(LibJS) add_subdirectory(LibM) diff --git a/Tests/LibELF/CMakeLists.txt b/Tests/LibELF/CMakeLists.txt new file mode 100644 index 00000000000..c4bbaf4fa57 --- /dev/null +++ b/Tests/LibELF/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB CMD_SOURCES CONFIGURE_DEPENDS "*.cpp") + +foreach(CMD_SRC ${CMD_SOURCES}) + serenity_test(${CMD_SRC} LibELF) +endforeach() diff --git a/Tests/LibELF/test-elf.cpp b/Tests/LibELF/test-elf.cpp new file mode 100644 index 00000000000..cbfe8badd20 --- /dev/null +++ b/Tests/LibELF/test-elf.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2021, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +TEST_CASE(test_interp_header_tiny_p_filesz) +{ + char buffer[0x2000]; + + auto& header = *(Elf32_Ehdr*)buffer; + header.e_ident[EI_MAG0] = ELFMAG0; + header.e_ident[EI_MAG1] = ELFMAG1; + header.e_ident[EI_MAG2] = ELFMAG2; + header.e_ident[EI_MAG3] = ELFMAG3; + header.e_ident[EI_CLASS] = ELFCLASS32; + header.e_ident[EI_DATA] = ELFDATA2LSB; + header.e_ident[EI_VERSION] = EV_CURRENT; + header.e_ident[EI_OSABI] = ELFOSABI_SYSV; + header.e_ident[EI_ABIVERSION] = 0; + header.e_type = ET_REL; + header.e_version = EV_CURRENT; + header.e_ehsize = sizeof(Elf32_Ehdr); + header.e_machine = EM_386; + header.e_shentsize = sizeof(Elf32_Shdr); + header.e_phnum = 1; + header.e_phoff = 52; // inaccurate + header.e_phentsize = sizeof(Elf32_Phdr); // inaccurate + header.e_shnum = 3; // inaccurate + header.e_shoff = 1024; // inaccurate + header.e_shstrndx = 2; // inaccurate + header.e_entry = 1024; // inaccurate + + auto* ph = (Elf32_Phdr*)(&buffer[header.e_phoff]); + ph[0].p_flags = PF_R | PF_X; + ph[0].p_vaddr = 0x00d4; + ph[0].p_align = PAGE_SIZE; + ph[0].p_type = PT_INTERP; + ph[0].p_memsz = 0xffff0000; + ph[0].p_offset = 0x100; + + // p_filesz (1 or less) to trigger crash + ph[0].p_filesz = 1; + + char path[] = "/tmp/test-elf.XXXXXX"; + auto fd = mkstemp(path); + EXPECT_NE(fd, -1); + EXPECT_EQ(fchmod(fd, 0700), 0); + + int nwritten = write(fd, buffer, sizeof(buffer)); + EXPECT(nwritten); + + auto elf_path = Core::File::read_link(String::formatted("/proc/{}/fd/{}", getpid(), fd)); + EXPECT(elf_path.characters()); + + int rc = execl(elf_path.characters(), "test-elf", nullptr); + EXPECT_EQ(rc, -1); + EXPECT_EQ(errno, 8); + + EXPECT_EQ(unlink(path), 0); +} + +TEST_CASE(test_interp_header_p_filesz_larger_than_p_memsz) +{ + char buffer[0x2000]; + + auto& header = *(Elf32_Ehdr*)buffer; + header.e_ident[EI_MAG0] = ELFMAG0; + header.e_ident[EI_MAG1] = ELFMAG1; + header.e_ident[EI_MAG2] = ELFMAG2; + header.e_ident[EI_MAG3] = ELFMAG3; + header.e_ident[EI_CLASS] = ELFCLASS32; + header.e_ident[EI_DATA] = ELFDATA2LSB; + header.e_ident[EI_VERSION] = EV_CURRENT; + header.e_ident[EI_OSABI] = ELFOSABI_SYSV; + header.e_ident[EI_ABIVERSION] = 0; + header.e_type = ET_REL; + header.e_version = EV_CURRENT; + header.e_ehsize = sizeof(Elf32_Ehdr); + header.e_machine = EM_386; + header.e_shentsize = sizeof(Elf32_Shdr); + header.e_phnum = 1; + header.e_phoff = 52; // inaccurate + header.e_phentsize = sizeof(Elf32_Phdr); // inaccurate + header.e_shnum = 3; // inaccurate + header.e_shoff = 1024; // inaccurate + header.e_shstrndx = 2; // inaccurate + header.e_entry = 1024; // inaccurate + + auto* ph = (Elf32_Phdr*)(&buffer[header.e_phoff]); + ph[0].p_flags = PF_R | PF_X; + ph[0].p_vaddr = 0x00d4; + ph[0].p_align = PAGE_SIZE; + ph[0].p_type = PT_INTERP; + ph[0].p_memsz = 0xffff0000; + ph[0].p_offset = 0x1000; + ph[0].p_filesz = 0x1000; + + char path[] = "/tmp/test-elf.XXXXXX"; + auto fd = mkstemp(path); + EXPECT_NE(fd, -1); + EXPECT_EQ(fchmod(fd, 0700), 0); + + int nwritten = write(fd, buffer, sizeof(buffer)); + EXPECT(nwritten); + + auto elf_path = Core::File::read_link(String::formatted("/proc/{}/fd/{}", getpid(), fd)); + EXPECT(elf_path.characters()); + + int rc = execl(elf_path.characters(), "test-elf", nullptr); + EXPECT_EQ(rc, -1); + EXPECT_EQ(errno, 8); + + EXPECT_EQ(unlink(path), 0); +} + +TEST_CASE(test_interp_header_p_filesz_plus_p_offset_overflow_p_memsz) +{ + char buffer[0x2000]; + + auto& header = *(Elf32_Ehdr*)buffer; + header.e_ident[EI_MAG0] = ELFMAG0; + header.e_ident[EI_MAG1] = ELFMAG1; + header.e_ident[EI_MAG2] = ELFMAG2; + header.e_ident[EI_MAG3] = ELFMAG3; + header.e_ident[EI_CLASS] = ELFCLASS32; + header.e_ident[EI_DATA] = ELFDATA2LSB; + header.e_ident[EI_VERSION] = EV_CURRENT; + header.e_ident[EI_OSABI] = ELFOSABI_SYSV; + header.e_ident[EI_ABIVERSION] = 0; + header.e_type = ET_REL; + header.e_version = EV_CURRENT; + header.e_ehsize = sizeof(Elf32_Ehdr); + header.e_machine = EM_386; + header.e_shentsize = sizeof(Elf32_Shdr); + header.e_phoff = 52; // inaccurate + header.e_phentsize = sizeof(Elf32_Phdr); // inaccurate + header.e_shnum = 3; // inaccurate + header.e_shoff = 1024; // inaccurate + header.e_shstrndx = 2; // inaccurate + header.e_entry = 1024; // inaccurate + + auto* ph = (Elf32_Phdr*)(&buffer[header.e_phoff]); + ph[0].p_flags = PF_R | PF_X; + ph[0].p_vaddr = 0x00d4; + ph[0].p_align = PAGE_SIZE; + ph[0].p_type = PT_INTERP; + + // p_memsz must be of sufficient size to hold maxint - 0x1000 + ph[0].p_memsz = 0xfffff000; + + // p_offset + p_filesz must not exceed buffer size in order to pass buffer size check in ELF::validate_program_headers(). + // p_memsz + p_offset must be sufficiently large to overflow maxint. + ph[0].p_offset = 0x1234; + ph[0].p_filesz = -0x1000; + + char path[] = "/tmp/test-elf.XXXXXX"; + auto fd = mkstemp(path); + EXPECT_NE(fd, -1); + EXPECT_EQ(fchmod(fd, 0700), 0); + + int nwritten = write(fd, buffer, sizeof(buffer)); + EXPECT(nwritten); + + auto elf_path = Core::File::read_link(String::formatted("/proc/{}/fd/{}", getpid(), fd)); + EXPECT(elf_path.characters()); + + int rc = execl(elf_path.characters(), "test-elf", nullptr); + EXPECT_EQ(rc, -1); + EXPECT_EQ(errno, 8); + + EXPECT_EQ(unlink(path), 0); +} + +TEST_CASE(test_load_header_p_memsz_zero) +{ + char buffer[0x2000]; + + auto& header = *(Elf32_Ehdr*)buffer; + header.e_ident[EI_MAG0] = ELFMAG0; + header.e_ident[EI_MAG1] = ELFMAG1; + header.e_ident[EI_MAG2] = ELFMAG2; + header.e_ident[EI_MAG3] = ELFMAG3; + header.e_ident[EI_CLASS] = ELFCLASS32; + header.e_ident[EI_DATA] = ELFDATA2LSB; + header.e_ident[EI_VERSION] = EV_CURRENT; + header.e_ident[EI_OSABI] = ELFOSABI_SYSV; + header.e_ident[EI_ABIVERSION] = 0; + header.e_type = ET_REL; + header.e_version = EV_CURRENT; + header.e_ehsize = sizeof(Elf32_Ehdr); + header.e_machine = EM_386; + header.e_shentsize = sizeof(Elf32_Shdr); + header.e_phoff = 52; // inaccurate + header.e_phentsize = sizeof(Elf32_Phdr); // inaccurate + header.e_shnum = 3; // inaccurate + header.e_shoff = 1024; // inaccurate + header.e_shstrndx = 2; // inaccurate + header.e_entry = 1024; // inaccurate + + auto* ph = (Elf32_Phdr*)(&buffer[header.e_phoff]); + ph[0].p_flags = PF_R | PF_X; + ph[0].p_vaddr = 0x00d4; + ph[0].p_align = PAGE_SIZE; + ph[0].p_type = PT_LOAD; + ph[0].p_offset = 0; + ph[0].p_filesz = 0; + + // p_memsz zero to trigger crash + ph[0].p_memsz = 0; + + char path[] = "/tmp/test-elf.XXXXXX"; + auto fd = mkstemp(path); + EXPECT_NE(fd, -1); + EXPECT_EQ(fchmod(fd, 0700), 0); + + int nwritten = write(fd, buffer, sizeof(buffer)); + EXPECT(nwritten); + + auto elf_path = Core::File::read_link(String::formatted("/proc/{}/fd/{}", getpid(), fd)); + EXPECT(elf_path.characters()); + + int rc = execl(elf_path.characters(), "test-elf", nullptr); + EXPECT_EQ(rc, -1); + EXPECT_EQ(errno, 8); + + EXPECT_EQ(unlink(path), 0); +} + +TEST_CASE(test_load_header_p_memsz_not_equal_to_p_align) +{ + char buffer[0x2000]; + + auto& header = *(Elf32_Ehdr*)buffer; + header.e_ident[EI_MAG0] = ELFMAG0; + header.e_ident[EI_MAG1] = ELFMAG1; + header.e_ident[EI_MAG2] = ELFMAG2; + header.e_ident[EI_MAG3] = ELFMAG3; + header.e_ident[EI_CLASS] = ELFCLASS32; + header.e_ident[EI_DATA] = ELFDATA2LSB; + header.e_ident[EI_VERSION] = EV_CURRENT; + header.e_ident[EI_OSABI] = ELFOSABI_SYSV; + header.e_ident[EI_ABIVERSION] = 0; + header.e_type = ET_REL; + header.e_version = EV_CURRENT; + header.e_ehsize = sizeof(Elf32_Ehdr); + header.e_machine = EM_386; + header.e_shentsize = sizeof(Elf32_Shdr); + header.e_phoff = 52; // inaccurate + header.e_phentsize = sizeof(Elf32_Phdr); // inaccurate + header.e_shnum = 3; // inaccurate + header.e_shoff = 1024; // inaccurate + header.e_shstrndx = 2; // inaccurate + header.e_entry = 1024; // inaccurate + + auto* ph = (Elf32_Phdr*)(&buffer[header.e_phoff]); + ph[0].p_flags = PF_R | PF_X; + ph[0].p_vaddr = 0x00d4; + ph[0].p_type = PT_LOAD; + ph[0].p_memsz = 0xffff0000; + ph[0].p_offset = 0x1000; + ph[0].p_filesz = 0x1000; + + // p_align not equal to PAGE_SIZE to trigger crash + ph[0].p_align = PAGE_SIZE / 2; + + char path[] = "/tmp/test-elf.XXXXXX"; + auto fd = mkstemp(path); + EXPECT_NE(fd, -1); + EXPECT_EQ(fchmod(fd, 0700), 0); + + int nwritten = write(fd, buffer, sizeof(buffer)); + EXPECT(nwritten); + + auto elf_path = Core::File::read_link(String::formatted("/proc/{}/fd/{}", getpid(), fd)); + EXPECT(elf_path.characters()); + + int rc = execl(elf_path.characters(), "test-elf", nullptr); + EXPECT_EQ(rc, -1); + EXPECT_EQ(errno, 8); + + EXPECT_EQ(unlink(path), 0); +}