| // 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/phdr.h> |
| #include <lib/stdcompat/span.h> |
| |
| #include <optional> |
| #include <string> |
| #include <vector> |
| |
| #include <zxtest/zxtest.h> |
| |
| #include "tests.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; |
| } |
| |
| constexpr auto EmptyTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, Empty) { TestAllFormats(EmptyTest); } |
| |
| // No PT_NULL headers. |
| constexpr auto NullObserverNoNullsTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, NullObserverNoNulls) { TestAllFormats(NullObserverNoNullsTest); } |
| |
| // One PT_NULL header. |
| constexpr auto NullObserverOneNullTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(1, diag.warnings()); |
| ASSERT_EQ(1, warnings.size()); |
| EXPECT_STREQ(kNullWarning, warnings[0]); |
| }; |
| |
| TEST(ElfldltlPhdrTests, NullObserverOneNull) { TestAllFormats(NullObserverOneNullTest); } |
| |
| // Three PT_NULL headers. |
| constexpr auto NullObserverThreeNullsTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(3, diag.warnings()); |
| ASSERT_EQ(3, warnings.size()); |
| EXPECT_STREQ(kNullWarning, warnings[0]); |
| EXPECT_STREQ(kNullWarning, warnings[1]); |
| EXPECT_STREQ(kNullWarning, warnings[2]); |
| }; |
| |
| TEST(ElfldltlPhdrTests, NullObserverThreeNulls) { TestAllFormats(NullObserverThreeNullsTest); } |
| |
| // At most one header per type. |
| constexpr auto SingletonObserverAtMostOneHeaderPerTypeTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(0, 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); |
| }; |
| |
| TEST(ElfldltlPhdrTests, SingletonObserverAtMostOneHeaderPerType) { |
| TestAllFormats(SingletonObserverAtMostOneHeaderPerTypeTest); |
| } |
| |
| // Multiple headers per type. |
| constexpr auto SingletonObserverMultipleHeadersPerTypeTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(2, diag.warnings()); |
| |
| ASSERT_EQ(warnings.size(), 2); |
| EXPECT_STREQ(warnings[0], "too many PT_GNU_RELRO headers; expected at most one"); |
| EXPECT_STREQ(warnings[1], "too many PT_INTERP headers; expected at most one"); |
| }; |
| |
| TEST(ElfldltlPhdrTests, SingletonObserverMultipleHeadersPerType) { |
| TestAllFormats(SingletonObserverMultipleHeadersPerTypeTest); |
| } |
| |
| constexpr auto UnknownFlagsTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(4, diag.warnings()); |
| |
| ASSERT_EQ(warnings.size(), 4); |
| EXPECT_STREQ(warnings[0], |
| "PT_DYNAMIC header has unrecognized flags (other than PF_R, PF_W, PF_X)"); |
| EXPECT_STREQ(warnings[1], |
| "PT_INTERP header has unrecognized flags (other than PF_R, PF_W, PF_X)"); |
| EXPECT_STREQ(warnings[2], |
| "PT_GNU_STACK header has unrecognized flags (other than PF_R, PF_W, PF_X)"); |
| EXPECT_STREQ(warnings[3], |
| "PT_GNU_RELRO header has unrecognized flags (other than PF_R, PF_W, PF_X)"); |
| }; |
| |
| TEST(ElfldltlPhdrTests, UnknownFlags) { TestAllFormats(UnknownFlagsTest); } |
| |
| constexpr auto BadAlignmentTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(3, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| |
| ASSERT_EQ(errors.size(), 3); |
| EXPECT_STREQ(errors[0], "PT_INTERP header has `p_align` that is not zero or a power of two"); |
| EXPECT_STREQ(errors[1], "PT_NOTE header has `p_align` that is not zero or a power of two"); |
| EXPECT_STREQ(errors[2], "PT_GNU_RELRO header has `p_align` that is not zero or a power of two"); |
| }; |
| |
| TEST(ElfldltlPhdrTests, BadAlignment) { TestAllFormats(BadAlignmentTest); } |
| |
| constexpr auto OffsetNotEquivVaddrTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(elf)>; |
| using Phdr = typename Elf::Phdr; |
| |
| constexpr Phdr kPhdrs[] = { |
| // OK |
| { |
| .type = ElfPhdrType::kLoad, |
| .offset = kAlign, |
| .vaddr = kAlign, |
| .align = kAlign, |
| }, |
| // OK |
| { |
| .type = ElfPhdrType::kDynamic, |
| .offset = 17 * kAlign, |
| .vaddr = kAlign, |
| .align = kAlign, |
| }, |
| // OK |
| { |
| .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(2, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| |
| ASSERT_EQ(2, errors.size()); |
| EXPECT_STREQ(errors[0], |
| "PT_NOTE header has incongruent `p_offset` and `p_vaddr` modulo `p_align`"); |
| EXPECT_STREQ(errors[1], |
| "PT_GNU_RELRO header has incongruent `p_offset` and `p_vaddr` modulo `p_align`"); |
| }; |
| |
| TEST(ElfldltlPhdrTests, OffsetNotEquivVaddrVaddr) { TestAllFormats(OffsetNotEquivVaddrTest); } |
| |
| // Executable stack permitted; non-zero memsz. |
| constexpr auto StackObserverExecOkPhdrNonzeroSizeTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0x1000, *size); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverExecOkPhdrNonzeroSize) { |
| TestAllFormats(StackObserverExecOkPhdrNonzeroSizeTest); |
| } |
| |
| // Executable stack permitted; zero memsz. |
| constexpr auto StackObserverExecOkPhdrZeroSizeTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverExecOkPhdrZeroSize) { |
| TestAllFormats(StackObserverExecOkPhdrZeroSizeTest); |
| } |
| |
| // Executable stack permitted; no header to report size. |
| constexpr auto StackObserverExecOkNoPhdrSizeTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverExecOkNoPhdrSize) { |
| TestAllFormats(StackObserverExecOkNoPhdrSizeTest); |
| } |
| |
| // Executable stack permitted; header present and reports PF_X. |
| constexpr auto StackObserverExecOkPhdrWithXTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0x1000, *size); |
| EXPECT_TRUE(executable); |
| EXPECT_EQ(0, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverExecOkPhdrWithX) { |
| TestAllFormats(StackObserverExecOkPhdrWithXTest); |
| } |
| |
| // Executable stack permitted; header present and does not report PF_X. |
| constexpr auto StackObserverExecOkPhdrWithoutXTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverExecOkPhdrWithoutX) { |
| TestAllFormats(StackObserverExecOkPhdrWithoutXTest); |
| } |
| |
| // Executable stack permitted; header not present. |
| constexpr auto StackObserverExecOkNoPhdrTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverExecOkNoPhdr) { |
| TestAllFormats(StackObserverExecOkNoPhdrTest); |
| } |
| |
| // Executable stack not permitted; non-zero memsz. |
| constexpr auto StackObserverExecNotOkPhdrNonzeroSizeTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0x1000, *size); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverExecNotOkPhdrNonzeroSize) { |
| TestAllFormats(StackObserverExecNotOkPhdrNonzeroSizeTest); |
| } |
| |
| // Executable stack not permitted; zero memsz. |
| constexpr auto StackObserverExecNotOkPhdrZeroSizeTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverExecNotOkPhdrZeroSize) { |
| TestAllFormats(StackObserverExecNotOkPhdrZeroSizeTest); |
| } |
| |
| // Executable stack not permitted; no header to report size. |
| constexpr auto StackObserverExecNotOkNoPhdrSizeTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverExecNotOkNoPhdrSize) { |
| TestAllFormats(StackObserverExecNotOkNoPhdrSizeTest); |
| } |
| |
| // Executable stack not permitted; header present and reports PF_X. |
| constexpr auto StackObserverExecNotOkPhdrWithXTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(errors.size(), 1); |
| EXPECT_STREQ(errors.front(), "executable stack not supported: PF_X is set"); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverExecNotOkPhdrWithX) { |
| TestAllFormats(StackObserverExecNotOkPhdrWithXTest); |
| } |
| |
| // Executable stack not permitted; header present and does not report PF_X. |
| constexpr auto StackObserverExecNotOkPhdrWithoutXTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverExecNotOkPhdrWithoutX) { |
| TestAllFormats(StackObserverExecNotOkPhdrWithoutXTest); |
| } |
| |
| // Executable stack not permitted; header not present. |
| constexpr auto StackObserverExecNotOkNoPhdrTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(errors.size(), 1); |
| EXPECT_STREQ(errors.front(), "executable stack not supported: PT_GNU_STACK header required"); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverExecNotOkNoPhdr) { |
| TestAllFormats(StackObserverExecNotOkNoPhdrTest); |
| } |
| |
| // Non-readable stacks are disallowed. |
| constexpr auto StackObserverNonReadableTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(errors.size(), 1); |
| EXPECT_STREQ(errors.front(), "stack is not readable: PF_R is not set"); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverNonReadable) { TestAllFormats(StackObserverNonReadableTest); } |
| |
| // Non-writable stacks are disallowed. |
| constexpr auto StackObserverNonWritableTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(errors.size(), 1); |
| EXPECT_STREQ(errors.front(), "stack is not writable: PF_W is not set"); |
| }; |
| |
| TEST(ElfldltlPhdrTests, StackObserverNonWritable) { TestAllFormats(StackObserverNonWritableTest); } |
| |
| constexpr auto MetadataObserverNoPhdrTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, MetadataObserverNoPhdr) { TestAllFormats(MetadataObserverNoPhdrTest); } |
| |
| constexpr auto MetadataObserverUnalignedVaddrTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| |
| ASSERT_EQ(errors.size(), 1); |
| EXPECT_STREQ(errors[0], "PT_INTERP header has `p_vaddr % p_align != 0`"); |
| }; |
| |
| TEST(ElfldltlPhdrTests, MetadataObserverUnalignedVaddr) { |
| TestAllFormats(MetadataObserverUnalignedVaddrTest); |
| } |
| |
| constexpr auto MetadataObserverFileszNotEqMemszTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ("PT_INTERP header has `p_filesz != p_memsz`", errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, MetadataObserverFileszNotEqMemsz) { |
| TestAllFormats(MetadataObserverFileszNotEqMemszTest); |
| } |
| |
| constexpr auto MetadataObserverIncompatibleEntrySizeTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ("PT_DYNAMIC segment size is not a multiple of entry size", errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, MetadataObserverIncompatibleEntrySize) { |
| TestAllFormats(MetadataObserverIncompatibleEntrySizeTest); |
| } |
| |
| constexpr auto MetadataObserverIncompatibleEntryAlignmentTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ("PT_DYNAMIC segment alignment is not a multiple of entry alignment", errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, MetadataObserverIncompatibleAlignmentSize) { |
| TestAllFormats(MetadataObserverIncompatibleEntryAlignmentTest); |
| } |
| |
| constexpr auto LoadObserverNoPhdrTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, vaddr_start); |
| EXPECT_EQ(0, vaddr_size); |
| EXPECT_EQ(0, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, LoadObserverNoPhdr) { TestAllFormats(LoadObserverNoPhdrTest); } |
| |
| constexpr auto BasicLoadObserverSmallAlignTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ("PT_LOAD's `p_align` is not page-aligned", errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, BasicLoadObserverSmallAlign) { |
| TestAllFormats(BasicLoadObserverSmallAlignTest); |
| } |
| |
| constexpr auto BasicLoadObserverZeroMemszTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(1, diag.warnings()); |
| ASSERT_EQ(1, warnings.size()); |
| EXPECT_STREQ("PT_LOAD has `p_memsz == 0`", warnings.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, BasicLoadObserverZeroMemsz) { |
| TestAllFormats(BasicLoadObserverZeroMemszTest); |
| } |
| |
| constexpr auto BasicLoadObserverMemszTooSmallTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ("PT_LOAD has `p_memsz < p_filez`", errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, BasicLoadObserverMemszTooSmall) { |
| TestAllFormats(BasicLoadObserverMemszTooSmallTest); |
| } |
| |
| constexpr auto BasicLoadObserverMemEndOverflowTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ("PT_LOAD has overflowing `p_vaddr + p_memsz`", errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, BasicLoadObserverMemEndOverflow) { |
| TestAllFormats(BasicLoadObserverMemEndOverflowTest); |
| } |
| |
| constexpr auto BasicLoadObserverAlignedMemEndOverflowTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ("PT_LOAD has overflowing `p_align`-aligned `p_vaddr + p_memsz`", errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, BasicLoadObserverAlignedMemEndOverflows) { |
| TestAllFormats(BasicLoadObserverAlignedMemEndOverflowTest); |
| } |
| |
| constexpr auto BasicLoadObserverFileEndOverflowTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ("PT_LOAD has overflowing `p_offset + p_filesz`", errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, BasicLoadObserverFileEndOverflow) { |
| TestAllFormats(BasicLoadObserverFileEndOverflowTest); |
| } |
| |
| constexpr auto BasicLoadObserverAlignedFileEndOverflowTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ("PT_LOAD has overflowing `p_align`-aligned `p_offset + p_filesz`", errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, BasicLoadObserverAlignedFileEndOverflows) { |
| TestAllFormats(BasicLoadObserverAlignedFileEndOverflowTest); |
| } |
| |
| constexpr auto BasicLoadObserverUnorderedTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ( |
| "PT_LOAD has `p_align`-aligned memory ranges that overlap or do not increase " |
| "monotonically", |
| errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, BasicLoadObserverUnordered) { |
| TestAllFormats(BasicLoadObserverUnorderedTest); |
| } |
| |
| constexpr auto BasicLoadObserverOverlappingMemoryRangeTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ( |
| "PT_LOAD has `p_align`-aligned memory ranges that overlap or do not increase " |
| "monotonically", |
| errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, BasicLoadObserverOverlappingMemoryRange) { |
| TestAllFormats(BasicLoadObserverOverlappingMemoryRangeTest); |
| } |
| |
| constexpr auto BasicLoadObserverCompliantTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| |
| EXPECT_EQ(kAlign, vaddr_start); |
| EXPECT_EQ(99 * kAlign, vaddr_size); |
| }; |
| |
| TEST(ElfldltlPhdrTests, BasicLoadObserverCompliant) { |
| TestAllFormats(BasicLoadObserverCompliantTest); |
| } |
| |
| constexpr auto FileRangeMonotonicLoadObserverUnorderedTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ( |
| "PT_LOAD has `p_align`-aligned file offset ranges that overlap or do not " |
| "increase monotonically", |
| errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, FileRangeMonotonicLoadObserverUnordered) { |
| TestAllFormats(FileRangeMonotonicLoadObserverUnorderedTest); |
| } |
| |
| constexpr auto FileRangeMonotonicLoadObserverOverlappingAlignedFileRangeTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ( |
| "PT_LOAD has `p_align`-aligned file offset ranges that overlap or do not " |
| "increase monotonically", |
| errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, FileRangeMonotonicLoadObserverOverlappingAlignedFileRange) { |
| TestAllFormats(FileRangeMonotonicLoadObserverOverlappingAlignedFileRangeTest); |
| } |
| |
| constexpr auto FileRangeMonotonicLoadObserverCompliantTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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_STREQ("", errors.front()); |
| EXPECT_EQ(0, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| EXPECT_EQ(kAlign, vaddr_start); |
| EXPECT_EQ(99 * kAlign, vaddr_size); |
| }; |
| |
| TEST(ElfldltlPhdrTests, FileRangeMonotonicLoadObserverCompliant) { |
| TestAllFormats(FileRangeMonotonicLoadObserverCompliantTest); |
| } |
| |
| constexpr auto ContiguousLoadObserverHighFirstOffsetTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ("PT_LOAD has `p_align`-aligned file offset ranges that are not contiguous", |
| errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, ContiguousLoadObserverHighFirstOffset) { |
| TestAllFormats(ContiguousLoadObserverHighFirstOffsetTest); |
| } |
| |
| constexpr auto ContiguousLoadObserverNonContiguousFileRangesTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(1, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| ASSERT_EQ(1, errors.size()); |
| EXPECT_STREQ("first PT_LOAD's `p_offset` does not lie within the first page", errors.front()); |
| }; |
| |
| TEST(ElfldltlPhdrTests, ContiguousLoadObserverNonContiguousFileRanges) { |
| TestAllFormats(ContiguousLoadObserverNonContiguousFileRangesTest); |
| } |
| |
| constexpr auto ContiguousLoadObserverCompliantTest = [](auto&& elf) { |
| using Elf = std::decay_t<decltype(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(0, diag.errors()); |
| EXPECT_EQ(0, diag.warnings()); |
| EXPECT_EQ(kAlign, vaddr_start); |
| EXPECT_EQ(6 * kAlign, vaddr_size); |
| }; |
| |
| TEST(ElfldltlPhdrTests, ContiguousLoadObserverCompliant) { |
| TestAllFormats(ContiguousLoadObserverCompliantTest); |
| } |
| |
| } // namespace |