From bef3286e81f1b8a816419ec0d6d9748edaf7d6ca Mon Sep 17 00:00:00 2001 From: Tharun Balaji Date: Thu, 8 Oct 2026 20:15:09 +0530 Subject: [PATCH 1/2] fix(relinker): bound DT_RELA table range --- core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp b/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp index 2d0134bc5..d631eea28 100644 --- a/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp +++ b/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp @@ -165,6 +165,9 @@ RelinkResult RelinkerPipeline::Relink(const std::vector& sourceElf if (hasPltRelocations && (dynJmpRelOffset > raw.size() || dynJmpRelSize > raw.size() - dynJmpRelOffset)) throw RelinkerException("Jump relocation table is out of bounds", dynJmpRelOffset); + if (dynRelaOffset > raw.size() || dynRelaSize > raw.size() - dynRelaOffset) + throw RelinkerException("Relocation table is out of bounds", dynRelaOffset); + auto readCStr = [&](FileByteOffset strOff) -> std::string { if (strOff >= dynStrTabSize) throw RelinkerException("Dynamic string offset is outside DT_STRSZ", strOff); From 204a5e7a42a51ad7e5f1299b8f7c8b479d9440c1 Mon Sep 17 00:00:00 2001 From: Tharun Balaji Date: Thu, 8 Oct 2026 20:15:13 +0530 Subject: [PATCH 2/2] test(relinker): cover relocation table bounds in C++ under both output modes --- core/relinker/CMakeLists.txt | 5 + .../relinker/tests/RelaBoundsTests.cpp | 4 +- .../relinker/tests/RelocTableBoundsTests.cpp | 126 ++++++++++++++++++ 3 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 core/relinker/relinker/tests/RelocTableBoundsTests.cpp diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index d93145995..d2ec417ca 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -313,5 +313,10 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/DynamicTableBoundsTests.cpp ) add_test(NAME dynamic_table_bounds COMMAND dynamic_table_bounds_tests $) + + add_test_executable(reloc_table_bounds_tests + ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/RelocTableBoundsTests.cpp + ) + add_test(NAME reloc_table_bounds COMMAND reloc_table_bounds_tests $) endif() endif() diff --git a/core/relinker/relinker/tests/RelaBoundsTests.cpp b/core/relinker/relinker/tests/RelaBoundsTests.cpp index a445c4f69..9fb373944 100644 --- a/core/relinker/relinker/tests/RelaBoundsTests.cpp +++ b/core/relinker/relinker/tests/RelaBoundsTests.cpp @@ -35,8 +35,8 @@ void RunAndRequireRejected(const std::string& binary, const Bytes& image, const WriteFile(input, image); const auto run = RunRelinker(binary, {"--skip-sce-module", "--to-intel", input.string(), output.string()}, directory.Path() / "relinker.log"); require(run.ExitCode != 0, "Relinker accepted a relocation table " + what + " and exited 0:\n" + run.Output); - require(run.Output.find("Relocation entry out of bounds") != std::string::npos, - "Relinker did not report the entry as out of bounds for a table " + what + ":\n" + run.Output); + require(run.Output.find("Relocation table is out of bounds") != std::string::npos, + "Relinker did not report the table as out of bounds for a table " + what + ":\n" + run.Output); } } diff --git a/core/relinker/relinker/tests/RelocTableBoundsTests.cpp b/core/relinker/relinker/tests/RelocTableBoundsTests.cpp new file mode 100644 index 000000000..fc0abc51f --- /dev/null +++ b/core/relinker/relinker/tests/RelocTableBoundsTests.cpp @@ -0,0 +1,126 @@ +#include "ElfFixture.hpp" +#include "RelinkerProcess.hpp" +#include +#include +#include +#include +#include +#include + +namespace { + +using namespace RelinkerTests; + +void require(const bool condition, const std::string& message) { + if (!condition) throw std::runtime_error(message); +} + +const Bytes kCode = {0xC3}; + +constexpr std::size_t kTagStride = 16; +constexpr std::size_t kTagBase = kCodeOffset + kDynamicVaddr; +constexpr std::size_t kEntryPointOffset = 24; +constexpr std::uint64_t kEntryVaddr = 0x100; +constexpr std::size_t kRelaTag = 4; +constexpr std::size_t kRelaSzTag = 5; +constexpr std::int64_t kDtRela = 7; +constexpr std::int64_t kDtRelaSz = 8; +constexpr std::int64_t kDtOsRela = 0x6100002f; +constexpr std::int64_t kDtOsRelaSz = 0x61000031; +constexpr std::uint64_t kImageSize = 0x8000; +constexpr std::uint64_t kUnmappedVaddr = 0x2000; +constexpr std::uint64_t kOffsetPastEnd = kImageSize + 0x1000; +constexpr std::uint64_t kOffsetBeforeEnd = kImageSize - 16; +constexpr std::size_t kRelaEntry = kCodeOffset + 0x700; +constexpr std::uint64_t kRelativeEntryInfo = 8; + +struct OutputMode { + std::string Flag; + std::string Name; +}; + +const std::vector& OutputModes() { + static const std::vector modes{{"--windows", "windows"}, {"--to-intel", "intel"}}; + return modes; +} + +void WriteTag(Bytes& image, const std::size_t index, const std::int64_t tag, const std::uint64_t value) { + Write(image, kTagBase + index * kTagStride, tag); + Write(image, kTagBase + index * kTagStride + 8, value); +} + +void WriteRelativeEntry(Bytes& image) { + Write(image, kRelaEntry, 0x300); + Write(image, kRelaEntry + 8, kRelativeEntryInfo); + Write(image, kRelaEntry + 16, 0); +} + +Bytes ImageWithRelocationTable(const std::int64_t offsetTag, const std::int64_t sizeTag, + const std::uint64_t offset, const std::uint64_t size) { + Bytes image = MakeExecutable(kCode); + Write(image, kEntryPointOffset, kEntryVaddr); + Write(image, kCodeOffset + kEntryVaddr, 0xC3); + WriteTag(image, kRelaTag, offsetTag, offset); + WriteTag(image, kRelaSzTag, sizeTag, size); + return image; +} + +void RunAndRequireAccepted(const std::string& binary, const Bytes& image, const OutputMode& mode, const std::string& what) { + const TempDirectory directory; + const auto input = directory.Path() / "input.elf"; + const auto output = directory.Path() / ("output." + mode.Name); + WriteFile(input, image); + const auto run = RunRelinker(binary, {"--skip-sce-module", mode.Flag, input.string(), output.string()}, directory.Path() / "relinker.log"); + require(run.ExitCode == 0 && std::filesystem::exists(output), + "Relinker rejected " + what + " for " + mode.Flag + ":\n" + run.Output); +} + +void RunAndRequireRejected(const std::string& binary, const Bytes& image, const OutputMode& mode, const std::string& what, const std::string& expected) { + const TempDirectory directory; + const auto input = directory.Path() / "input.elf"; + const auto output = directory.Path() / ("output." + mode.Name); + WriteFile(input, image); + const auto run = RunRelinker(binary, {"--skip-sce-module", mode.Flag, input.string(), output.string()}, directory.Path() / "relinker.log"); + require(run.ExitCode != 0 && !std::filesystem::exists(output), + "Relinker accepted " + what + " for " + mode.Flag + ":\n" + run.Output); + require(run.Output.find(expected) != std::string::npos, + "Relinker did not report " + expected + " for " + what + ":\n" + run.Output); +} + +} + +int main(const int argc, char** argv) { + try { + require(argc == 2, "usage: reloc_table_bounds_tests "); + const std::string binary = argv[1]; + const std::string tableError = "Relocation table is out of bounds"; + const std::string unmappedError = "Virtual address not mapped by any PT_LOAD segment"; + + for (const auto& mode : OutputModes()) { + RunAndRequireAccepted(binary, ImageWithRelocationTable(kDtRela, kDtRelaSz, 0x700, 0), mode, "an empty relocation table in the file"); + RunAndRequireAccepted(binary, ImageWithRelocationTable(kDtOsRela, kDtOsRelaSz, kRelaEntry, 0), mode, "an empty DT_OS_RELA table in the file"); + + { + Bytes image = ImageWithRelocationTable(kDtRela, kDtRelaSz, 0x700, 24); + WriteRelativeEntry(image); + RunAndRequireAccepted(binary, image, mode, "a relocation table that fits in the file"); + } + + RunAndRequireRejected(binary, ImageWithRelocationTable(kDtOsRela, kDtOsRelaSz, kOffsetPastEnd, 0), mode, + "an empty relocation table whose offset is past the end of the file", tableError); + RunAndRequireRejected(binary, ImageWithRelocationTable(kDtOsRela, kDtOsRelaSz, 0xFFFFFFFFFFFFFFFFull, 0), mode, + "an empty relocation table whose offset is near UINT64_MAX", tableError); + RunAndRequireRejected(binary, ImageWithRelocationTable(kDtOsRela, kDtOsRelaSz, kOffsetBeforeEnd, 0x108), mode, + "a relocation table that does not fit in the file", tableError); + RunAndRequireRejected(binary, ImageWithRelocationTable(kDtRela, kDtRelaSz, 0x700, 0xFFFFFFFFFFFFFFF0ull), mode, + "a relocation table whose size does not fit in the file", tableError); + RunAndRequireRejected(binary, ImageWithRelocationTable(kDtRela, kDtRelaSz, kUnmappedVaddr, 0), mode, + "a relocation table whose address no segment maps", unmappedError); + } + + return 0; + } catch (const std::exception& error) { + std::cerr << "reloc_table_bounds_tests: " << error.what() << '\n'; + return 1; + } +} \ No newline at end of file