fix(relinker): only end the gap mask at a block that holds a syscall

A chain of table bytes that decodes to a return after a padding run looked
like trailing gap code and stopped the mask inside a real table, which hands
the rest of it back to the decoder. Truncating now needs the block to contain
a syscall, or to end where the gap turns into padding, so a 2 KiB table with
00/cc/90 runs and return-like bytes stays masked.
This commit is contained in:
Brainiac91 committed 2026-10-26 02:02:49 +02:00
1 parent 3cb3e8f53b
commit 133ae3261f
2 files changed
+32 -1

No files matched your search

@@ -156,7 +156,7 @@ public:
const bool isSyscall = IsForbiddenSyscallInstruction(code, pos, info);
syscalls[pos] = isSyscall || syscalls[next];
if (EndsControlFlow(info, isSyscall)) {
trailing[pos] = Trailing::Terminator;
trailing[pos] = isSyscall || trailing[next] == Trailing::Padding ? Trailing::Terminator : Trailing::NonTerminator;
continue;
}
if (trailing[next] != Trailing::Padding) {
@@ -545,6 +545,36 @@ void CheckStubBeforeUndecodableData() {
Require(tables[0].End <= TextAddress + 0x13, "Mask covered the syscall stub before undecodable data");
}
void CheckTableWithPaddingAndReturnBytes() {
constexpr std::size_t gap = 0x800;
Bytes text(0x10 + gap, 0x90);
text[0] = 0x48;
text[1] = 0x8d;
text[2] = 0x05;
SetDisplacement(text, 3, 7, TextAddress + 0x10);
text[7] = 0xc3;
for (std::size_t i = 8; i < 0x10; ++i) text[i] = 0xcc;
static constexpr std::uint8_t row[]{
0x18, 0x18, 0x60, 0x18, 0xc0, 0x78, 0x30, 0xd8,
0x18, 0x18, 0x60, 0x18, 0xc0, 0x78, 0x30, 0xd8,
0x0f, 0x78, 0x33, 0x1e
};
for (std::size_t at = 0x10; at + sizeof(row) <= text.size(); at += sizeof(row))
std::memcpy(text.data() + at, row, sizeof(row));
static constexpr std::uint8_t paddedChain[]{0x00, 0xcc, 0x90, 0x48, 0x89, 0xc0, 0xc3};
static constexpr std::uint8_t alignedChain[]{0x00, 0xcc, 0x90, 0x00, 0x48, 0x89, 0xc0, 0xc3};
std::memcpy(text.data() + 0x10 + 0x3b8, paddedChain, sizeof(paddedChain));
std::memcpy(text.data() + 0x10 + 0x4ac, alignedChain, sizeof(alignedChain));
const std::vector<std::pair<VirtualAddress, VirtualAddress>> functions{
{TextAddress, TextAddress + 8},
{TextAddress + text.size(), TextAddress + text.size() + 8}
};
const auto tables = Relinker::FindRelativeJumpTables(text, TextAddress, functions);
Require(tables.size() == 1, "Expected a table with padding and return bytes to be recognized");
Require(tables[0].Begin == TextAddress + 0x10, "Table with padding and return bytes started at the wrong address");
Require(tables[0].End == TextAddress + text.size(), "Mask stopped inside a table with padding and return bytes");
}
void CheckLargeZeroGaps() {
constexpr std::size_t stride = 0x10000;
constexpr std::size_t count = 32;
@@ -607,6 +637,7 @@ int main() {
CheckEmbeddedDataTables();
CheckDecodedPaddingSuffix();
CheckStubBeforeUndecodableData();
CheckTableWithPaddingAndReturnBytes();
CheckLargeZeroGaps();
std::cout << "Control-flow graph tests passed\n";
return 0;