From f0da9edbfeb7dd7bc3674cf15846cde00396b9fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Ortellado?= Date: Tue, 13 Oct 2026 11:05:22 -0300 Subject: [PATCH] fix(relinker): reject overflowing LSDA landing pad addresses --- .../UnusedNidFilter/EhFrameReader.cpp | 7 +++- .../relinker/tests/StrictNidFilterTests.cpp | 39 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/core/relinker/relinker/src/analysis/UnusedNidFilter/EhFrameReader.cpp b/core/relinker/relinker/src/analysis/UnusedNidFilter/EhFrameReader.cpp index 8ce7c297b..fd61157b6 100644 --- a/core/relinker/relinker/src/analysis/UnusedNidFilter/EhFrameReader.cpp +++ b/core/relinker/relinker/src/analysis/UnusedNidFilter/EhFrameReader.cpp @@ -238,7 +238,12 @@ private: encoded(position, callEncoding); const auto landing = encoded(position, callEncoding); leb(position); - if (landing != 0) region.ExtraTargets.push_back(landingBase + landing); + if (landing != 0) { + const bool negative = (callEncoding & 0x08) != 0 && (landing >> 63) != 0; + if (negative ? landingBase < std::uint64_t{0} - landing : landing > std::numeric_limits::max() - landingBase) + throw RelinkerException("Strict filter: LSDA landing pad address overflowed", address); + region.ExtraTargets.push_back(landingBase + landing); + } } if (position != tableEnd) throw RelinkerException("Strict filter: LSDA call-site table size mismatch", address); } diff --git a/core/relinker/relinker/tests/StrictNidFilterTests.cpp b/core/relinker/relinker/tests/StrictNidFilterTests.cpp index bc31840e6..7fea13222 100644 --- a/core/relinker/relinker/tests/StrictNidFilterTests.cpp +++ b/core/relinker/relinker/tests/StrictNidFilterTests.cpp @@ -271,6 +271,45 @@ void exceptionLandingPads() { emit(input, 31, {0xC3}); importThunk(input, 64, 0x2000); require(AnalyzeStrictReachability(input).ImportSlots.contains(0x2000), "Exception-only import was removed"); + const auto checkLanding = [&](std::uint64_t base, std::uint64_t offset, bool signedOffset, bool rejected) { + auto data = bytes; + write(data, 0x280, 0); + write(data, 0x281, base); + write(data, 0x289, 0xFF); + write(data, 0x28A, signedOffset ? 12 : 4); + write(data, 0x28B, 25); + write(data, 0x28C, 0); + write(data, 0x294, 1); + write(data, 0x29C, offset); + write(data, 0x2A4, 0); + try { + const auto functions = Relinker::UnusedNidFilter::ReadExceptionFunctions(data, headers, {}, {}); + require(!rejected, "Overflowing LSDA landing pad address was accepted"); + const auto expected = offset == 0 ? std::vector{0x1000} : + std::vector{0x1000, base + offset}; + require(functions.size() == 1 && functions[0].ExtraTargets == expected, + "Valid LSDA landing pad address changed"); + } catch (const Relinker::RelinkerException& error) { + require(rejected && std::string(error.what()).find("LSDA landing pad address overflowed") != std::string::npos, + "LSDA landing pad rejected for an unexpected reason"); + } + }; + const auto maximum = std::numeric_limits::max(); + for (const std::uint64_t base : {std::uint64_t{0x1000}, maximum - 16}) { + checkLanding(base, 0, false, false); + checkLanding(base, maximum - base, false, false); + checkLanding(base, maximum - base + 1, false, true); + checkLanding(base, maximum, false, true); + } + checkLanding(0x1000, maximum, true, false); + checkLanding(0x1000, std::uint64_t{0} - 0x1000, true, false); + checkLanding(0x1000, std::uint64_t{0} - 0x1001, true, true); + checkLanding(maximum - 16, 16, true, false); + checkLanding(maximum - 16, 17, true, true); + checkLanding(0, 0, true, false); + checkLanding(0, maximum, false, false); + checkLanding(std::uint64_t{1} << 63, std::uint64_t{1} << 63, true, false); + checkLanding((std::uint64_t{1} << 63) - 1, std::uint64_t{1} << 63, true, true); write(bytes, 0x282, 0xFF); requireFailure([&] { Relinker::UnusedNidFilter::ReadExceptionFunctions(bytes, headers, {}, {}); }, "Malformed LSDA was accepted"); }