| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <lib/elfldltl/constants.h> |
| #include <lib/elfldltl/diagnostics.h> |
| #include <lib/elfldltl/memory.h> |
| #include <lib/elfldltl/phdr.h> |
| #include <lib/elfldltl/testing/typed-test.h> |
| #include <lib/stdcompat/span.h> |
| |
| #include <optional> |
| #include <string> |
| #include <vector> |
| |
| #include <gtest/gtest.h> |
| |
| namespace { |
| |
| using elfldltl::ElfPhdrType; |
| |
| // Diagnostic flags for signaling as much information as possible. |
| constexpr elfldltl::DiagnosticsFlags kFlags = { |
| .multiple_errors = true, |
| .warnings_are_errors = false, |
| .extra_checking = true, |
| }; |
| |
| constexpr size_t kAlign = 0x1000; // Example alignment. |
| constexpr size_t kPageSize = 0x1000; |
| |
| constexpr std::string_view kNullWarning = "PT_NULL header encountered"; |
| |
| template <class Phdr> |
| constexpr auto kRWX = Phdr::kRead | Phdr::kWrite | Phdr::kExecute; |
| |
| template <class Phdr> |
| constexpr Phdr OnePageStack(uint32_t flags) { |
| Phdr phdr{.type = ElfPhdrType::kStack, .memsz = 0x1000}; |
| // Not inlined, as the relative ordering between `flags` and `memsz` is not |
| // fixed. |
| phdr.flags = flags; |
| return phdr; |
| } |
| |
| FORMAT_TYPED_TEST_SUITE(ElfldltlPhdrTests); |
| |
| TYPED_TEST(ElfldltlPhdrTests, Empty) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| // No matchers and nothing to match. |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span<const Phdr>{})); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| } |
| |
| // No PT_NULL headers. |
| TYPED_TEST(ElfldltlPhdrTests, NullObserverNoNulls) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = {{.type = ElfPhdrType::kLoad}}; |
| |
| std::vector<std::string> warnings; |
| auto diag = elfldltl::CollectStringsDiagnostics(warnings, kFlags); |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), elfldltl::PhdrNullObserver<Elf>())); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| } |
| |
| // One PT_NULL header. |
| TYPED_TEST(ElfldltlPhdrTests, NullObserverOneNull) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = { |
| {.type = ElfPhdrType::kLoad}, |
| {.type = ElfPhdrType::kNull}, |
| {.type = ElfPhdrType::kLoad}, |
| }; |
| |
| std::vector<std::string> warnings; |
| auto diag = elfldltl::CollectStringsDiagnostics(warnings, kFlags); |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), elfldltl::PhdrNullObserver<Elf>())); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(1u, diag.warnings()); |
| ASSERT_EQ(1u, warnings.size()); |
| EXPECT_EQ(kNullWarning, warnings[0]); |
| } |
| |
| // Three PT_NULL headers. |
| TYPED_TEST(ElfldltlPhdrTests, NullObserverThreeNulls) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = { |
| {.type = ElfPhdrType::kNull}, {.type = ElfPhdrType::kNull}, {.type = ElfPhdrType::kLoad}, |
| {.type = ElfPhdrType::kNull}, {.type = ElfPhdrType::kLoad}, |
| }; |
| |
| std::vector<std::string> warnings; |
| auto diag = elfldltl::CollectStringsDiagnostics(warnings, kFlags); |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), elfldltl::PhdrNullObserver<Elf>())); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(3u, diag.warnings()); |
| ASSERT_EQ(3u, warnings.size()); |
| EXPECT_EQ(kNullWarning, warnings[0]); |
| EXPECT_EQ(kNullWarning, warnings[1]); |
| EXPECT_EQ(kNullWarning, warnings[2]); |
| } |
| |
| // At most one header per type. |
| TYPED_TEST(ElfldltlPhdrTests, SingletonObserverAtMostOneHeaderPerType) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = { |
| {.type = ElfPhdrType::kInterp}, |
| {.type = ElfPhdrType::kEhFrameHdr}, |
| {.type = ElfPhdrType::kRelro}, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors); |
| std::optional<Phdr> dynamic, interp, eh_frame, relro; |
| EXPECT_TRUE(elfldltl::DecodePhdrs( |
| diag, cpp20::span(kPhdrs), // |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kDynamic>(dynamic), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kInterp>(interp), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kEhFrameHdr>(eh_frame), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kRelro>(relro))); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| |
| EXPECT_FALSE(dynamic); |
| |
| ASSERT_TRUE(interp); |
| EXPECT_EQ(ElfPhdrType::kInterp, interp->type); |
| |
| ASSERT_TRUE(eh_frame); |
| EXPECT_EQ(ElfPhdrType::kEhFrameHdr, eh_frame->type); |
| |
| ASSERT_TRUE(relro); |
| EXPECT_EQ(ElfPhdrType::kRelro, relro->type); |
| } |
| |
| // Multiple headers per type. |
| TYPED_TEST(ElfldltlPhdrTests, SingletonObserverMultipleHeadersPerType) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = { |
| {.type = ElfPhdrType::kInterp}, {.type = ElfPhdrType::kEhFrameHdr}, |
| {.type = ElfPhdrType::kRelro}, {.type = ElfPhdrType::kRelro}, |
| {.type = ElfPhdrType::kInterp}, |
| }; |
| |
| std::vector<std::string> warnings; |
| auto diag = elfldltl::CollectStringsDiagnostics(warnings, kFlags); |
| std::optional<Phdr> interp, eh_frame, relro; |
| EXPECT_TRUE(elfldltl::DecodePhdrs( |
| diag, cpp20::span(kPhdrs), // |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kInterp>(interp), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kEhFrameHdr>(eh_frame), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kRelro>(relro))); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(2u, diag.warnings()); |
| |
| ASSERT_EQ(warnings.size(), 2u); |
| EXPECT_EQ(warnings[0], "too many PT_GNU_RELRO headers; expected at most one"); |
| EXPECT_EQ(warnings[1], "too many PT_INTERP headers; expected at most one"); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, UnknownFlags) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = { |
| {.type = ElfPhdrType::kLoad, .flags = kRWX<Phdr>}, |
| {.type = ElfPhdrType::kDynamic, .flags = ~Phdr::kRead}, |
| {.type = ElfPhdrType::kInterp, .flags = ~Phdr::kWrite}, |
| {.type = ElfPhdrType::kStack, .flags = ~Phdr::kExecute}, |
| {.type = ElfPhdrType::kRelro, .flags = ~kRWX<Phdr>}, |
| }; |
| |
| std::vector<std::string> warnings; |
| auto diag = elfldltl::CollectStringsDiagnostics(warnings, kFlags); |
| std::optional<Phdr> load, dynamic, interp, stack, relro; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), // |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kLoad>(load), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kDynamic>(dynamic), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kInterp>(interp), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kStack>(stack), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kRelro>(relro))); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(4u, diag.warnings()); |
| |
| ASSERT_EQ(warnings.size(), 4u); |
| EXPECT_EQ(warnings[0], "PT_DYNAMIC header has unrecognized flags (other than PF_R, PF_W, PF_X)"); |
| EXPECT_EQ(warnings[1], "PT_INTERP header has unrecognized flags (other than PF_R, PF_W, PF_X)"); |
| EXPECT_EQ(warnings[2], |
| "PT_GNU_STACK header has unrecognized flags (other than PF_R, PF_W, PF_X)"); |
| EXPECT_EQ(warnings[3], |
| "PT_GNU_RELRO header has unrecognized flags (other than PF_R, PF_W, PF_X)"); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, BadAlignment) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = { |
| {.type = ElfPhdrType::kLoad, .align = 0}, // OK |
| {.type = ElfPhdrType::kDynamic, .align = kAlign}, // OK |
| {.type = ElfPhdrType::kInterp, .align = 3}, |
| {.type = ElfPhdrType::kNote, .align = kAlign - 1}, |
| {.type = ElfPhdrType::kRelro, .align = kAlign + 1}, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| std::optional<Phdr> load, dynamic, interp, note, relro; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), // |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kLoad>(load), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kDynamic>(dynamic), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kInterp>(interp), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kNote>(note), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kRelro>(relro))); |
| |
| EXPECT_EQ(3u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| |
| ASSERT_EQ(errors.size(), 3u); |
| EXPECT_EQ(errors[0], "PT_INTERP header has `p_align` that is not zero or a power of two"); |
| EXPECT_EQ(errors[1], "PT_NOTE header has `p_align` that is not zero or a power of two"); |
| EXPECT_EQ(errors[2], "PT_GNU_RELRO header has `p_align` that is not zero or a power of two"); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, OffsetNotEquivVaddr) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = { |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = kAlign, |
| .vaddr = kAlign, |
| .align = kAlign, |
| }, |
| { |
| .type = ElfPhdrType::kDynamic, |
| .offset = 17 * kAlign, |
| .vaddr = kAlign, |
| .align = kAlign, |
| }, |
| { |
| .type = ElfPhdrType::kInterp, |
| .offset = 100, |
| .vaddr = 101, |
| .align = 0, |
| }, |
| { |
| .type = ElfPhdrType::kNote, |
| .offset = kAlign - 1, |
| .vaddr = kAlign, |
| .align = kAlign, |
| }, |
| { |
| .type = ElfPhdrType::kRelro, |
| .offset = kAlign + 1, |
| .vaddr = kAlign, |
| .align = kAlign, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| std::optional<Phdr> load, dynamic, interp, note, relro; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), // |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kLoad>(load), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kDynamic>(dynamic), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kInterp>(interp), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kNote>(note), |
| elfldltl::PhdrSingletonObserver<Elf, ElfPhdrType::kRelro>(relro))); |
| |
| EXPECT_EQ(2u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| |
| ASSERT_EQ(2u, errors.size()); |
| EXPECT_EQ(errors[0], "PT_NOTE header has incongruent `p_offset` and `p_vaddr` modulo `p_align`"); |
| EXPECT_EQ(errors[1], |
| "PT_GNU_RELRO header has incongruent `p_offset` and `p_vaddr` modulo `p_align`"); |
| } |
| |
| // Executable stack permitted; non-zero memsz. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverExecOkPhdrNonzeroSize) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = {OnePageStack<Phdr>(Phdr::kRead | Phdr::kWrite)}; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| bool executable = false; |
| EXPECT_TRUE(elfldltl::DecodePhdrs( |
| diag, cpp20::span(kPhdrs), |
| elfldltl::PhdrStackObserver<Elf, /*CanBeExecutable=*/true>(size, executable))); |
| |
| ASSERT_TRUE(size); |
| EXPECT_EQ(0x1000u, *size); |
| } |
| |
| // Executable stack permitted; zero memsz. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverExecOkPhdrZeroSize) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = {{.type = ElfPhdrType::kStack, .flags = Phdr::kRead | Phdr::kWrite}}; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| bool executable = false; |
| EXPECT_TRUE(elfldltl::DecodePhdrs( |
| diag, cpp20::span(kPhdrs), |
| elfldltl::PhdrStackObserver<Elf, /*CanBeExecutable=*/true>(size, executable))); |
| |
| ASSERT_FALSE(size); |
| } |
| |
| // Executable stack permitted; no header to report size. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverExecOkNoPhdrSize) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| bool executable = false; |
| EXPECT_TRUE(elfldltl::DecodePhdrs( |
| diag, cpp20::span<const Phdr>{}, |
| elfldltl::PhdrStackObserver<Elf, /*CanBeExecutable=*/true>(size, executable))); |
| |
| ASSERT_FALSE(size); |
| } |
| |
| // Executable stack permitted; header present and reports PF_X. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverExecOkPhdrWithX) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = {OnePageStack<Phdr>(kRWX<Phdr>)}; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| bool executable = false; |
| EXPECT_TRUE(elfldltl::DecodePhdrs( |
| diag, cpp20::span(kPhdrs), |
| elfldltl::PhdrStackObserver<Elf, /*CanBeExecutable=*/true>(size, executable))); |
| |
| ASSERT_TRUE(size); |
| EXPECT_EQ(0x1000u, *size); |
| EXPECT_TRUE(executable); |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| } |
| |
| // Executable stack permitted; header present and does not report PF_X. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverExecOkPhdrWithoutX) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = {OnePageStack<Phdr>(Phdr::kRead | Phdr::kWrite)}; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| bool executable = false; |
| EXPECT_TRUE(elfldltl::DecodePhdrs( |
| diag, cpp20::span(kPhdrs), |
| elfldltl::PhdrStackObserver<Elf, /*CanBeExecutable=*/true>(size, executable))); |
| |
| EXPECT_FALSE(executable); |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| } |
| |
| // Executable stack permitted; header not present. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverExecOkNoPhdr) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| bool executable = false; |
| EXPECT_TRUE(elfldltl::DecodePhdrs( |
| diag, cpp20::span<const Phdr>{}, |
| elfldltl::PhdrStackObserver<Elf, /*CanBeExecutable=*/true>(size, executable))); |
| |
| EXPECT_TRUE(executable); |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| } |
| |
| // Executable stack not permitted; non-zero memsz. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverExecNotOkPhdrNonzeroSize) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = {OnePageStack<Phdr>(Phdr::kRead | Phdr::kWrite)}; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| elfldltl::PhdrStackObserver<Elf, /*CanBeExecutable=*/false>(size))); |
| |
| ASSERT_TRUE(size); |
| EXPECT_EQ(0x1000u, *size); |
| } |
| |
| // Executable stack not permitted; zero memsz. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverExecNotOkPhdrZeroSize) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = {{.type = ElfPhdrType::kStack, .flags = Phdr::kRead | Phdr::kWrite}}; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| elfldltl::PhdrStackObserver<Elf, /*CanBeExecutable=*/false>(size))); |
| |
| ASSERT_FALSE(size); |
| } |
| |
| // Executable stack not permitted; no header to report size. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverExecNotOkNoPhdrSize) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span<const Phdr>{}, |
| elfldltl::PhdrStackObserver<Elf, /*CanBeExecutable=*/false>(size))); |
| |
| ASSERT_FALSE(size); |
| } |
| |
| // Executable stack not permitted; header present and reports PF_X. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverExecNotOkPhdrWithX) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = {OnePageStack<Phdr>(kRWX<Phdr>)}; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| elfldltl::PhdrStackObserver<Elf, /*CanBeExecutable=*/false>(size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(errors.size(), 1u); |
| EXPECT_EQ(errors.front(), "executable stack not supported: PF_X is set"); |
| } |
| |
| // Executable stack not permitted; header present and does not report PF_X. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverExecNotOkPhdrWithoutX) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = {OnePageStack<Phdr>(Phdr::kRead | Phdr::kWrite)}; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| elfldltl::PhdrStackObserver<Elf, /*CanBeExecutable=*/false>(size))); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| } |
| |
| // Executable stack not permitted; header not present. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverExecNotOkNoPhdr) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span<const Phdr>{}, |
| elfldltl::PhdrStackObserver<Elf, /*CanBeExecutable=*/false>(size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(errors.size(), 1u); |
| EXPECT_EQ(errors.front(), "executable stack not supported: PT_GNU_STACK header required"); |
| } |
| |
| // Non-readable stacks are disallowed. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverNonReadable) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = {OnePageStack<Phdr>(Phdr::kWrite)}; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), elfldltl::PhdrStackObserver<Elf>(size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(errors.size(), 1u); |
| EXPECT_EQ(errors.front(), "stack is not readable: PF_R is not set"); |
| } |
| |
| // Non-writable stacks are disallowed. |
| TYPED_TEST(ElfldltlPhdrTests, StackObserverNonWritable) { |
| using Elf = typename TestFixture::Elf; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = {OnePageStack<Phdr>(Phdr::kRead)}; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<size_type> size; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), elfldltl::PhdrStackObserver<Elf>(size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(errors.size(), 1u); |
| EXPECT_EQ(errors.front(), "stack is not writable: PF_W is not set"); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, MetadataObserverNoPhdr) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<Phdr> phdr; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span<const Phdr>{}, |
| elfldltl::PhdrMetadataObserver<Elf, ElfPhdrType::kInterp>(phdr))); |
| |
| EXPECT_FALSE(phdr); |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, MetadataObserverUnalignedVaddr) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = { |
| { |
| .type = ElfPhdrType::kInterp, |
| .offset = kAlign + 1, |
| .vaddr = kAlign + 1, |
| .align = kAlign, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| std::optional<Phdr> phdr; |
| EXPECT_TRUE(elfldltl::DecodePhdrs( |
| diag, cpp20::span(kPhdrs), elfldltl::PhdrMetadataObserver<Elf, ElfPhdrType::kInterp>(phdr))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| |
| ASSERT_EQ(errors.size(), 1u); |
| EXPECT_EQ(errors[0], "PT_INTERP header has `p_vaddr % p_align != 0`"); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, MetadataObserverFileszNotEqMemsz) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = { |
| { |
| .type = ElfPhdrType::kInterp, |
| .filesz = kAlign, |
| .memsz = kAlign + 1, |
| .align = kAlign, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<Phdr> phdr; |
| EXPECT_TRUE(elfldltl::DecodePhdrs( |
| diag, cpp20::span(kPhdrs), elfldltl::PhdrMetadataObserver<Elf, ElfPhdrType::kInterp>(phdr))); |
| |
| EXPECT_TRUE(phdr); |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("PT_INTERP header has `p_filesz != p_memsz`", errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, MetadataObserverIncompatibleEntrySize) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using Dyn = typename Elf::Dyn; |
| |
| constexpr Phdr kPhdrs[] = { |
| { |
| .type = ElfPhdrType::kDynamic, |
| .filesz = sizeof(Dyn) + 1, |
| .memsz = sizeof(Dyn) + 1, |
| .align = kAlign, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<Phdr> phdr; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| elfldltl::PhdrMetadataObserver<Elf, ElfPhdrType::kDynamic, Dyn>(phdr))); |
| |
| EXPECT_TRUE(phdr); |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("PT_DYNAMIC segment size is not a multiple of entry size", errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, MetadataObserverIncompatibleEntryAlignment) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using Dyn = typename Elf::Dyn; |
| |
| constexpr Phdr kPhdrs[] = { |
| { |
| .type = ElfPhdrType::kDynamic, |
| .align = alignof(Dyn) / 2, // Too small. |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| std::optional<Phdr> phdr; |
| EXPECT_TRUE( |
| elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| elfldltl::PhdrMetadataObserver<Elf, ElfPhdrType::kDynamic, Dyn>(phdr))); |
| |
| EXPECT_TRUE(phdr); |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("PT_DYNAMIC segment alignment is not a multiple of entry alignment", errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, LoadObserverNoPhdr) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kBasic>; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span<const Phdr>{}, |
| LoadObserver(kPageSize, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(0u, vaddr_start); |
| EXPECT_EQ(0u, vaddr_size); |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, BasicLoadObserverSmallAlign) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kBasic>; |
| |
| constexpr Phdr kPhdrs[] = { |
| {.type = ElfPhdrType::kLoad, .memsz = kPageSize, .align = kPageSize / 2}, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kPageSize, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("PT_LOAD's `p_align` is not page-aligned", errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, BasicLoadObserverZeroMemsz) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kBasic>; |
| |
| constexpr Phdr kPhdrs[] = { |
| {.type = ElfPhdrType::kLoad, .memsz = 0}, |
| }; |
| |
| std::vector<std::string> warnings; |
| auto diag = elfldltl::CollectStringsDiagnostics(warnings, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kPageSize, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(1u, diag.warnings()); |
| ASSERT_EQ(1u, warnings.size()); |
| EXPECT_EQ("PT_LOAD has `p_memsz == 0`", warnings.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, BasicLoadObserverMemszTooSmall) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kBasic>; |
| |
| constexpr Phdr kPhdrs[] = { |
| {.type = ElfPhdrType::kLoad, .filesz = 0x100, .memsz = 0x100 - 1}, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kPageSize, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("PT_LOAD has `p_memsz < p_filesz`", errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, BasicLoadObserverMemEndOverflow) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kBasic>; |
| |
| constexpr auto kMax = std::numeric_limits<size_type>::max(); |
| |
| constexpr Phdr kPhdrs[] = { |
| {.type = ElfPhdrType::kLoad, .vaddr = kAlign, .memsz = kMax}, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kPageSize, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("PT_LOAD has overflowing `p_vaddr + p_memsz`", errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, BasicLoadObserverAlignedMemEndOverflow) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kBasic>; |
| |
| constexpr auto kMax = std::numeric_limits<size_type>::max(); |
| |
| constexpr Phdr kPhdrs[] = { |
| {.type = ElfPhdrType::kLoad, .vaddr = 0, .memsz = kMax - kAlign + 2, .align = kAlign}, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kPageSize, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("PT_LOAD has overflowing `p_align`-aligned `p_vaddr + p_memsz`", errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, BasicLoadObserverFileEndOverflow) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kBasic>; |
| |
| constexpr auto kMax = std::numeric_limits<size_type>::max(); |
| |
| constexpr Phdr kPhdrs[] = { |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 2 * kAlign, |
| .filesz = kMax - kAlign, |
| .memsz = kMax - kAlign, |
| .align = kAlign, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kPageSize, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("PT_LOAD has overflowing `p_offset + p_filesz`", errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, BasicLoadObserverAlignedFileEndOverflow) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kBasic>; |
| |
| constexpr auto kMax = std::numeric_limits<size_type>::max(); |
| |
| constexpr Phdr kPhdrs[] = { |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 2 * kAlign, |
| .filesz = kMax - 3 * kAlign + 2, |
| .memsz = kMax - 3 * kAlign + 2, |
| .align = kAlign, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kPageSize, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("PT_LOAD has overflowing `p_align`-aligned `p_offset + p_filesz`", errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, BasicLoadObserverUnordered) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kBasic>; |
| |
| constexpr Phdr kPhdrs[] = { |
| {.type = ElfPhdrType::kLoad, .vaddr = kAlign, .memsz = kAlign, .align = kAlign}, |
| {.type = ElfPhdrType::kLoad, .vaddr = 3 * kAlign, .memsz = kAlign, .align = kAlign}, |
| {.type = ElfPhdrType::kLoad, .vaddr = 2 * kAlign, .memsz = kAlign, .align = kAlign}, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kPageSize, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ( |
| "PT_LOAD has `p_align`-aligned memory ranges that overlap or do not increase " |
| "monotonically", |
| errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, BasicLoadObserverOverlappingMemoryRange) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kBasic>; |
| |
| constexpr Phdr kPhdrs[] = { |
| {.type = ElfPhdrType::kLoad, .vaddr = kAlign, .memsz = 2 * kAlign}, |
| {.type = ElfPhdrType::kLoad, .vaddr = 2 * kAlign, .memsz = 2 * kAlign}, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kPageSize, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ( |
| "PT_LOAD has `p_align`-aligned memory ranges that overlap or do not increase " |
| "monotonically", |
| errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, BasicLoadObserverCompliant) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kBasic>; |
| |
| constexpr Phdr kPhdrs[] = { |
| // [kAlign + 10, 2*kAlign + 10) |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 10, |
| .vaddr = kAlign + 10, |
| .memsz = kAlign, |
| .align = kAlign, |
| }, |
| // [3*kAlign, (7/2)*kAlign) |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = kAlign, |
| .vaddr = 3 * kAlign, |
| .memsz = kAlign / 2, |
| .align = kAlign, |
| }, |
| // [(37/2)*kAlign, 100*kAlign - 10) |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = kAlign / 2, |
| .vaddr = 37 * (kAlign / 2), |
| .memsz = 100 * kAlign - 10 - 37 * (kAlign / 2), |
| .align = kAlign, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kAlign / 2, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| |
| EXPECT_EQ(kAlign, vaddr_start); |
| EXPECT_EQ(99 * kAlign, vaddr_size); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, FileRangeMonotonicLoadObserverUnordered) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = |
| elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kFileRangeMonotonic>; |
| |
| constexpr Phdr kPhdrs[] = { |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = kPageSize, |
| .vaddr = 0, |
| .filesz = kPageSize, |
| .memsz = kPageSize, |
| }, |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 3 * kPageSize, |
| .vaddr = kPageSize, |
| .filesz = kPageSize, |
| .memsz = kPageSize, |
| }, |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 2 * kPageSize, |
| .vaddr = 2 * kPageSize, |
| .filesz = kPageSize, |
| .memsz = kPageSize, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kPageSize, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ( |
| "PT_LOAD has `p_align`-aligned file offset ranges that overlap or do not " |
| "increase monotonically", |
| errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, FileRangeMonotonicLoadObserverOverlappingAlignedFileRange) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = |
| elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kFileRangeMonotonic>; |
| |
| constexpr Phdr kPhdrs[] = { |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 0, |
| .vaddr = 0, |
| .filesz = 3 * (kAlign / 2), |
| .memsz = 3 * (kAlign / 2), |
| .align = kAlign, |
| }, |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 3 * (kAlign / 2), |
| .vaddr = 5 * (kAlign / 2), |
| .filesz = kAlign, |
| .memsz = kAlign, |
| .align = kAlign, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kAlign, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ( |
| "PT_LOAD has `p_align`-aligned file offset ranges that overlap or do not " |
| "increase monotonically", |
| errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, FileRangeMonotonicLoadObserverCompliant) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = |
| elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kFileRangeMonotonic>; |
| |
| constexpr Phdr kPhdrs[] = { |
| // memory: [kAlign + 10, (3/2)*kAlign + 10) |
| // file: [kAlign + 10, (3/2)*kAlign + 10) |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = kAlign + 10, |
| .vaddr = kAlign + 10, |
| .filesz = kAlign / 2, |
| .memsz = kAlign / 2, |
| .align = kAlign, |
| }, |
| // memory: [3*kAlign, (7/2)*kAlign) |
| // file: [2*kAlign, 3*kAlign) |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 2 * kAlign, |
| .vaddr = 3 * kAlign, |
| .filesz = kAlign / 2, |
| .memsz = kAlign / 2, |
| .align = kAlign, |
| }, |
| // memory: [(37/2)*kAlign - 100, 100*kAlign - 10) |
| // file: [(21/2)*kAlign - 100, 11*kAlign - 10) |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 21 * (kAlign / 2) - 100, |
| .vaddr = 37 * (kAlign / 2) - 100, |
| .filesz = 11 * kAlign - 10 - 21 * (kAlign / 2) + 100, |
| .memsz = 100 * kAlign - 10 - 37 * (kAlign / 2) + 100, |
| .align = kAlign, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kAlign, vaddr_start, vaddr_size))); |
| |
| // EXPECT_EQ("", errors.front()); |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| EXPECT_EQ(kAlign, vaddr_start); |
| EXPECT_EQ(99 * kAlign, vaddr_size); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ContiguousLoadObserverHighFirstOffset) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kContiguous>; |
| |
| constexpr Phdr kPhdrs[] = { |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 0, |
| .vaddr = kAlign, |
| .filesz = kAlign, |
| .memsz = kAlign, |
| .align = kAlign, |
| }, |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 3 * kAlign, |
| .vaddr = 2 * kAlign, |
| .filesz = kAlign, |
| .memsz = kAlign, |
| .align = kAlign, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kAlign, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("PT_LOAD has `p_align`-aligned file offset ranges that are not contiguous", |
| errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ContiguousLoadObserverNonContiguousFileRanges) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kContiguous>; |
| |
| constexpr Phdr kPhdrs[] = { |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = kPageSize, |
| .vaddr = kPageSize, |
| .filesz = kPageSize, |
| .memsz = kPageSize, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kPageSize, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("first PT_LOAD's `p_offset` does not lie within the first page", errors.front()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ContiguousLoadObserverCompliant) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| using LoadObserver = elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kContiguous>; |
| |
| constexpr Phdr kPhdrs[] = { |
| // memory: [kAlign + 10, 2*kAlign + 10) |
| // file: [10, kAlign) |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 10, |
| .vaddr = kAlign + 10, |
| .filesz = kAlign - 10, |
| .memsz = kAlign, |
| .align = kAlign, |
| }, |
| // memory: [3*kAlign + 10, (9/2)*kAlign + 100) |
| // file: [kAlign + 10, 2*kAlign - 1) |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = kAlign + 10, |
| .vaddr = 3 * kAlign + 10, |
| .filesz = kAlign - 11, |
| .memsz = 3 * (kAlign / 2) + 90, |
| .align = kAlign, |
| }, |
| // memory: [5*kAlign + 100, 6*kAlign + 100) |
| // file: [2*kAlign + 100, 3*kAlign) |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 2 * kAlign + 100, |
| .vaddr = 5 * kAlign + 100, |
| .filesz = kAlign - 100, |
| .memsz = kAlign, |
| .align = kAlign, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| EXPECT_TRUE(elfldltl::DecodePhdrs(diag, cpp20::span(kPhdrs), |
| LoadObserver(kAlign, vaddr_start, vaddr_size))); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| EXPECT_EQ(kAlign, vaddr_start); |
| EXPECT_EQ(6 * kAlign, vaddr_size); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, LoadObserverCallback) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| |
| struct ExpectedLoad { |
| size_type offset, filesz, memsz, limit; |
| }; |
| constexpr std::array kExpected{ |
| ExpectedLoad{ |
| .offset = 0, |
| .filesz = 1234, |
| .memsz = 2345, |
| .limit = kAlign, |
| }, |
| ExpectedLoad{ |
| .offset = kAlign, |
| .filesz = 2345, |
| .memsz = 3456, |
| .limit = 2 * kAlign, |
| }, |
| }; |
| |
| constexpr Phdr kPhdrs[] = { |
| { |
| .type = ElfPhdrType::kInterp, |
| .offset = 1200, |
| .filesz = 17, |
| .memsz = 17, |
| }, |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = kExpected[0].offset, |
| .vaddr = 0, |
| .filesz = kExpected[0].filesz, |
| .memsz = kExpected[0].memsz, |
| .align = kAlign, |
| }, |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = kExpected[1].offset, |
| .vaddr = kAlign, |
| .filesz = kExpected[1].filesz, |
| .memsz = kExpected[1].memsz, |
| .align = kAlign, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = 0; |
| size_type vaddr_size = 0; |
| |
| size_t count = 0; |
| auto check_phdrs = [kExpected, &count, &diag](auto& callback_diag, const Phdr& phdr) -> bool { |
| EXPECT_EQ(&callback_diag, &diag); |
| |
| // We only get callbacks for the PT_LOAD headers, not the others. |
| EXPECT_EQ(phdr.type, ElfPhdrType::kLoad); |
| |
| EXPECT_LT(count, std::size(kExpected)); |
| if (count >= std::size(kExpected)) { |
| return false; |
| } |
| |
| EXPECT_EQ(phdr.offset(), kExpected[count].offset) << count; |
| EXPECT_EQ(phdr.filesz(), kExpected[count].filesz) << count; |
| EXPECT_EQ(phdr.memsz(), kExpected[count].memsz) << count; |
| |
| ++count; |
| return true; |
| }; |
| |
| EXPECT_TRUE(elfldltl::DecodePhdrs( |
| diag, cpp20::span(kPhdrs), |
| elfldltl::MakePhdrLoadObserver<Elf>(kPageSize, vaddr_start, vaddr_size, check_phdrs))); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| EXPECT_EQ(0u, vaddr_start); |
| EXPECT_EQ(kAlign * 2, vaddr_size); |
| |
| ASSERT_EQ(count, 2u); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, LoadObserverCallbackBailout) { |
| using Elf = typename TestFixture::Elf; |
| using Phdr = typename Elf::Phdr; |
| using size_type = typename Elf::size_type; |
| |
| constexpr Phdr kPhdrs[] = { |
| { |
| .type = ElfPhdrType::kInterp, |
| .offset = 1200, |
| .filesz = 17, |
| .memsz = 17, |
| }, |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 0, |
| .vaddr = 0, |
| .filesz = 1234, |
| .memsz = 1234, |
| .align = kAlign, |
| }, |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = 0, |
| .vaddr = kAlign, |
| .filesz = 1234, |
| .memsz = 2345, |
| .align = kAlign, |
| }, |
| }; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| size_type vaddr_start = -1; |
| size_type vaddr_size = 0; |
| |
| auto bail_early = [](auto& diag, const Phdr& phdr) { |
| EXPECT_EQ(phdr.memsz(), 1234u); |
| return false; |
| }; |
| EXPECT_FALSE(elfldltl::DecodePhdrs( |
| diag, cpp20::span(kPhdrs), |
| elfldltl::MakePhdrLoadObserver<Elf>(kPageSize, vaddr_start, vaddr_size, bail_early))); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| EXPECT_EQ(0u, vaddr_start); |
| |
| // It should have bailed out on seeing the first PT_LOAD, but only after the |
| // generic code updated the vaddr_size. It's still before the second PT_LOAD |
| // gets processed, so the vaddr_size shouldn't have its final value yet. |
| EXPECT_EQ(kAlign, vaddr_size); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ReadPhdrsFromFileBadSize) { |
| using Elf = typename TestFixture::Elf; |
| using Ehdr = typename Elf::Ehdr; |
| using Phdr = typename Elf::Phdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| Ehdr ehdr{.phentsize = sizeof(Phdr) + 1, .phnum = 1}; |
| elfldltl::DirectMemory file; |
| auto result = ReadPhdrsFromFile(diag, file, elfldltl::NoArrayFromFile<Phdr>(), ehdr); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("e_phentsize has unexpected value", errors[0]); |
| EXPECT_EQ(0u, diag.warnings()); |
| EXPECT_FALSE(result); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ReadPhdrsFromFileBadOffset) { |
| using Elf = typename TestFixture::Elf; |
| using Ehdr = typename Elf::Ehdr; |
| using Phdr = typename Elf::Phdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| Ehdr ehdr{.phoff = 0, .phentsize = sizeof(Phdr), .phnum = 1}; |
| elfldltl::DirectMemory file; |
| auto result = ReadPhdrsFromFile(diag, file, elfldltl::NoArrayFromFile<Phdr>(), ehdr); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("e_phoff overlaps with ELF file header", errors[0]); |
| EXPECT_EQ(0u, diag.warnings()); |
| EXPECT_FALSE(result); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ReadPhdrsFromFileBadAlign) { |
| using Elf = typename TestFixture::Elf; |
| using Ehdr = typename Elf::Ehdr; |
| using Phdr = typename Elf::Phdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| Ehdr ehdr{.phoff = sizeof(Ehdr) + 1, .phentsize = sizeof(Phdr), .phnum = 1}; |
| elfldltl::DirectMemory file; |
| auto result = ReadPhdrsFromFile(diag, file, elfldltl::NoArrayFromFile<Phdr>(), ehdr); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("e_phoff has insufficient alignment", errors[0]); |
| EXPECT_EQ(0u, diag.warnings()); |
| EXPECT_FALSE(result); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ReadPhdrsFromFilePhXNumBadShSize) { |
| using Elf = typename TestFixture::Elf; |
| using Ehdr = typename Elf::Ehdr; |
| using Phdr = typename Elf::Phdr; |
| using Shdr = typename Elf::Shdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| Ehdr ehdr{.phoff = sizeof(Ehdr), |
| .phentsize = sizeof(Phdr), |
| .phnum = Ehdr::kPnXnum, |
| .shentsize = sizeof(Shdr) + 1, |
| .shnum = 1}; |
| elfldltl::DirectMemory file; |
| auto result = ReadPhdrsFromFile(diag, file, elfldltl::NoArrayFromFile<Phdr>(), ehdr); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("e_shentsize has unexpected value", errors[0]); |
| EXPECT_EQ(0u, diag.warnings()); |
| EXPECT_FALSE(result); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ReadPhdrsFromFilePhXNumBadShOff) { |
| using Elf = typename TestFixture::Elf; |
| using Ehdr = typename Elf::Ehdr; |
| using Phdr = typename Elf::Phdr; |
| using Shdr = typename Elf::Shdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| Ehdr ehdr{.phoff = sizeof(Ehdr), |
| .shoff = 0, |
| .phentsize = sizeof(Phdr), |
| .phnum = Ehdr::kPnXnum, |
| .shentsize = sizeof(Shdr), |
| .shnum = 1}; |
| elfldltl::DirectMemory file; |
| auto result = ReadPhdrsFromFile(diag, file, elfldltl::NoArrayFromFile<Phdr>(), ehdr); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("e_shoff overlaps with ELF file header", errors[0]); |
| EXPECT_EQ(0u, diag.warnings()); |
| EXPECT_FALSE(result); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ReadPhdrsFromFilePhXNumNoShdrs) { |
| using Elf = typename TestFixture::Elf; |
| using Ehdr = typename Elf::Ehdr; |
| using Phdr = typename Elf::Phdr; |
| using Shdr = typename Elf::Shdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| Ehdr ehdr{.phoff = sizeof(Ehdr), |
| .shoff = sizeof(Ehdr), |
| .phentsize = sizeof(Phdr), |
| .phnum = Ehdr::kPnXnum, |
| .shentsize = sizeof(Shdr), |
| .shnum = 0}; |
| elfldltl::DirectMemory file; |
| auto result = ReadPhdrsFromFile(diag, file, elfldltl::NoArrayFromFile<Phdr>(), ehdr); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("PN_XNUM with no section headers", errors[0]); |
| EXPECT_EQ(0u, diag.warnings()); |
| EXPECT_FALSE(result); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ReadPhdrsFromFilePhXNumCantReadShdr) { |
| using Elf = typename TestFixture::Elf; |
| using Ehdr = typename Elf::Ehdr; |
| using Phdr = typename Elf::Phdr; |
| using Shdr = typename Elf::Shdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| Ehdr ehdr{.phoff = sizeof(Ehdr), |
| .shoff = sizeof(Ehdr), |
| .phentsize = sizeof(Phdr), |
| .phnum = Ehdr::kPnXnum, |
| .shentsize = sizeof(Shdr), |
| .shnum = 1}; |
| elfldltl::DirectMemory file; |
| auto result = ReadPhdrsFromFile(diag, file, elfldltl::NoArrayFromFile<Phdr>(), ehdr); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("cannot read section header 0 from ELF file", errors[0]); |
| EXPECT_EQ(0u, diag.warnings()); |
| EXPECT_FALSE(result); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ReadPhdrsFromFileCantReadPhdr) { |
| using Elf = typename TestFixture::Elf; |
| using Ehdr = typename Elf::Ehdr; |
| using Phdr = typename Elf::Phdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| Ehdr ehdr{.phoff = sizeof(Ehdr), .phentsize = sizeof(Phdr), .phnum = 1}; |
| elfldltl::DirectMemory file; |
| auto result = ReadPhdrsFromFile(diag, file, elfldltl::NoArrayFromFile<Phdr>(), ehdr); |
| |
| EXPECT_EQ(1u, diag.errors()); |
| ASSERT_EQ(1u, errors.size()); |
| EXPECT_EQ("cannot read program headers from ELF file", errors[0]); |
| EXPECT_EQ(0u, diag.warnings()); |
| EXPECT_FALSE(result); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ReadPhdrsFromFileNoPhdrs) { |
| using Elf = typename TestFixture::Elf; |
| using Ehdr = typename Elf::Ehdr; |
| using Phdr = typename Elf::Phdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| Ehdr ehdr{.phnum = 0}; |
| elfldltl::DirectMemory file; |
| auto result = ReadPhdrsFromFile(diag, file, elfldltl::NoArrayFromFile<Phdr>(), ehdr); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_TRUE(result); |
| EXPECT_EQ(0u, result->size()); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ReadPhdrsFromFile) { |
| using Elf = typename TestFixture::Elf; |
| using Ehdr = typename Elf::Ehdr; |
| using Phdr = typename Elf::Phdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| struct { |
| Ehdr ehdr{.phoff = sizeof(Ehdr), .phentsize = sizeof(Phdr), .phnum = 1}; |
| Phdr phdrs[1]{}; |
| } elfbytes; |
| elfldltl::DirectMemory file{ |
| cpp20::span<std::byte>{reinterpret_cast<std::byte*>(&elfbytes), sizeof(elfbytes)}}; |
| auto result = ReadPhdrsFromFile(diag, file, elfldltl::NoArrayFromFile<Phdr>(), elfbytes.ehdr); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_TRUE(result); |
| auto& phdrs = *result; |
| EXPECT_EQ(1u, phdrs.size()); |
| EXPECT_FALSE((memcmp(elfbytes.phdrs, std::addressof(phdrs[0]), sizeof(Phdr)))); |
| } |
| |
| TYPED_TEST(ElfldltlPhdrTests, ReadPhdrsFromFilePhXNum) { |
| using Elf = typename TestFixture::Elf; |
| using Ehdr = typename Elf::Ehdr; |
| using Phdr = typename Elf::Phdr; |
| using Shdr = typename Elf::Shdr; |
| |
| std::vector<std::string> errors; |
| auto diag = elfldltl::CollectStringsDiagnostics(errors, kFlags); |
| |
| struct { |
| Ehdr ehdr{.phoff = sizeof(Ehdr) + sizeof(Shdr), |
| .shoff = sizeof(Ehdr), |
| .phentsize = sizeof(Phdr), |
| .phnum = Ehdr::kPnXnum, |
| .shentsize = sizeof(Shdr), |
| .shnum = 1}; |
| Shdr shdrs[1]{{.info = 1}}; |
| Phdr phdrs[1]{}; |
| } elfbytes; |
| elfldltl::DirectMemory file{ |
| cpp20::span<std::byte>{reinterpret_cast<std::byte*>(&elfbytes), sizeof(elfbytes)}}; |
| auto result = ReadPhdrsFromFile(diag, file, elfldltl::NoArrayFromFile<Phdr>(), elfbytes.ehdr); |
| |
| EXPECT_EQ(0u, diag.errors()); |
| EXPECT_EQ(0u, diag.warnings()); |
| ASSERT_TRUE(result); |
| auto& phdrs = *result; |
| EXPECT_EQ(1u, phdrs.size()); |
| EXPECT_FALSE((memcmp(elfbytes.phdrs, std::addressof(phdrs[0]), sizeof(Phdr)))); |
| } |
| |
| } // namespace |