Merge pull request #299 from Pouare514/fix/relinker-tls-tail

fix(relinker): tolerate trailing data in TLS scan
This commit is contained in:
Dean 2026-10-05 12:44:41 +13:00 committed by GitHub
commit d220149542
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 76 additions and 4 deletions

View File

@ -4,6 +4,8 @@
#include <codegen/x86/X64InstructionDecoder.hpp>
#include <codegen/CodegenException.hpp>
#include <io/BufferUtils.hpp>
#include <algorithm>
#include <iterator>
#include <limits>
#include <map>
#include <string>
@ -173,6 +175,8 @@ std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::ve
if (roots.empty()) throw Domain::RelinkerException("Code analysis: no code entry points");
std::set<std::uint64_t> instructions;
const Codegen::X64InstructionDecoder decoder;
std::vector<std::pair<std::uint64_t, std::uint64_t>> skipped;
std::set<std::uint64_t> staticTargets;
for (const auto& [begin, end] : functions) {
const auto offset = fileOffset(begin, end - begin);
for (auto address = begin; address < end;) {
@ -180,16 +184,36 @@ std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::ve
try {
info = decoder.DecodeInstruction(bytes.data() + offset + address - begin, end - address);
} catch (const Codegen::CodegenException& error) {
throw Domain::RelinkerException(std::string("Code analysis: ") + error.what(), address);
const auto tailBytes = end - address;
bool tailHasFsPrefix = false;
for (auto probe = address; probe < end; ++probe) {
if (bytes[offset + probe - begin] == 0x64) {
tailHasFsPrefix = true;
break;
}
}
if (tailBytes > 15 || tailHasFsPrefix)
throw Domain::RelinkerException(std::string("Code analysis: ") + error.what(), address);
skipped.emplace_back(address, end);
break;
}
if (info.Length == 0 || info.Length > end - address)
throw Domain::RelinkerException("Code analysis: instruction crosses function boundary", address);
instructions.insert(address);
if (info.HasBranchTarget && !info.HasRipRelativeDisp)
addRoot(address + info.Length + static_cast<std::uint64_t>(info.BranchDisp));
if (info.HasBranchTarget && !info.HasRipRelativeDisp) {
const auto target = address + info.Length + static_cast<std::uint64_t>(info.BranchDisp);
staticTargets.insert(target);
addRoot(target);
}
address += info.Length;
}
}
for (const auto target : staticTargets) {
for (const auto& [skipBegin, skipEnd] : skipped) {
if (skipBegin <= target && target < skipEnd)
throw Domain::RelinkerException("Code analysis: branch into skipped range tail", target);
}
}
std::size_t previousRoots = 0;
do {
previousRoots = roots.size();
@ -211,7 +235,47 @@ std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::ve
} while (roots.size() != previousRoots);
std::uint64_t previousEnd = 0;
for (const auto address : instructions) {
if (address < previousEnd) throw Domain::RelinkerException("Code analysis: overlapping instruction boundaries", address);
if (address < previousEnd) {
std::uint64_t spanEnd = previousEnd;
try {
for (const auto& header : headers) {
if (header.Type != 1 || (header.Flags & 1) == 0 || address < header.MappedAddress || address - header.MappedAddress >= header.FileSize) continue;
const auto offset = address - header.MappedAddress;
const auto length = decoder.DecodeInstruction(bytes.data() + header.Offset + offset, header.FileSize - offset).Length;
if (length == 0 || length > header.FileSize - offset) break;
spanEnd = std::max(spanEnd, address + length);
break;
}
} catch (const Codegen::CodegenException&) {
}
bool spanHasFsPrefix = false;
for (const auto& header : headers) {
if (header.Type != 1 || (header.Flags & 1) == 0 || address < header.MappedAddress || address - header.MappedAddress >= header.FileSize) continue;
const auto base = header.Offset + address - header.MappedAddress;
for (auto probe = base; probe < base + (spanEnd - address) && probe < bytes.size(); ++probe) {
if (bytes[probe] == 0x64) {
spanHasFsPrefix = true;
break;
}
}
break;
}
if (!spanHasFsPrefix) {
const auto previous = instructions.lower_bound(address);
if (previous != instructions.begin()) {
const auto previousStart = *std::prev(previous);
for (const auto& header : headers) {
if (header.Type != 1 || (header.Flags & 1) == 0 || previousStart < header.MappedAddress || previousStart - header.MappedAddress >= header.FileSize) continue;
const auto base = header.Offset + previousStart - header.MappedAddress;
if (decoder.DecodeInstruction(bytes.data() + base, bytes.size() - base).SegmentPrefix == 0x64) spanHasFsPrefix = true;
break;
}
}
}
if (spanHasFsPrefix) throw Domain::RelinkerException("Code analysis: overlapping instruction boundaries", address);
previousEnd = spanEnd;
continue;
}
for (const auto& header : headers) {
if (header.Type != 1 || (header.Flags & 1) == 0 || address < header.MappedAddress || address - header.MappedAddress >= header.FileSize) continue;
const auto offset = address - header.MappedAddress;

View File

@ -235,6 +235,14 @@ def main():
overlapping = make_image("register", "unwind")
overlapping[0x1200:0x1205] = b"\xe9" + struct.pack("<i", 0x1245 - 0x1205)
convert("overlapping-entry", overlapping, "Code analysis: overlapping instruction boundaries")
fs_overlap = make_image("register", "unwind")
fs_overlap[0x1209:0x1211] = bytes.fromhex("48 8b 04 25 64 00 00 00")
fs_overlap[0x1211:0x1216] = b"\xe8" + struct.pack("<i", 0x120D - 0x1216)
convert("overlapping-fs-prefix", fs_overlap, "Code analysis: overlapping instruction boundaries")
fs66_overlap = make_image("register", "unwind")
fs66_overlap[0x1209:0x1216] = bytes.fromhex("66 66 66 66 64 48 8b 04 25 78 56 00 00")
fs66_overlap[0x1216:0x121B] = b"\xe8" + struct.pack("<i", 0x1211 - 0x121B)
convert("overlapping-fs-prefix-66", fs66_overlap, "Code analysis: overlapping instruction boundaries")
external = make_image("register", "unwind")
external[0x1300:0x1310] = external[0x1240:0x1250]
external[0x1240:0x1250] = b"\xe8" + struct.pack("<i", 0x1300 - 0x1245) + b"\xc3" + b"\x90" * 10