fix(relinker): reject overflowing LSDA landing pad addresses

This commit is contained in:
Tomás Ortellado 2026-10-13 11:05:22 -03:00
parent d0711248b1
commit f0da9edbfe
2 changed files with 45 additions and 1 deletions

View File

@ -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<VirtualAddress>::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);
}

View File

@ -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<std::uint8_t>(data, 0x280, 0);
write<std::uint64_t>(data, 0x281, base);
write<std::uint8_t>(data, 0x289, 0xFF);
write<std::uint8_t>(data, 0x28A, signedOffset ? 12 : 4);
write<std::uint8_t>(data, 0x28B, 25);
write<std::uint64_t>(data, 0x28C, 0);
write<std::uint64_t>(data, 0x294, 1);
write<std::uint64_t>(data, 0x29C, offset);
write<std::uint8_t>(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<std::uint64_t>{0x1000} :
std::vector<std::uint64_t>{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<std::uint64_t>::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<std::uint8_t>(bytes, 0x282, 0xFF);
requireFailure([&] { Relinker::UnusedNidFilter::ReadExceptionFunctions(bytes, headers, {}, {}); }, "Malformed LSDA was accepted");
}