Merge pull request #299 from Pouare514/fix/relinker-tls-tail
fix(relinker): tolerate trailing data in TLS scan
This commit is contained in:
commit
d220149542
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue