From a8f862b6df50c5467b9c08b334878e9ea22cac82 Mon Sep 17 00:00:00 2001 From: Hashaam Zafar Date: Wed, 7 Oct 2026 12:05:05 -0700 Subject: [PATCH] fix(relinker): reject overlapping PT_LOAD segments before sizing the Windows image WindowsLoadImage sized its data and page table from the end of the last segment and checked for overlap while copying. A segment whose memory size reached past the last segment's end wrote page flags past the table before its overlap was found; the relinker crashed with SIGSEGV on such an input. Overlap is now checked in a pass before sizing. Co-Authored-By: Claude Opus 5.5 --- .../elfpatcher/src/windows/WindowsLoadImage.cpp | 11 ++++++----- .../relinker/tests/test_executable_segments.py | 10 ++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/core/relinker/elfpatcher/src/windows/WindowsLoadImage.cpp b/core/relinker/elfpatcher/src/windows/WindowsLoadImage.cpp index ea4158313..f82ee3d93 100644 --- a/core/relinker/elfpatcher/src/windows/WindowsLoadImage.cpp +++ b/core/relinker/elfpatcher/src/windows/WindowsLoadImage.cpp @@ -28,15 +28,16 @@ WindowsLoadImage::WindowsLoadImage(const std::vector& source, cons throw Domain::RelinkerException("No PT_LOAD segments found"); std::sort(segments.begin(), segments.end(), [](const auto& left, const auto& right) { return left.MappedAddress < right.MappedAddress; }); firstAddress = segments.front().MappedAddress & ~static_cast(SectionAlignment - 1); - const auto end = segments.back().MappedAddress + segments.back().MemorySize; + std::uint64_t end = firstAddress; + for (const auto& segment : segments) { + if (segment.MappedAddress < end) + throw Domain::RelinkerException("Overlapping PT_LOAD memory ranges", segment.MappedAddress); + end = segment.MappedAddress + segment.MemorySize; + } data.resize(AlignRva(end - firstAddress)); CheckedRva(LoadRva + static_cast(data.size())); pageFlags.resize(data.size() / SectionAlignment); - std::uint64_t previousEnd = firstAddress; for (const auto& segment : segments) { - if (segment.MappedAddress < previousEnd) - throw Domain::RelinkerException("Overlapping PT_LOAD memory ranges", segment.MappedAddress); - previousEnd = segment.MappedAddress + segment.MemorySize; const auto offset = static_cast(segment.MappedAddress - firstAddress); std::copy_n(source.begin() + static_cast(segment.Offset), static_cast(segment.FileSize), data.begin() + static_cast(offset)); std::uint32_t flags = 0; diff --git a/core/relinker/relinker/tests/test_executable_segments.py b/core/relinker/relinker/tests/test_executable_segments.py index ea75606e6..ac3eb83d4 100644 --- a/core/relinker/relinker/tests/test_executable_segments.py +++ b/core/relinker/relinker/tests/test_executable_segments.py @@ -77,6 +77,16 @@ def main(): result, output = run_case(relinker, work, "strict", ["unused-filter=2"], b"\x90\xc3") assert result.returncode == 2 and "multiple executable segments" in result.stderr and not output.exists(), result + + for name, memory_size in (("overlap-past-end", 0x10000000000000), ("overlap-next", 0x1300)): + image = fixture(b"\x90\xc3") + struct.pack_into("